libffado-2.4.5/0000755000175000001440000000000014206145613012662 5ustar jwoitheuserslibffado-2.4.5/SConstruct0000644000175000001440000011533314206145570014724 0ustar jwoitheusers# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008, 2010 Arnold Krille # Copyright (C) 2007, 2008 Pieter Palmers # Copyright (C) 2008, 2012 Jonathan Woithe # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # from __future__ import print_function FFADO_API_VERSION = "9" FFADO_VERSION="2.4.5" from subprocess import Popen, PIPE, check_output import os import re import sys from string import Template import distutils.sysconfig if not os.path.isdir( "cache" ): os.makedirs( "cache" ) opts = Variables( "cache/options.cache" ) opts.AddVariables( BoolVariable( "DEBUG", """\ Build with \"-g -Wall\" rather than \"-O2\", and include extra debugging checks in the code.""", True ), BoolVariable( "DEBUG_MESSAGES", "Enable support for debug messages", True ), BoolVariable( "PROFILE", "Build with symbols, warnings and other profiling info", False ), PathVariable( "PREFIX", "The prefix where ffado will be installed to.", "/usr/local", PathVariable.PathAccept ), PathVariable( "BINDIR", "Overwrite the directory where apps are installed to.", "$PREFIX/bin", PathVariable.PathAccept ), PathVariable( "LIBDIR", "Overwrite the directory where libs are installed to.", "$PREFIX/lib", PathVariable.PathAccept ), PathVariable( "INCLUDEDIR", "Overwrite the directory where headers are installed to.", "$PREFIX/include", PathVariable.PathAccept ), PathVariable( "SHAREDIR", "Overwrite the directory where misc shared files are installed to.", "$PREFIX/share/libffado", PathVariable.PathAccept ), PathVariable( "LIBDATADIR", "Location for architecture-dependent data.", "$LIBDIR/libffado", PathVariable.PathAccept ), PathVariable( "MANDIR", "Overwrite the directory where manpages are installed", "$PREFIX/man", PathVariable.PathAccept ), PathVariable( "PYPKGDIR", "The directory where the python modules get installed.", distutils.sysconfig.get_python_lib( prefix="$PREFIX" ), PathVariable.PathAccept ), PathVariable( "UDEVDIR", "Overwrite the directory where udev rules are installed to.", "/lib/udev/rules.d/", PathVariable.PathAccept ), BoolVariable( "ENABLE_BEBOB", "Enable/Disable support for the BeBoB platform.", True ), BoolVariable( "ENABLE_FIREWORKS", "Enable/Disable support for the ECHO Audio FireWorks platform.", True ), BoolVariable( "ENABLE_OXFORD", "Enable/Disable support for the Oxford Semiconductor FW platform.", True ), BoolVariable( "ENABLE_MOTU", "Enable/Disable support for the MOTU platform.", True ), BoolVariable( "ENABLE_DICE", "Enable/Disable support for the TCAT DICE platform.", True ), BoolVariable( "ENABLE_METRIC_HALO", "Enable/Disable support for the Metric Halo platform.", False ), BoolVariable( "ENABLE_RME", "Enable/Disable support for the RME platform.", True ), BoolVariable( "ENABLE_DIGIDESIGN", "Enable/Disable support for Digidesign interfaces.", False ), BoolVariable( "ENABLE_BOUNCE", "Enable/Disable the BOUNCE device.", False ), BoolVariable( "ENABLE_GENERICAVC", """\ Enable/Disable the the generic avc part (mainly used by apple). Note that disabling this option might be overwritten by other devices needing this code.""", False ), BoolVariable( "ENABLE_ALL", "Enable/Disable support for all devices.", False ), BoolVariable( "SERIALIZE_USE_EXPAT", "Use libexpat for XML serialization.", False ), EnumVariable( "BUILD_DOC", "Build API documentation", 'none', allowed_values=('all', 'user', 'none'), ignorecase=2), EnumVariable( "BUILD_MIXER", "Build the ffado-mixer", 'auto', allowed_values=('auto', 'true', 'false'), ignorecase=2), BoolVariable( "BUILD_TESTS", """\ Build the tests in their directory. As some contain quite some functionality, this is on by default. If you just want to use ffado with jack without the tools, you can disable this.\ """, True ), BoolVariable( "BUILD_STATIC_TOOLS", "Build a statically linked version of the FFADO tools.", False ), EnumVariable('DIST_TARGET', 'Build target for cross compiling packagers', 'auto', allowed_values=('auto', 'i386', 'i686', 'x86_64', 'powerpc', 'powerpc64', 'none' ), ignorecase=2), BoolVariable( "ENABLE_OPTIMIZATIONS", "Enable optimizations and the use of processor specific extentions (MMX/SSE/...).", False ), BoolVariable( "DETECT_USERSPACE_ENV", "Try to detect the user space environment and add necessary 32/64 bit machine flags.", True ), BoolVariable( "PEDANTIC", "Enable -Werror and more pedantic options during compile.", False ), BoolVariable( "CUSTOM_ENV", "Respect CC, CXX, CFLAGS, CXXFLAGS and LDFLAGS.\nOnly meant for distributors and gentoo-users who want to over-optimize their build.\n Using this is not supported by the ffado-devs!", False ), ( "COMPILE_FLAGS", "Deprecated (use CFLAGS and CXXFLAGS with CUSTOM_ENV=True instead). Add additional flags to the environment.\nOnly meant for distributors and gentoo-users who want to over-optimize their build.\n Using this is not supported by the ffado-devs!" ), EnumVariable( "ENABLE_SETBUFFERSIZE_API_VER", "Report API version at runtime which includes support for dynamic buffer resizing (requires recent jack).", 'auto', allowed_values=('auto', 'true', 'false', 'force'), ignorecase=2), ("PYTHON_INTERPRETER", "Python interpreter to be used by FFADO installation.", "/usr/bin/python"), ) ## Load the builders in config buildenv=os.environ env = Environment( tools=['default','scanreplace','pyuic','pyuic4','pyuic5','dbus','doxygen','pkgconfig'], toolpath=['admin'], ENV = buildenv, options=opts ) custom_flags = False if 'COMPILE_FLAGS' in env and len(env['COMPILE_FLAGS']) > 0: print("The COMPILE_FLAGS option is deprecated. Use CFLAGS and CXXFLAGS with CUSTOM_ENV=True instead") custom_flags = True env.MergeFlags(env['COMPILE_FLAGS']) if env['CUSTOM_ENV']: custom_flags = True # Honour the user choice of compiler (if any). if 'CC' in os.environ and len(os.environ['CC']) > 0: env['CC'] = os.environ['CC'] if 'CXX' in os.environ and len(os.environ['CXX']) > 0: env['CXX'] = os.environ['CXX'] # Honour the user supplied flags (if any), but notify the user that this is not supported. if 'CFLAGS' in os.environ and len(os.environ['CFLAGS']) > 0: env.Append(CFLAGS = str(os.environ['CFLAGS'].replace('\"', ''))) if 'CXXFLAGS' in os.environ and len(os.environ['CXXFLAGS']) > 0: env.Append(CXXFLAGS = str(os.environ['CXXFLAGS'].replace('\"', ''))) if 'LDFLAGS' in os.environ and len(os.environ['LDFLAGS']) > 0: env.Append(LINKFLAGS = str(os.environ['LDFLAGS'].replace('\"', ''))) if custom_flags: print(''' * Usage of additional flags is not supported by the ffado-devs. * Use at own risk! * * Flags in use: * CC = %s * CXX = %s * CFLAGS = %s * CXXFLAGS = %s * LDFLAGS = %s ''' % (env['CC'], env['CXX'], env['CFLAGS'], env['CXXFLAGS'], env['LINKFLAGS'])) Help( """ For building ffado you can set different options as listed below. You have to specify them only once, scons will save the last value you used and re-use that. To really undo your settings and return to the factory defaults, remove the "cache"-folder and the file ".sconsign.dblite" from this directory. For example with: "rm -Rf .sconsign.dblite cache" Note that this is a development version! Don't complain if its not working! See www.ffado.org for stable releases. """ ) Help( opts.GenerateHelpText( env ) ) # make sure the necessary dirs exist if not os.path.isdir( "cache" ): os.makedirs( "cache" ) if not os.path.isdir( 'cache/objects' ): os.makedirs( 'cache/objects' ) CacheDir( 'cache/objects' ) opts.Save( 'cache/options.cache', env ) def ConfigGuess( context ): context.Message( "Trying to find the system triple: " ) ret = check_output(("/bin/sh", "admin/config.guess")).rstrip() context.Result( ret ) return ret.decode() def CheckForApp( context, app ): context.Message( "Checking whether '" + app + "' executes " ) ret = context.TryAction( app ) context.Result( ret[0] ) return ret[0] def CheckForPyModule( context, module ): context.Message( "Checking for the python module '" + module + "' " ) ret = context.TryAction( "$PYTHON_INTERPRETER $SOURCE", "import %s" % module, ".py" ) context.Result( ret[0] ) return ret[0] def CompilerCheck( context ): context.Message( "Checking for a working C-compiler " ) ret = context.TryRun( """ #include int main() { printf( "Hello World!" ); return 0; }""", '.c' )[0] context.Result( ret ) if ret == 0: return False; context.Message( "Checking for a working C++-compiler " ) ret = context.TryRun( """ #include int main() { std::cout << "Hello World!" << std::endl; return 0; }""", ".cpp" )[0] context.Result( ret ) return ret def CheckPKG(context, name): context.Message( 'Checking for %s... ' % name ) ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] context.Result( ret ) return ret tests = { "ConfigGuess" : ConfigGuess, "CheckForApp" : CheckForApp, "CheckForPyModule": CheckForPyModule, "CompilerCheck" : CompilerCheck, "CheckPKG" : CheckPKG, } tests.update( env['PKGCONFIG_TESTS'] ) tests.update( env['PYUIC_TESTS'] ) tests.update( env['PYUIC4_TESTS'] ) conf = Configure( env, custom_tests = tests, conf_dir = "cache/", log_file = 'cache/config.log' ) version_re = re.compile(r'^(\d+)\.(\d+)\.(\d+)') def CheckJackdVer(): print('Checking jackd version...', end='') popen = Popen(("which", 'jackd'), stdout=PIPE, stderr=PIPE) stdout, stderr = popen.communicate() assert popen.returncode in (0, 1), "which returned a unexpected status" if popen.returncode == 1: print("not installed") return None jackd = stdout.decode ().rstrip () ret = check_output ((jackd, '--version')).decode() .rstrip () ret = ret.split ('\n') [-1]; # Last line. ret = ret.split () [2]; # Third field. if not version_re.match (ret): print("failed to parse version") return None print (ret) # Trim off any "rc" (release candidate) components from the end of the # version string ret = ret.split ('rc')[0] ret = ret.split ('.') ret = map (int, ret) return tuple (ret) if env['SERIALIZE_USE_EXPAT']: env['SERIALIZE_USE_EXPAT']=1 else: env['SERIALIZE_USE_EXPAT']=0 if env['ENABLE_BOUNCE'] or env['ENABLE_ALL']: env['REQUIRE_LIBAVC']=1 else: env['REQUIRE_LIBAVC']=0 if not env.GetOption('clean'): # # Check for working gcc and g++ compilers and their environment. # if not conf.CompilerCheck(): print("\nIt seems as if your system isn't even able to compile any C-/C++-programs. Probably you don't have gcc and g++ installed. Compiling a package from source without a working compiler is very hard to do, please install the needed packages.\nHint: on *ubuntu you need both gcc- and g++-packages installed, easiest solution is to install build-essential which depends on gcc and g++.") Exit( 1 ) # Check for pkg-config before using pkg-config to check for other dependencies. if not conf.CheckForPKGConfig(): print("\nThe program 'pkg-config' could not be found.\nEither you have to install the corresponding package first or make sure that PATH points to the right directions.") Exit( 1 ) # # The following checks are for headers and libs and packages we need. # allpresent = 1; # for cache-serialization. if env['SERIALIZE_USE_EXPAT']: allpresent &= conf.CheckHeader( "expat.h" ) allpresent &= conf.CheckLib( 'expat', 'XML_ExpatVersion', '#include ' ) pkgs = { 'libraw1394' : '2.0.5', 'libiec61883' : '1.1.0', 'libconfig++' : '0' } if env['REQUIRE_LIBAVC']: pkgs['libavc1394'] = '0.5.3' if not env['SERIALIZE_USE_EXPAT']: if conf.CheckPKG('libxml++-3.0'): pkgs['libxml++-3.0'] = '3.0.0' if not('libxml++-3.0' in pkgs): pkgs['libxml++-2.6'] = '2.13.0' # Provide a way for users to compile newer libffado which will work # against older jack installations which will not accept the new API # version reported at runtime. have_jack = conf.CheckPKG('jack') if have_jack: good_jack1 = conf.CheckPKG('jack < 1.9.0') and conf.CheckPKG('jack >= 0.121.4') good_jack2 = conf.CheckPKG('jack >= 1.9.9') else: jackd_ver = CheckJackdVer() if jackd_ver: # If jackd is unknown to pkg-config but is never-the-less # runnable, use the version number reported by it. This means # users don't have to have jack development files present on # their system for this to work. have_jack = jackd_ver >= (0, 0, 0) good_jack1 = jackd_ver < (1, 9, 0) and jackd_ver >= (0, 121, 4) good_jack2 = jackd_ver >= (1, 9, 9) if env['ENABLE_SETBUFFERSIZE_API_VER'] == 'auto': if not(have_jack): print(""" No Jack Audio Connection Kit (JACK) installed: assuming a FFADO setbuffersize-compatible version will be used. """) elif not(good_jack1 or good_jack2): FFADO_API_VERSION="8" print(""" Installed Jack Audio Connection Kit (JACK) jack does not support FFADO setbuffersize API: will report earlier API version at runtime. Consider upgrading to jack1 >=0.122.0 or jack2 >=1.9.9 at some point, and then recompile ffado to gain access to this added feature. """) else: print("Installed Jack Audio Connection Kit (JACK) supports FFADO setbuffersize API") elif env['ENABLE_SETBUFFERSIZE_API_VER'] == 'true': if (have_jack and not(good_jack1) and not(good_jack2)): print(""" SetBufferSize API version is enabled but no suitable version of Jack Audio Connection Kit (JACK) has been found. The resulting FFADO would cause your jackd to abort with "incompatible FFADO version". Please upgrade to jack1 >=0.122.0 or jack2 >=1.9.9, or set ENABLE_SETBUFFERSIZE_API_VER to "auto" or "false". """) # Although it's not strictly an error, in almost every case that # this occurs the user will want to know about it and fix the # problem, so we exit so they're guaranteed of seeing the above # message. Exit( 1 ) else: print("Will report SetBufferSize API version at runtime") elif env['ENABLE_SETBUFFERSIZE_API_VER'] == 'force': print("Will report SetBufferSize API version at runtime") else: FFADO_API_VERSION="8" print("Will not report SetBufferSize API version at runtime") for pkg in pkgs: name2 = pkg.replace("+","").replace(".","").replace("-","").upper() env['%s_FLAGS' % name2] = conf.GetPKGFlags( pkg, pkgs[pkg] ) #print('%s_FLAGS' % name2) if env['%s_FLAGS'%name2] == 0: allpresent &= 0 if not allpresent: print(""" (At least) One of the dependencies is missing. I can't go on without it, please install the needed packages for each of the lines saying "no". (Remember to also install the *-devel packages!) And remember to remove the cache with "rm -Rf .sconsign.dblite cache" so the results above get rechecked. """) Exit( 1 ) # libxml++-2.6 requires a c++11 compiler as of version 2.39.1. The # gnu++11 standard seems to work both with these later libxml++ versions # and ffado itself, although a significant number of warnings are # produced. Add the necessary option to CXXFLAGS if required. if conf.CheckPKG('libxml++-2.6 >= 2.39.1'): env.Append(CXXFLAGS = '-std=gnu++11') if conf.CheckPKG('libxml++-3.0 >= 3.0.0'): env.Append(CXXFLAGS = '-std=gnu++11') # Check for C99 lrint() and lrintf() functions used to convert from # float to integer more efficiently via float_cast.h. If not # present the standard slower methods will be used instead. This # might not be the best way of testing for these but it's the only # way which seems to work properly. CheckFunc() fails due to # argument count problems. if 'CFLAGS' in env: oldcf = env['CFLAGS'] else: oldcf = "" env.Append(CFLAGS = '-std=c99') if conf.CheckLibWithHeader( "m", "math.h", "c", "lrint(3.2);" ): HAVE_LRINT = 1 else: HAVE_LRINT = 0 if conf.CheckLibWithHeader( "m", "math.h", "c", "lrintf(3.2);" ): HAVE_LRINTF = 1 else: HAVE_LRINTF = 0 env['HAVE_LRINT'] = HAVE_LRINT; env['HAVE_LRINTF'] = HAVE_LRINTF; env.Replace(CFLAGS=oldcf) # # Optional checks follow: # # PyQT checks if env['BUILD_MIXER'] != 'false': if ( conf.CheckForApp( 'which pyuic4' ) \ and conf.CheckForPyModule( 'PyQt4' ) \ and conf.CheckForPyModule( 'dbus.mainloop.qt' )) \ or ( conf.CheckForApp( 'which pyuic5' ) \ and conf.CheckForPyModule( 'PyQt5' ) \ and conf.CheckForPyModule( 'dbus.mainloop.pyqt5' )): env['BUILD_MIXER'] = 'true' elif not env.GetOption('clean'): if env['BUILD_MIXER'] == 'auto': env['BUILD_MIXER'] = 'false' print(""" The prerequisites ('pyuic4'/'pyuic5' and the python-modules 'dbus' and 'PyQt4'/'PyQt5', the packages could be named like dbus-python and PyQt) to build the mixer were not found. Therefore the qt mixer will not be installed.""") else: # env['BUILD_MIXER'] == 'true' print(""" The prerequisites ('pyuic4'/'pyuic5' and the python-modules 'dbus' and 'PyQt4'/'PyQt5', the packages could be named like dbus-python and PyQt) to build the mixer were not found, but BUILD_MIXER was requested.""") Exit( 1 ) env['XDG_TOOLS'] = False if env['BUILD_MIXER'] == 'true': if conf.CheckForApp( 'xdg-desktop-menu --help' ) and conf.CheckForApp( 'xdg-icon-resource --help' ): env['XDG_TOOLS'] = True else: print(""" I couldn't find the 'xdg-desktop-menu' and 'xdg-icon-resource' programs. These are needed to add the fancy entry for the mixer to your menu, but you can still start it by executing "ffado-mixer".""") # # Optional pkg-config # pkgs = { 'alsa': '0', 'dbus-1': '1.0', 'dbus-c++-1' : '0', } for pkg in pkgs: name2 = pkg.replace("+","").replace(".","").replace("-","").upper() env['%s_FLAGS' % name2] = conf.GetPKGFlags( pkg, pkgs[pkg] ) if not env['DBUS1_FLAGS'] or not env['DBUSC1_FLAGS'] or not conf.CheckForApp('which dbusxx-xml2cpp'): env['DBUS1_FLAGS'] = b"" env['DBUSC1_FLAGS'] = b"" print(""" One of the dbus-headers, the dbus-c++-headers and/or the application 'dbusxx-xml2cpp' where not found. The dbus-server for ffado will therefore not be built. """) else: # Get the directory where dbus stores the service-files env['dbus_service_dir'] = conf.GetPKGVariable( 'dbus-1', 'session_bus_services_dir' ).strip() # this is required to indicate that the DBUS version we use has support # for platform dependent threading init functions # this is true for DBUS >= 0.96 or so. Since we require >= 1.0 it is # always true env['DBUS1_FLAGS'] += b" -DDBUS_HAS_THREADS_INIT_DEFAULT" # The controlserver-glue.h file generated by dbusxx-xml2cpp generates # a large number of instances where call.reader()'s return value is # stored (in ri) but not used. This generates a compiler warning which # we can do nothing about. Therefore when compiling dbus-related # code, suppress the "set but not used" warning. env['DBUS1_FLAGS'] += b" -Wno-unused-but-set-variable" config_guess = conf.ConfigGuess() conf.Finish() if env['DEBUG']: print("Doing a debug build") env.MergeFlags( "-Wall -g -DDEBUG" ) env['DEBUG_MESSAGES'] = True elif not custom_flags: # Only merge -O2 to flags if the user has not specified custom flags. env.MergeFlags( "-O2" ) if env['DEBUG_MESSAGES']: env.MergeFlags( "-DDEBUG_MESSAGES" ) if env['PROFILE']: print("Doing a PROFILE build") env.MergeFlags( "-Wall -g" ) if env['PEDANTIC']: env.MergeFlags( "-Werror" ) if env['ENABLE_ALL']: env['ENABLE_BEBOB'] = True env['ENABLE_FIREWORKS'] = True env['ENABLE_OXFORD'] = True env['ENABLE_MOTU'] = True env['ENABLE_DICE'] = True env['ENABLE_METRIC_HALO'] = True env['ENABLE_RME'] = True env['ENABLE_DIGIDESIGN'] = True env['ENABLE_BOUNCE'] = True env['BUILD_STATIC_LIB'] = False if env['BUILD_STATIC_TOOLS']: print("Building static versions of the tools...") env['BUILD_STATIC_LIB'] = True env['build_base']="#/" # # Get the DESTDIR (if wanted) from the commandline # env.destdir = ARGUMENTS.get( 'DESTDIR', "" ) # # Uppercase variables are for usage in code, lowercase versions for usage in # scons-files for installing. # env['BINDIR'] = Template( env['BINDIR'] ).safe_substitute( env ) env['LIBDIR'] = Template( env['LIBDIR'] ).safe_substitute( env ) env['INCLUDEDIR'] = Template( env['INCLUDEDIR'] ).safe_substitute( env ) env['SHAREDIR'] = Template( env['SHAREDIR'] ).safe_substitute( env ) env['LIBDATADIR'] = Template( env['LIBDATADIR'] ).safe_substitute( env ) env['UDEVDIR'] = Template( env['UDEVDIR'] ).safe_substitute( env ) env['PYTHON_INTERPRETER'] = Template( env['PYTHON_INTERPRETER'] ).safe_substitute( env ) env['prefix'] = Template( env.destdir + env['PREFIX'] ).safe_substitute( env ) env['bindir'] = Template( env.destdir + env['BINDIR'] ).safe_substitute( env ) env['libdir'] = Template( env.destdir + env['LIBDIR'] ).safe_substitute( env ) env['includedir'] = Template( env.destdir + env['INCLUDEDIR'] ).safe_substitute( env ) env['sharedir'] = Template( env.destdir + env['SHAREDIR'] ).safe_substitute( env ) env['libdatadir'] = Template( env.destdir + env['LIBDATADIR'] ).safe_substitute( env ) env['mandir'] = Template( env.destdir + env['MANDIR'] ).safe_substitute( env ) env['pypkgdir'] = Template( env.destdir + env['PYPKGDIR'] ).safe_substitute( env ) env['udevdir'] = Template( env.destdir + env['UDEVDIR'] ).safe_substitute( env ) env['PYPKGDIR'] = Template( env['PYPKGDIR'] ).safe_substitute( env ) env['metainfodir'] = Template( env.destdir + "/usr/share/metainfo" ).safe_substitute( env ) env.Command( target=env['sharedir'], source="", action=Mkdir( env['sharedir'] ) ) env.Alias( "install", env['libdir'] ) env.Alias( "install", env['includedir'] ) env.Alias( "install", env['sharedir'] ) env.Alias( "install", env['libdatadir'] ) env.Alias( "install", env['bindir'] ) env.Alias( "install", env['mandir'] ) if env['BUILD_MIXER'] == 'true': env.Alias( "install", env['pypkgdir'] ) env.Alias( "install", env['metainfodir'] ) # # shamelessly copied from the Ardour scons file # opt_flags = [] env['USE_SSE'] = 0 # guess at the platform, used to define compiler flags config_cpu = 0 config_arch = 1 config_kernel = 2 config_os = 3 config = config_guess.split ("-") needs_fPIC = False #=== Begin Revised CXXFLAGS ========================================= def cpuinfo_kv(): """generator which reads lines from Linux /proc/cpuinfo and splits them into key:value tokens and yields (key, value) tuple. """ with open('/proc/cpuinfo', 'r') as f: for line in f: line = line.strip() if line: k,v = line.split(':', 1) yield (k.strip(), v.strip()) class CpuInfo (object): """Collects information about the CPU, mainly from /proc/cpuinfo """ def __init__(self): self.sysname, self.hostname, self.release, self.version, self.machine = os.uname() # general CPU architecture self.is_x86 = self.machine in ('i686', 'x86_64') or \ re.match("i[3-5]86", self.machine) or False self.is_powerpc = self.machine in ('ppc64', 'ppc', 'powerpc', 'powerpc64', 'ppc64le') #!!! probably not comprehensive self.is_mips = self.machine == 'mips' #!!! not a comprehensive list. uname -m on one android phone reports 'armv71' # I have no other arm devices I can check self.is_arm = self.machine in ('armv71', ) self.cpu_count = 0 if self.is_x86: self.cpu_info_x86() elif self.is_powerpc: self.cpu_info_ppc() elif self.is_mips: self.cpu_info_mips() # 64-bit (x86_64/AMD64/Intel64) # Long Mode (x86-64: amd64, also known as Intel 64, i.e. 64-bit capable) self.is_64bit = (self.is_x86 and 'lm' in self.x86_flags) or \ (self.is_powerpc and \ ('970' in self.ppc_type or 'power8' in self.ppc_type.lower())) # Hardware virtualization capable: vmx (Intel), svm (AMD, Hygon) self.has_hwvirt = self.is_x86 and ( ((self.is_amd or self.is_hygon) and 'svm' in self.x86_flags) or (self.is_intel and 'vmx' in self.x86_flags)) # Physical Address Extensions (support for more than 4GB of RAM) self.has_pae = self.is_x86 and 'pae' in self.x86_flags def cpu_info_x86(self): "parse /proc/cpuinfo for x86 kernels" for k,v in cpuinfo_kv(): if k == 'processor': self.cpu_count += 1 if self.cpu_count > 1: # assume all CPUs are identical features, no need to # parse all of them continue elif k == 'vendor_id': # AuthenticAMD, HygonGenuine, GenuineIntel self.vendor_id = v self.is_amd = v == 'AuthenticAMD' self.is_hygon = v == 'HygonGenuine' self.is_intel = v == 'GenuineIntel' elif k == 'flags': self.x86_flags = v.split() elif k == 'model name': self.model_name = v elif k == 'cpu family': self.cpu_family = v elif k == 'model': self.model = v def cpu_info_ppc(self): "parse /proc/cpuinfo for PowerPC kernels" # http://en.wikipedia.org/wiki/List_of_PowerPC_processors # PowerPC 7xx family # PowerPC 740 and 750, 233-366 MHz # 745/755, 300–466 MHz # PowerPC G4 series # 7400/7410 350 - 550 MHz, uses AltiVec, a SIMD extension of the original PPC specs # 7450 micro-architecture family up to 1.5 GHz and 256 kB on-chip L2 cache and improved Altivec # 7447/7457 micro-architecture family up to 1.8 GHz with 512 kB on-chip L2 cache # 7448 micro-architecture family (1.5 GHz) in 90 nm with 1MB L2 cache and slightly # improved AltiVec (out of order instructions). # 8640/8641/8640D/8641D with one or two e600 (Formerly known as G4) cores, 1MB L2 cache # PowerPC G5 series # 970 (2003), 64-bit, derived from POWER4, enhanced with VMX, 512 kB L2 cache, 1.4 – 2 GHz # 970FX (2004), manufactured at 90 nm, 1.8 - 2.7 GHz # 970GX (2006), manufactured at 90 nm, 1MB L2 cache/core, 1.2 - 2.5 GHz # 970MP (2005), dual core, 1 MB L2 cache/core, 1.6 - 2.5 GHz for k,v in cpuinfo_kv(): if k == 'processor': self.cpu_count += 1 elif k == 'cpu': self.is_altivec_supported = 'altivec' in v if ',' in v: ppc_type, x = v.split(',') else: ppc_type = v self.ppc_type = ppc_type.strip() # older kernels might not have a 'processor' line if self.cpu_count == 0: self.cpu_count += 1 def cpu_info_mips(self): "parse /proc/cpuinfo for MIPS kernels" for k,v in cpuinfo_kv(): if k == 'processor': self.cpu_count += 1 elif k == 'cpu model': self.mips_cpu_model = v def is_userspace_32bit(cpuinfo): """Even if `uname -m` reports a 64-bit architecture, userspace could still be 32-bit, such as Debian on powerpc64. This function tries to figure out if userspace is 32-bit, i.e. we might need to pass '-m32' or '-m64' to gcc. """ if not cpuinfo.is_64bit: return True # note that having a 64-bit CPU means nothing for these purposes. You could # run a completely 32-bit system on a 64-bit capable CPU. answer = None # If setting DIST_TARGET to i686 on a 64-bit CPU to facilitate # compilation of a multilib environment, force 32-bit. if env['DIST_TARGET'] == 'i686': return True # Debian ppc64 returns machine 'ppc64', but userspace might be 32-bit # We'll make an educated guess by examining a known executable exe = '/bin/mount' if os.path.isfile(exe): #print('Found %s' % exe) if os.path.islink(exe): real_exe = os.path.join(os.path.dirname(exe), os.readlink(exe)) #print('%s is a symlink to %s' % (exe, real_exe)) else: real_exe = exe # presumably if a person is running this script, they should have # a gcc toolchain installed... x = check_output(('objdump', '-Wi', real_exe)).decode() # should emit a line that looks like this: # /bin/mount: file format elf32-i386 # or like this: # /bin/mount: file format elf64-x86-64 # or like this: # /bin/mount: file format elf32-powerpc for line in x.split('\n'): line = line.strip() if line.startswith(real_exe): x, fmt = line.rsplit(None, 1) answer = 'elf32' in fmt break else: print('!!! Not found %s' % exe) return answer def cc_flags_x86(cpuinfo, enable_optimizations): """add certain gcc -m flags based on CPU features """ # See http://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/i386-and-x86_002d64-Options.html cc_opts = [] if cpuinfo.machine == 'i586': cc_opts.append('-march=i586') elif cpuinfo.machine == 'i686': cc_opts.append('-march=i686') if 'mmx' in cpuinfo.x86_flags: cc_opts.append('-mmmx') # map from proc/cpuinfo flags to gcc options opt_flags = [ ('sse', ('-mfpmath=sse', '-msse')), ('sse2', '-msse2'), ('ssse3', '-mssse3'), ('sse4', '-msse4'), ('sse4_1', '-msse4.1'), ('sse4_2', '-msse4.2'), ('sse4a', '-msse4a'), ('3dnow', '-m3dnow'), ] if enable_optimizations: for flag, gccopt in opt_flags: if flag in cpuinfo.x86_flags: if isinstance(gccopt, (tuple, list)): cc_opts.extend(gccopt) else: cc_opts.append(gccopt) return cc_opts def cc_flags_powerpc(cpuinfo, enable_optimizations): """add certain gcc -m flags based on CPU model """ cc_opts = [] if cpuinfo.is_altivec_supported: cc_opts.append ('-maltivec') cc_opts.append ('-mabi=altivec') if re.match('74[0145][0578]A?', cpuinfo.ppc_type) is not None: cc_opts.append ('-mcpu=7400') cc_opts.append ('-mtune=7400') elif re.match('750', cpuinfo.ppc_type) is not None: cc_opts.append ('-mcpu=750') cc_opts.append ('-mtune=750') elif re.match('PPC970', cpuinfo.ppc_type) is not None: cc_opts.append ('-mcpu=970') cc_opts.append ('-mtune=970') elif re.match('Cell Broadband Engine', cpuinfo.ppc_type) is not None: cc_opts.append('-mcpu=cell') cc_opts.append('-mtune=cell') return cc_opts #=== End Revised CXXFLAGS ========================================= # Autodetect if env['DIST_TARGET'] == 'auto': if re.search ("x86_64", config[config_cpu]) is not None: env['DIST_TARGET'] = 'x86_64' elif re.search("i[0-5]86", config[config_cpu]) is not None: env['DIST_TARGET'] = 'i386' elif re.search("i686", config[config_cpu]) is not None: env['DIST_TARGET'] = 'i686' elif re.search("powerpc64", config[config_cpu]) is not None: env['DIST_TARGET'] = 'powerpc64' elif re.search("powerpc", config[config_cpu]) is not None: env['DIST_TARGET'] = 'powerpc' else: env['DIST_TARGET'] = config[config_cpu] print("Detected DIST_TARGET = " + env['DIST_TARGET']) #=== Begin Revised CXXFLAGS ========================================= # comment on DIST_TARGET up top implies it can be used for cross-compiling # but that's not true because even if it is not 'auto' the original # script still reads /proc/cpuinfo to determine gcc arch flags. # This script does the same as the original. Needs to be fixed someday. cpuinfo = CpuInfo() if cpuinfo.is_x86: opt_flags.extend(cc_flags_x86(cpuinfo, env['ENABLE_OPTIMIZATIONS'])) if cpuinfo.is_powerpc: opt_flags.extend(cc_flags_powerpc(cpuinfo, env['ENABLE_OPTIMIZATIONS'])) if '-msse' in opt_flags: env['USE_SSE'] = 1 if '-msse2' in opt_flags: env['USE_SSE2'] = 1 if env['DETECT_USERSPACE_ENV']: m32 = is_userspace_32bit(cpuinfo) print('User space is %s' % (m32 and '32-bit' or '64-bit')) if cpuinfo.is_powerpc: if m32: print("Doing a 32-bit PowerPC build for %s CPU" % cpuinfo.ppc_type) machineflags = { 'CXXFLAGS' : ['-m32'] } else: print("Doing a 64-bit PowerPC build for %s CPU" % cpuinfo.ppc_type) machineflags = { 'CXXFLAGS' : ['-m64'] } env.MergeFlags( machineflags ) elif cpuinfo.is_x86: if m32: print("Doing a 32-bit %s build for %s" % (cpuinfo.machine, cpuinfo.model_name)) if cpuinfo.machine == 'x86_64': machineflags = { 'CXXFLAGS' : ['-mx32'] } else: machineflags = { 'CXXFLAGS' : ['-m32'] } else: print("Doing a 64-bit %s build for %s" % (cpuinfo.machine, cpuinfo.model_name)) machineflags = { 'CXXFLAGS' : ['-m64'] } needs_fPIC = True env.MergeFlags( machineflags ) #=== End Revised CXXFLAGS ========================================= if needs_fPIC or ( 'COMPILE_FLAGS' in env and '-fPIC' in env['COMPILE_FLAGS'] ): env.MergeFlags( "-fPIC" ) # end of processor-specific section if env['ENABLE_OPTIMIZATIONS']: opt_flags.extend (["-fomit-frame-pointer","-ffast-math","-funroll-loops"]) env.MergeFlags( opt_flags ) print("Doing an optimized build...") try: env['REVISION'] = check_output(('svnversion', '.',)).decode().rstrip() except: env['REVISION'] = '' # This may be as simple as '89' or as complex as '4123:4184M'. # We'll just use the last bit. env['REVISION'] = env['REVISION'].split(':')[-1] # Assume an unversioned directory indicates a release. if env['REVISION'].startswith ('Unversioned'): env['REVISION'] = '' # try to circumvent localized versions if env['REVISION'].startswith ('export'): env['REVISION'] = '' # avoid the 1.999.41- type of version for exported versions if env['REVISION'] != '': env['REVISIONSTRING'] = '-' + env['REVISION'] else: env['REVISIONSTRING'] = '' env['FFADO_API_VERSION'] = FFADO_API_VERSION env['PACKAGE'] = "libffado" env['VERSION'] = FFADO_VERSION env['LIBVERSION'] = "1.0.0" env['CONFIGDIR'] = "~/.ffado" env['CACHEDIR'] = "~/.ffado" env['USER_CONFIG_FILE'] = env['CONFIGDIR'] + "/configuration" env['SYSTEM_CONFIG_FILE'] = env['SHAREDIR'] + "/configuration" env['REGISTRATION_URL'] = "http://ffado.org/deviceregistration/register.php?action=register" # # To have the top_srcdir as the doxygen-script is used from auto* # env['top_srcdir'] = env.Dir( "." ).abspath # # Start building # env.ScanReplace( "config.h.in" ) env.ScanReplace( "config_debug.h.in" ) env.ScanReplace( "version.h.in" ) # ensure that the config.h is updated env.Depends( "config.h", "SConstruct" ) env.Depends( "config.h", 'cache/options.cache' ) # update version.h whenever the version or SVN revision changes env.Depends( "version.h", env.Value(env['REVISION'])) env.Depends( "version.h", env.Value(env['VERSION'])) env.Depends( "libffado.pc", "SConstruct" ) pkgconfig = env.ScanReplace( "libffado.pc.in" ) env.Install( env['libdir'] + '/pkgconfig', pkgconfig ) env.Install( env['sharedir'], 'configuration' ) subdirs=['src','libffado','support','doc'] if env['BUILD_TESTS']: subdirs.append('tests') env.SConscript( dirs=subdirs, exports="env" ) if 'debian' in COMMAND_LINE_TARGETS: env.SConscript("deb/SConscript", exports="env") # By default only src is built but all is cleaned if not env.GetOption('clean'): Default( 'src' ) Default( 'support' ) if env['BUILD_TESTS']: Default( 'tests' ) if env['BUILD_DOC'] != 'none': Default( 'doc' ) env.Install( env['metainfodir'], "support/xdg/ffado-mixer.appdata.xml" ) # # Deal with the DESTDIR vs. xdg-tools conflict (which is basicely that the # xdg-tools can't deal with DESTDIR, so the packagers have to deal with this # their own :-/ # if len(env.destdir) > 0: if not len( ARGUMENTS.get( "WILL_DEAL_WITH_XDG_MYSELF", "" ) ) > 0: print(""" WARNING! You are using the (packagers) option DESTDIR to install this package to a different place than the real prefix. As the xdg-tools can't cope with that, the .desktop-files are not installed by this build, you have to deal with them your own. (And you have to look into the SConstruct to learn how to disable this message.) """) else: def CleanAction( action ): if env.GetOption( "clean" ): env.Execute( action ) if env['BUILD_MIXER'] == 'true' and env['XDG_TOOLS']: if not env.GetOption("clean"): action = "install" else: action = "uninstall" mixerdesktopaction = env.Action( "-xdg-desktop-menu %s support/xdg/ffado.org-ffadomixer.desktop" % action ) mixericonaction = env.Action( "-xdg-icon-resource %s --size 64 --novendor --context apps support/xdg/hi64-apps-ffado.png ffado" % action ) env.Command( "__xdgstuff1", None, mixerdesktopaction ) env.Command( "__xdgstuff2", None, mixericonaction ) env.Alias( "install", ["__xdgstuff1", "__xdgstuff2" ] ) CleanAction( mixerdesktopaction ) CleanAction( mixericonaction ) # # Create a tags-file for easier emacs/vim-source-browsing # I don't know if the dependency is right... # findcommand = "find . \( -path \"*.h\" -o -path \"*.cpp\" -o -path \"*.c\" \) \! -path \"*.svn*\" \! -path \"./doc*\" \! -path \"./cache*\"" env.Command( "tags", "", findcommand + " |xargs ctags" ) env.Command( "TAGS", "", findcommand + " |xargs etags" ) env.AlwaysBuild( "tags", "TAGS" ) if 'NoCache' in dir(env): env.NoCache( "tags", "TAGS" ) # Another convinience target if env.GetOption( "clean" ): env.Execute( "rm cache/objects -Rf" ) # # vim: ts=4 sw=4 et libffado-2.4.5/README0000644000175000001440000003342114206145246013547 0ustar jwoitheusersFFADO v2.4 ========== The FFADO project aims to provide a free driver implementation for FireWire (IEEE1394, iLink) based audio interfaces. The focus of the project are on audio/music production rather than consumer audio. This means that although we intend to supported all features at some point, consumer features are considered less important. The most obvious example of a consumer feature is AC3/DTS pass-through support, which is unsupported at the moment. This package provides the libffado shared library that provides a unified programming interface to configure and use all supported devices. Currently this library is used by the "firewire" backends of the jack audio connection kit sound server (jackaudio.org). This backend provides audio and midi support, and is available both in jackd and its multiprocessor variant jackdmp. At present there is no support for ALSA or pulseaudio, although jack bridging solutions may help in some situations. Access to the device internal configuration (the internal mixer and device settings) is exposed using the ffado-dbus-server daemon. This daemon exposes the configurable parameters of all detected devices through DBUS. The ffado-mixer application in support/mixer/ presents a GUI to control these parameters. Features -------- * 24-bit audio input/output (number of channels only limited by interface hardware) * supports for all sample rates a device supports * MIDI input/output (unlimited number of channels) * Support for S/PDIF and ADAT/SMUX I/O * Internal mixer and device control support for all officially supported devices (NOTE: no support for internal DSP) * Support for device aggregation (limited to devices on the same bus) Device Support -------------- The "officially supported" label is only given to devices that fulfil the following: * at least one of the developers has the device * the vendor provides development support (access to information) * the device works The devices which are officially supported are: * ESI Quatafire 610 * Terratec Producer Phase 88 * Focusrite Saffire (original/white) * Focusrite Saffire Pro10 * Focusrite Saffire Pro26 * Focusrite Saffire Pro14, Pro40 * ECHO AudioFire2, AudioFire4, AudioFire8, AudioFire12 * Mackie Onyx Mixer FireWire expansion * RME Fireface 400, RME Fireface 800 The FFADO driver is written to provide generic support for all devices it might be able to handle. This means that most devices based on the BridgeCo BeBoB, the ECHO FireWorks platform or the TC Electronics DICE platform will work, at least to a certain extent. For some devices specific functions have been added to augment the generic framework and provide enhanced support, usually in the area of device and mixer control. FFADO includes device-specific functionality for following devices. The code has been developed based on feedback received from users, and it has been reported to work by users. Note that FFADO may not support all device functions. * Presonus Firebox and Inspire1394 * Presonus FireStudio Tube, FireStudio Project * M-Audio Ozonic, FireWire Solo * M-Audio Profire 2626, 610 * M-Audio Audiophile and 410 (latest firmware and startup workaround needed, see http://sourceforge.net/p/ffado/mailman/message/30807938) * M-Audio 1814 and ProjectMix (mixer only, audio streaming not supported. and FireWire 1814 needs startup workaround above) * Focusrite Saffire Pro24 * Focusrite Saffire Pro24 DSP (audio streaming only, DSP control not available) * Yamaha GO44 and GO46 Devices that have been reported to (partially) work with the generic support: * Presonus FirePod / FP10 * Alesis io14 * TC Konnekt 8, Konnekt 24D, Konnekt Live As a result of a significant reverse-engineering effort a selection of devices from MOTU are supported. The developers had no support from the device vendor and this of course limits the extent to which problems can be solved. You have been warned. Please do not buy devices for which support is based upon reverse engineering, nor from vendors who are hostile towards Linux like MOTU. Value the support that some vendors provide and buy their stuff. Check ffado.org for details. It can't be said enough: currently it is extremely unwise to buy a MOTU device if you intend to use Linux. MOTU devices reported to work with FFADO are: * MOTU Traveler * MOTU 828mkII, MOTU Ultralite, MOTU 896HD, MOTU 8pre, MOTU 4pre * Audio only: MOTU Ultralite mk3, MOTU Traveler mk3, MOTU 896mk3, MOTU 828mk3 * Audio only, FireWire interface only: MOTU Ultralite mk3 hybrid "Audio only" means that FFADO can be used to stream audio to and from the device, control sample rate and clock source. Control of the mixer and DSP functions is not presently supported. It is planned but no ETA is available at this stage. Devices for which work is in progress. These are not yet usable: * RME UFX and UCX FireWire devices Usupported devices: * Presonus FireStation * Other TC Konnekt devices * Other Alesis devices * Metric Halo devices We constantly try to persuade vendors to help us extend our device support. Dependencies ------------ FFADO uses the scons build system, which must be available on the system when building FFADO. It is not a runtime dependency. Scons 2 is currently used to build FFADO. Work continues on making FFADO's scons scripts compatible with both scons 2 and 3. Testing and bug reports when using scons 3 are welcomed. To build libffado you need several libraries. For all libraries a version is provided which is a "known good" version. The first few libraries it seems it is not necessary that the version must match. The chances that it works also with an older versions are good: libxml++2 (>= 2.6.13) These libraries here should be at least the version listed: libraw1394 (>= 2.0.7), https://ieee1394.wiki.kernel.org/ libiec61883 (>= 1.1.0), https://ieee1394.wiki.kernel.org/ dbus-1 (>= 1.0), http://dbus.freedesktop.org dbus-c++ (>= 0), http://sourceforge.net/apps/mediawiki/dbus-cplusplus/ libconfig (>= 0), http://www.hyperrealm.com/libconfig/ Currently only the jackd audio server is supported: jackd (>= 0.109.12), http://jackaudio.org While jack1 0.109.12 will work, jack1 >= 0.122.0 or jack2 >= 1.9.9 are recommended if support for jack's setbufsize functionality is desired. Optionally, but recommended is that you install qjackctl: qjackctl (>= 0.2.20.10), http://sourceforge.net/projects/qjackctl To build the optional ffado device mixer control utility you also require: Qt >= 4.0, http://qt-project.org/ SIP >= 4.7.0, http://www.riverbankcomputing.co.uk/software/sip/intro PyQt (note below), http://www.riverbankcomputing.co.uk/software/pyqt/intro dbus-python >= 0.82.0, http://dbus.freedesktop.org/releases/dbus-python/ The version of PyQt must be chosen to exactly match the version of Qt in use. For Qt 4.x use PyQt 4.x. SIP is only required to compile PyQt. If using a binary package of PyQt SIP should not be needed. Packages for building on Debian/Ubuntu distributions are installed with: $ sudo apt-get install build-essential subversion scons libxml++2.6-dev \ libiec61883-dev libdbus-1-dev libdbus-c++-bin \ libdbus-c++-dev libconfig++-dev pyqt5-dev-tools \ python3-dbus.mainloop.pyqt5 pyqt5-sip How to build ------------ If you want to build the release version you can simply do following: $ scons $ scons install [as root or via sudo] If you want some debug information (because something seems not to work correctly) you can try to do: $ scons DEBUG=yes $ scons install [as root or via sudo] Cleaning a build is done with: $ scons -c -Q More extended instructions can be found here: http://subversion.ffado.org/wiki/InstallingFfadoFromSource NOTE: In order to build jackd with ffado support, you have to install libffado before you build jackd. The backend to use in jackd is "firewire". NOTE: the beta versions are distributed with debugging enabled by default. DISTRIBUTION PACKAGERS NOTE: Please do not enable support for devices if it is not on by default. If device support for a specific device is not turned on by default by the developers, it means that it is not ready yet. Most of the time it is placeholder code for future devices. Running jackd ------------- The easiest way to run this is using qjackctl. There are only minor differences with the other backends, however you should change some of the default values: - It is recommended to change the "periods/buffer" field to 3, especially if you use low period sizes (=< 128) - It is recommended to raise the RT priority to 70. In order to get it running from the command line, you need to provide some arguments to jackd. Run $ jackd -d firewire --help to see the backend options. You can easily figure out how to set them using the remarks given above (for qjackctl). For the other aspects of jackd usage, consult the jackd documentation. Here is a sample session (without realtime support enabled): $ jackd -d firewire no message buffer overruns jackd 0.111.0 Copyright 2001-2005 Paul Davis and others. jackd comes with ABSOLUTELY NO WARRANTY This is free software, and you are welcome to redistribute it under certain conditions; see the file COPYING for details JACK compiled with System V SHM support. loading driver .. 3106528665: (ffado.cpp)[ 99] ffado_streaming_init: libffado 1.999.20 built Apr 26 2008 20:26:32 libiec61883 warning: Established connection on channel 0. You may need to manually set the channel on the receiving node. libiec61883 warning: Established connection on channel 1. You may need to manually set the channel on the transmitting node. (Note: you can safely ignore the libiec61883 warnings, they are normal.) An important remark is that for good performance, one should always run jack with the -R flag to enable realtime scheduling for critical threads: $ jackd -R -d firewire In most cases this is now the default. For best results across most hardware it is necessary to use a kernel configured with the "Low latency desktop" option (CONFIG_PREEMPT) enabled. Most distributions provide this as an option, often called "low latency". In general it is no longer necessary to use an RT-patched kernel. Ffado-mixer look and feel ------------------------- The look and feel of ffado-mixer can be changed via QT themes. When a dark mode is required, install a suitable QT theme. Some users have found the dark theme from UKUI to work well with ffado-mixer (often available in distributions through the qt5-ukui-platformtheme package). In case of problems ------------------- First of all, check whether your problem is a known issue, and whether it is a FFADO problem. Use your chosen web search engine for this. Many distributor kernels now include the alternative ALSA audio streaming drivers for selected FireWire audio interfaces (snd-bebob, snd-dice, etc). These are developed independently of FFADO. If these kernel modules are loaded then FFADO's streaming engine cannot be used: using jackd's "firewire" driver will fail because the kernel drivers have ownership of the interface. To continue to use FFADO's streaming system, the applicable snd-* module must be unloaded from the kernel and prevented from loading on boot. Use "rmmod" to remove the module from the running system, and blacklist the relevant snd-* module in a file under /lib/modprobe.d/ (or your distribution's equivalent). When seeking support from the developers keep in mind that none of the FFADO developers are paid to work on FFADO or to support FFADO users. Answering the same question multiple times reduces the amount of time they have to work on the code. Before contacting the developers please see if your query or problem has been seen before. The following places are helpful: * http://www.ffado.org/ * http://subversion.ffado.org/ * your chosen search engine (the terms "ffado-devel" and "ffado-user" work well) If you have tried to find a solution to your problem but couldn't or are confused, don't hesitate to ask for help. The preferred way is by signing up to the mailing list as described on http://www.ffado.org/?q=contact. Writing a bug report -------------------- Note that the more effort you put in your bug report, the more effort we will put into helping you. Make sure you have compiled a DEBUG=yes version of libffado. If not there is no way we can trace the problem. When reporting a problem, please run jackd with the --verbose option, and add the -v6 option to the firewire backend: $ jackd --verbose [...] -d firewire -v6 [...] ( [...] = other options ) This will generate an increadible amount of debug output that should contain what we need to track down the problem. If you have troubles saving the output, try redirecting it to a file: $ jackd --verbose -d firewire -v6 2> ffado-jack.log this will create a ffado.log file containing the output. Use CTRL-C to exit jack if necessary. The distribution contains a tool to gather some information about your system. When FFADO is installed on the system it can be run directly: $ ffado-diag > ffado-diag.log It is also possible to run it from the source tree: $ cd support/tools $ python ffado-diag.py > ffado-diag.log It will check your system for basic problems and gather some information regarding your hardware configuration. This will allow us to diagnose your problem faster. Once the logs have been created you can create a support ticket at http://subversion.ffado.org/newticket Be sure to include the following information: * the log file(s) (zipped/tar.gz'ed and attached) * the device you're trying to use * a description of what went wrong and how to reproduce it. You preferably try to figure out a sequence of steps that can reliably reproduce the issue on your system. A one-time failure is very difficult to diagnose and/or fix. * the distribution and its version libffado-2.4.5/admin/0000755000175000001440000000000014206145612013751 5ustar jwoitheuserslibffado-2.4.5/admin/dbus.py0000644000175000001440000000274014206145246015266 0ustar jwoitheusers#!/usr/bin/python # # Copyright (C) 2007-2008 Arnold Krille # Copyright (C) 2007-2008 Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # # xml translator # def dbusxx_xml2cpp_adaptor_action( target, source, env ): env.Execute( "dbusxx-xml2cpp %s --adaptor=%s" % ( source[0], target[0] ) ) return 0 def dbusxx_xml2cpp_proxy_action( target, source, env ): env.Execute( "dbusxx-xml2cpp %s --proxy=%s" % ( source[0], target[0] ) ) return 0 def generate( env, **kw ): env['BUILDERS']['Xml2Cpp_Adaptor'] = env.Builder(action = dbusxx_xml2cpp_adaptor_action, suffix = '.h', src_suffix = '.xml') env['BUILDERS']['Xml2Cpp_Proxy'] = env.Builder(action = dbusxx_xml2cpp_proxy_action, suffix = '.h', src_suffix = '.xml', single_source=True ) def exists( env ): return 1 libffado-2.4.5/admin/doxygen.py0000644000175000001440000001543114206145246016007 0ustar jwoitheusers#!/usr/bin/python # # Copyright (C) 2007-2008 Arnold Krille # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # # Astxx, the Asterisk C++ API and Utility Library. # Copyright (C) 2005, 2006 Matthew A. Nicholson # Copyright (C) 2006 Tim Blechmann # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License version 2.1 as published by the Free Software Foundation. # # 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 import os import os.path import glob from fnmatch import fnmatch from functools import reduce def DoxyfileParse(file_contents): """ Parse a Doxygen source file and return a dictionary of all the values. Values will be strings and lists of strings. """ data = {} import shlex lex = shlex.shlex(instream = file_contents.decode(), posix = True) lex.wordchars += "*+./-:" lex.whitespace = lex.whitespace.replace("\n", "") lex.escape = "" lineno = lex.lineno token = lex.get_token() key = token # the first token should be a key last_token = "" key_token = False next_key = False new_data = True def append_data(data, key, new_data, token): if new_data or len(data[key]) == 0: data[key].append(token) else: data[key][-1] += token while token: if token in ['\n']: if last_token not in ['\\']: key_token = True elif token in ['\\']: pass elif key_token: key = token key_token = False else: if token == "+=": if not key in data: data[key] = list() elif token == "=": data[key] = list() else: append_data( data, key, new_data, token ) new_data = True last_token = token token = lex.get_token() if last_token == '\\' and token != '\n': new_data = False append_data( data, key, new_data, '\\' ) # compress lists of len 1 into single strings to_pop = [] for (k, v) in data.items(): if len(v) == 0: # data.pop(k) # Shouldn't modify dictionary while looping to_pop.append(k) # items in the following list will be kept as lists and not converted to strings if k in ["INPUT", "FILE_PATTERNS", "EXCLUDE_PATTERNS"]: continue if len(v) == 1: data[k] = v[0] for k in to_pop: data.pop(k) return data def DoxySourceScan(node, env, path): """ Doxygen Doxyfile source scanner. This should scan the Doxygen file and add any files used to generate docs to the list of source files. """ default_file_patterns = [ '*.c', '*.cc', '*.cxx', '*.cpp', '*.c++', '*.java', '*.ii', '*.ixx', '*.ipp', '*.i++', '*.inl', '*.h', '*.hh ', '*.hxx', '*.hpp', '*.h++', '*.idl', '*.odl', '*.cs', '*.php', '*.php3', '*.inc', '*.m', '*.mm', '*.py', ] default_exclude_patterns = [ '*~', ] sources = [] data = DoxyfileParse(node.get_contents()) if data.get("RECURSIVE", "NO") == "YES": recursive = True else: recursive = False file_patterns = data.get("FILE_PATTERNS", default_file_patterns) exclude_patterns = data.get("EXCLUDE_PATTERNS", default_exclude_patterns) for node in data.get("INPUT", []): if os.path.isfile(node): sources.append(node) elif os.path.isdir(node): if recursive: for root, dirs, files in os.walk(node): for f in files: filename = os.path.join(root, f) pattern_check = reduce(lambda x, y: x or bool(fnmatch(filename, y)), file_patterns, False) exclude_check = reduce(lambda x, y: x and fnmatch(filename, y), exclude_patterns, True) if pattern_check and not exclude_check: sources.append(filename) else: for pattern in file_patterns: sources.extend(glob.glob("/".join([node, pattern]))) sources = map( lambda path: env.File(path), sources ) return sources def DoxySourceScanCheck(node, env): """Check if we should scan this file""" return os.path.isfile(node.path) def DoxyEmitter(source, target, env): """Doxygen Doxyfile emitter""" # possible output formats and their default values and output locations output_formats = { "HTML": ("YES", "html"), "LATEX": ("YES", "latex"), "RTF": ("NO", "rtf"), "MAN": ("YES", "man"), "XML": ("NO", "xml"), } data = DoxyfileParse(source[0].get_contents()) targets = [] out_dir = data.get("OUTPUT_DIRECTORY", ".") # add our output locations for (k, v) in output_formats.items(): if data.get("GENERATE_" + k, v[0]) == "YES": targets.append(env.Dir( os.path.join(out_dir, data.get(k + "_OUTPUT", v[1]))) ) # don't clobber targets for node in targets: env.Precious(node) # set up cleaning stuff for node in targets: env.Clean(node, node) return (targets, source) def generate(env): """ Add builders and construction variables for the Doxygen tool. This is currently for Doxygen 1.4.6. """ doxyfile_scanner = env.Scanner( DoxySourceScan, "DoxySourceScan", scan_check = DoxySourceScanCheck, ) import SCons.Builder doxyfile_builder = SCons.Builder.Builder( action = "cd ${SOURCE.dir} && ${DOXYGEN} ${SOURCE.file}", emitter = DoxyEmitter, target_factory = env.fs.Entry, single_source = True, source_scanner = doxyfile_scanner, ) env.Append(BUILDERS = { 'Doxygen': doxyfile_builder, }) env.AppendUnique( DOXYGEN = 'doxygen', ) def exists(env): """ Make sure doxygen exists. """ return env.Detect("doxygen") libffado-2.4.5/admin/pkgconfig.py0000644000175000001440000000666114206145246016306 0ustar jwoitheusers#!/usr/bin/python # # Copyright (C) 2007-2008 Arnold Krille # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # # Taken from http://www.scons.org/wiki/UsingPkgConfig # and heavily modified # import subprocess # # Checks for pkg-config # def CheckForPKGConfig( context, version='0.0.0' ): context.Message( "Checking for pkg-config (at least version %s)... " % version ) ret = context.TryAction( "pkg-config --atleast-pkgconfig-version=%s" %version )[0] context.Result( ret ) return ret # # Checks for the given package with an optional version-requirement # # The flags (which can be imported into the environment by env.MergeFlags(...) # are exported as env['NAME_FLAGS'] where name is built by removing all +,-,. # and upper-casing. # def CheckForPKG( context, name, version="" ): name2 = name.replace("+","").replace(".","").replace("-","") if version == "": context.Message( "Checking for %s... \t" % name ) ret = context.TryAction( "pkg-config --exists '%s'" % name )[0] else: context.Message( "Checking for %s (%s or higher)... \t" % (name,version) ) ret = context.TryAction( "pkg-config --atleast-version=%s '%s'" % (version,name) )[0] if ret: context.env['%s_FLAGS' % name2.upper()] = context.env.ParseFlags("!pkg-config --cflags --libs %s" % name) context.Result( ret ) return ret # # Checks for the existance of the package and returns the packages flags. # # This should allow caching of the flags so that pkg-config is called only once. # def GetPKGFlags( context, name, version="" ): import os if version == "": context.Message( "Checking for %s... \t" % name ) ret = context.TryAction( "pkg-config --exists '%s'" % name )[0] else: context.Message( "Checking for %s (%s or higher)... \t" % (name,version) ) ret = context.TryAction( "pkg-config --atleast-version=%s '%s'" % (version,name) )[0] if not ret: context.Result( ret ) return ret out = subprocess.Popen(['pkg-config', '--cflags', '--libs', name], stdout=subprocess.PIPE) ret = out.stdout.read() context.Result( True ) return ret # # Checks for the existance of the package and returns the value of the specified variable. # def GetPKGVariable( context, name, variable ): import os context.Message( "Checking for variable %s in package %s... \t" % (variable,name) ) ret = context.TryAction( "pkg-config --exists '%s'" % name )[0] if not ret: context.Result( ret ) return ret out = subprocess.Popen(['pkg-config', '--variable=%s' % variable, name], stdout=subprocess.PIPE) ret = out.stdout.read() context.Result( True ) return ret def generate( env, **kw ): env['PKGCONFIG_TESTS' ] = { 'CheckForPKGConfig' : CheckForPKGConfig, 'CheckForPKG' : CheckForPKG, 'GetPKGFlags' : GetPKGFlags, 'GetPKGVariable' : GetPKGVariable } def exists( env ): return 1 libffado-2.4.5/admin/pyuic.py0000644000175000001440000000276214206145246015466 0ustar jwoitheusers#!/usr/bin/python # # Copyright (C) 2007-2008 Arnold Krille # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import imp def pyuic_action( target, source, env ): env.Execute( "pyuic " + str( source[0] ) + " > " + str( target[0] ) ) return 0 def pyuic_string( target, source, env ): return "building '%s' from '%s'" % ( str(target[0]), str( source[0] ) ) def PyQtCheck( context ): context.Message( "Checking for pyuic (by checking for the python module pyqtconfig) " ) ret = True try: imp.find_module( "pyqtconfig" ) except ImportError: ret = False context.Result( ret ) return ret def generate( env, **kw ): env['BUILDERS']['PyUIC'] = env.Builder( action=pyuic_action, src_suffix=".ui", single_source=True ) env['PYUIC_TESTS'] = { "PyQtCheck" : PyQtCheck } def exists( env ): return 1 libffado-2.4.5/admin/pyuic4.py0000644000175000001440000000277414206145246015555 0ustar jwoitheusers#!/usr/bin/python # # Copyright (C) 2007-2008 Arnold Krille # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import imp def pyuic4_action( target, source, env ): env.Execute( "pyuic4 " + str( source[0] ) + " > " + str( target[0] ) ) return 0 def pyuic4_string( target, source, env ): return "building '%s' from '%s'" % ( str(target[0]), str( source[0] ) ) def PyQt4Check( context ): context.Message( "Checking for pyuic4 (by checking for the python module pyqtconfig) " ) ret = True try: imp.find_module( "pyqtconfig" ) except ImportError: ret = False context.Result( ret ) return ret def generate( env, **kw ): env['BUILDERS']['PyUIC4'] = env.Builder( action=pyuic4_action, src_suffix=".ui", single_source=True ) env['PYUIC4_TESTS'] = { "PyQt4Check" : PyQt4Check } def exists( env ): return 1 libffado-2.4.5/admin/pyuic5.py0000644000175000001440000000304114206145246015542 0ustar jwoitheusers#!/usr/bin/python # # Copyright (C) 2007-2008 Arnold Krille # Copyright (C) 2017 Jonathan Woithe # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import imp def pyuic5_action( target, source, env ): env.Execute( "pyuic5 " + str( source[0] ) + " > " + str( target[0] ) ) return 0 def pyuic5_string( target, source, env ): return "building '%s' from '%s'" % ( str(target[0]), str( source[0] ) ) def PyQt5Check( context ): context.Message( "Checking for pyuic5 (by checking for the python module pyqtconfig) " ) ret = True try: imp.find_module( "pyqtconfig" ) except ImportError: ret = False context.Result( ret ) return ret def generate( env, **kw ): env['BUILDERS']['PyUIC5'] = env.Builder( action=pyuic5_action, src_suffix=".ui", single_source=True ) env['PYUIC5_TESTS'] = { "PyQt5Check" : PyQt5Check } def exists( env ): return 1 libffado-2.4.5/admin/scanreplace.py0000644000175000001440000000270014206145246016605 0ustar jwoitheusers#!/usr/bin/python # # Copyright (C) 2007-2008 Arnold Krille # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # # Taken from http://www.scons.org/wiki/ReplacementBuilder # from string import Template import os def replace_action(target, source, env): open( str(target[0]), 'w' ).write( Template( open( str(source[0]), 'r' ).read() ).safe_substitute( env ) ) os.chmod( str(target[0]), os.stat( str(source[0]) ).st_mode ) return 0 def replace_string(target, source, env): return "building '%s' from '%s'" % ( str(target[0]), str(source[0]) ) def generate(env, **kw): action = env.Action( replace_action, replace_string ) env['BUILDERS']['ScanReplace'] = env.Builder( action=action, src_suffix='.in', single_source=True ) def exists(env): return 1 libffado-2.4.5/admin/config.guess0000644000175000001440000013036112235024211016261 0ustar jwoitheusers#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2013 Free Software Foundation, Inc. timestamp='2013-06-10' # This file 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 . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # # Please send patches with a ChangeLog entry to config-patches@gnu.org. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; or1k:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; or32:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: libffado-2.4.5/configuration0000644000175000001440000005565314206145246015474 0ustar jwoitheusersdevice_definitions = ( { vendorid = 0x00000f; modelid = 0x00010065; vendorname = "Mackie"; modelname = "Onyx FireWire"; driver = "BEBOB"; xmit_max_cycles_early_transmit = 4; }, { # Added by arnonym from ffado-mixers list vendorid = 0x00000f; modelid = 0x00010067; vendorname = "Mackie"; modelname = "Onyx FireWire"; driver = "BEBOB"; mixer = "MackieOnyx"; xmit_max_cycles_early_transmit = 4; }, { # Added by yellius vendorid = 0x0022E; modelid = 0x10067; vendorname = "Tascam"; modelname = "IFFWDM"; driver = "BEBOB"; }, { # IDs provided by Travis Kepley vendorid = 0x000ff2; modelid = 0x000006; vendorname = "Loud Technologies Inc."; modelname = "Onyx 1640i (DICE)"; driver = "DICE"; }, { # IDs provided by Melanie Bernkopf. Mixer entry from Scott Martin. vendorid = 0x000ff2; modelid = 0x000007; vendorname = "Loud Technologies Inc."; modelname = "Onyx Blackbird"; driver = "DICE"; mixer = "Generic_Dice_EAP"; }, { # IDs provided by Steven Tonge vendorid = 0x000ff2; modelid = 0x001640; vendorname = "Loud Technologies Inc."; modelname = "Onyx 1640i (Oxford)"; driver = "OXFORD"; # This transfer delay was tested at 48 kHz, as per # http://sourceforge.net/p/ffado/mailman/message/26964819/ xmit_transfer_delay = 12800; }, { # entries provided by Holger Dehnhardt vendorid = 0x000ff2; modelid = 0x081216; vendorname = "Loud Technologies Inc."; modelname = "Onyx-i"; driver = "OXFORD"; xmit_transfer_delay = 11776; }, { # IDs from Geoff Beasley. vendorid = 0x001564; modelid = 0x00000006; vendorname = "Behringer"; modelname = "X32"; driver = "BEBOB"; xmit_max_cycles_early_transmit = 4; }, { # IDs from Tony Rocco. vendorid = 0x001564; modelid = 0x00001604; vendorname = "Behringer"; modelname = "UFX-1604 mixer"; driver = "BEBOB"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x0003db; modelid = 0x00010048; vendorname = "Apogee Electronics"; modelname = "Rosetta 200"; driver = "BEBOB"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x0007f5; modelid = 0x00010048; vendorname = "BridgeCo"; modelname = "RD Audio1"; driver = "BEBOB"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x0007f5; modelid = 0x00010049; vendorname = "BridgeCo"; modelname = "Audio 5"; driver = "BEBOB"; mixer = "BCoAudio5Control"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x000a92; modelid = 0x00010000; vendorname = "PreSonus"; modelname = "FIREBOX"; driver = "BEBOB"; mixer = "Presonus_Firebox"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x000a92; modelid = 0x00010001; vendorname = "PreSonus"; modelname = "Inspire1394"; driver = "BEBOB"; mixer = "Presonus_Inspire1394"; }, { vendorid = 0x000a92; modelid = 0x00010066; vendorname = "PreSonus"; modelname = "FirePOD"; driver = "BEBOB"; mixer = "Presonus_FP10"; xmit_max_cycles_early_transmit = 4; }, { # Presonus Firestudio 26x26, Bob Hamil via jaimes on the forums vendorid = 0x000a92; modelid = 0x00000008; vendorname = "Presonus"; modelname = "Firestudio 26x26"; mixer = "Generic_Dice_EAP"; driver = "DICE"; }, { # Presonus Firestudio Project, from Walt Baldwin vendorid = 0x000a92; modelid = 0x0000000b; vendorname = "Presonus"; modelname = "Firestudio Project"; mixer = "Generic_Dice_EAP"; driver = "DICE"; }, { # Presonus Firestudio Tube, from Tobi Kraus vendorid = 0x000a92; modelid = 0x0000000c; vendorname = "Presonus"; modelname = "Firestudio Tube"; mixer = "Generic_Dice_EAP"; driver = "DICE"; }, { # Entry for Firestudio mobile provided by "Pule" via the forums. vendorid = 0x000a92; modelid = 0x00000011; vendorname = "PreSonus"; modelname = "Firestudio Mobile"; mixer = "Generic_Dice_EAP"; driver = "DICE"; }, { # Entry for StudioLive 2442, from Walt Baldwin vendorid = 0x00000A92; modelid = 0x00000012; vendorname = "PreSonus"; modelname = "STUDIOLIVE_2442"; driver = "DICE"; }, { # Entry for StudioLive 1602, from Ulrich-Lorenz Schluter vendorid = 0x00000A92; modelid = 0x00000013; vendorname = "PreSonus"; modelname = "STUDIOLIVE_1602"; driver = "DICE"; }, { # Entry for Studiolive 32.4.2, from Karl Swisher vendorid = 0x00000a92; modelid = 0x00000014; vendorname = "PreSonus"; modelname = "STUDIOLIVE_3242_mk2"; driver = "DICE"; }, { vendorid = 0x000aac; modelid = 0x00000003; vendorname = "TerraTec Electronic GmbH"; modelname = "Phase 88 FW"; driver = "BEBOB"; mixer = "Phase88Control"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x000aac; modelid = 0x00000004; vendorname = "TerraTec Electronic GmbH"; modelname = "Phase X24 FW (model version 4)"; driver = "BEBOB"; mixer = "Phase24Control"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x000aac; modelid = 0x00000007; vendorname = "TerraTec Electronic GmbH"; modelname = "Phase X24 FW (model version 7)"; driver = "BEBOB"; mixer = "Phase24Control"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x000f1b; modelid = 0x00010064; vendorname = "ESI"; modelname = "Quatafire 610"; driver = "BEBOB"; mixer = "QuataFire"; xmit_max_cycles_early_transmit = 4; }, # Shalok Shalom has a Quantafire 610 which reports a different modelid. # The reasons for this are unknown. { vendorid = 0x000f1b; modelid = 0x00000210; vendorname = "ESI"; modelname = "Quatafire 610 variant"; driver = "BEBOB"; mixer = "QuataFire"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x00130e; modelid = 0x00000003; vendorname = "Focusrite"; modelname = "Saffire Pro26IO"; driver = "BEBOB"; mixer = "SaffirePro"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x00130e; modelid = 0x00000006; vendorname = "Focusrite"; modelname = "Saffire Pro10IO"; driver = "BEBOB"; mixer = "SaffirePro"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x00130e; modelid = 0x00000000; vendorname = "Focusrite"; modelname = "Saffire (LE)"; driver = "BEBOB"; mixer = "Saffire"; cmd_interval_time = 10000; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x0040ab; modelid = 0x00010049; vendorname = "EDIROL"; modelname = "FA-66"; driver = "BEBOB"; mixer = "EdirolFa66Control"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x0040ab; modelid = 0x00010048; vendorname = "EDIROL"; modelname = "FA-101"; driver = "BEBOB"; mixer = "EdirolFa101Control"; xmit_max_cycles_early_transmit = 4; }, { # Added by Mark Brand (orania) vendorid = 0x000d6c; modelid = 0x0000000a; vendorname = "M-Audio"; modelname = "Ozonic"; driver = "BEBOB"; mixer = "MAudio_BeBoB"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x000d6c; modelid = 0x00010062; vendorname = "M-Audio"; modelname = "FW Solo"; driver = "BEBOB"; mixer = "MAudio_BeBoB"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x000d6c; modelid = 0x00010081; vendorname = "M-Audio"; modelname = "NRV10"; driver = "BEBOB"; # no mixer xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x000d6c; modelid = 0x00010060; vendorname = "M-Audio"; modelname = "FW Audiophile"; driver = "BEBOB"; mixer = "MAudio_BeBoB"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x000d6c; modelid = 0x00010046; vendorname = "M-Audio"; modelname = "FW 410"; driver = "BEBOB"; mixer = "MAudio_BeBoB"; xmit_max_cycles_early_transmit = 4; }, { // experimental for mixer functionality vendorid = 0x000d6c; modelid = 0x00010071; vendorname = "M-Audio"; modelname = "FW 1814"; driver = "BEBOB"; mixer = "MAudio_BeBoB"; xmit_max_cycles_early_transmit = 4; }, { // experimental for mixer functionality vendorid = 0x000d6c; modelid = 0x00010091; vendorname = "M-Audio"; modelname = "ProjectMix I/O"; driver = "BEBOB"; mixer = "MAudio_BeBoB"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x000d6c; modelid = 0x000100A1; vendorname = "M-Audio"; modelname = "ProFire Lightbridge"; driver = "BEBOB"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x000d6c; modelid = 0x00000010; vendorname = "M-Audio"; modelname = "ProFire 2626"; driver = "DICE"; mixer = "Profire2626"; }, { vendorid = 0x000d6c; modelid = 0x00000011; vendorname = "M-Audio"; modelname = "ProFire 610"; driver = "DICE"; mixer = "Profire2626"; }, { vendorid = 0x000aac; modelid = 0x00000002; vendorname = "Acoustic Reality"; modelname = "eAR Master One"; driver = "BEBOB"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x0000000A; modelid = 0x00030000; vendorname = "CME"; modelname = "Matrix K FW"; driver = "BEBOB"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x1486; modelid = 0xAF2; vendorname = "Echo"; modelname = "AudioFire2"; driver = "FIREWORKS"; mixer = "AudioFire"; xmit_max_cycles_early_transmit = 2; }, { vendorid = 0x1486; modelid = 0xAF4; vendorname = "Echo"; modelname = "AudioFire4"; driver = "FIREWORKS"; mixer = "AudioFire"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x1486; modelid = 0xAF8; vendorname = "Echo"; modelname = "AudioFire8"; driver = "FIREWORKS"; mixer = "AudioFire"; xmit_max_cycles_early_transmit = 2; }, { vendorid = 0x1486; modelid = 0xAF9; vendorname = "Echo"; modelname = "AudioFire8a"; driver = "FIREWORKS"; mixer = "AudioFire"; xmit_max_cycles_early_transmit = 2; }, { vendorid = 0x1486; modelid = 0xAF12; vendorname = "Echo"; modelname = "AudioFire12"; driver = "FIREWORKS"; mixer = "AudioFire"; xmit_max_cycles_early_transmit = 2; }, { vendorid = 0x1486; modelid = 0xAF12D; vendorname = "Echo"; modelname = "AudioFire12HD"; driver = "FIREWORKS"; xmit_max_cycles_early_transmit = 2; }, { vendorid = 0x1486; modelid = 0xF8; vendorname = "Echo"; modelname = "Fireworks 8"; driver = "FIREWORKS"; xmit_max_cycles_early_transmit = 2; }, { vendorid = 0x1486; modelid = 0xAFD1; vendorname = "Echo"; modelname = "FW HDMI"; driver = "FIREWORKS"; xmit_max_cycles_early_transmit = 2; }, { vendorid = 0xFF2; modelid = 0x400F; vendorname = "Mackie"; modelname = "Onyx 400F"; driver = "FIREWORKS"; xmit_max_cycles_early_transmit = 2; }, { vendorid = 0xFF2; modelid = 0x1200F; vendorname = "Mackie"; modelname = "Onyx 1200F"; driver = "FIREWORKS"; xmit_max_cycles_early_transmit = 2; }, { vendorid = 0x1564; modelid = 0xFC22; vendorname = "Behringer"; modelname = "FCA202"; driver = "OXFORD"; }, { vendorid = 0x00001260; modelid = 0x00001000; vendorname = "Stanton DJ"; modelname = "SCS.1m"; driver = "GENERICAVC"; xmit_max_cycles_early_transmit = 4; xmit_sp_dll_bw = 1.0; recv_sp_dll_bw = 1.0; }, { # added by arnonym from ffado-mixers list vendorid = 0x0001f2; modelid = 0x00000000; vendorname = "Motu"; modelname = "All pre-mark3 devices"; driver = "MOTU"; mixer = "Motu"; }, { vendorid = 0x0001f2; modelid = 0x00000001; vendorname = "Motu"; modelname = "All mark3 devices"; driver = "MOTU"; mixer = "Motu_Mark3"; }, { vendorid = 0x000a35; # Note: RME detection code compares the modelid field against the # device's unit version since RME seem to use the configrom modelid # for other things not necessarily related to device differentiation. modelid = 0x0001; vendorname = "RME"; modelname = "FireFace800"; driver = "RME"; mixer = "Rme"; }, { vendorid = 0x000a35; # Note: RME detection code compares the modelid field against the # device's unit version since RME seem to use the configrom modelid # for other things not necessarily related to device differentiation. modelid = 0x0002; vendorname = "RME"; modelname = "FireFace400"; driver = "RME"; mixer = "Rme"; }, { vendorid = 0x000a35; # Note: RME detection code compares the modelid field against the # device's unit version since RME seem to use the configrom modelid # for other things not necessarily related to device differentiation. modelid = 0x0003; vendorname = "RME"; modelname = "FireFace UFX"; driver = "RME"; mixer = "Rme"; }, { vendorid = 0x000a35; # Note: RME detection code compares the modelid field against the # device's unit version since RME seem to use the configrom modelid # for other things not necessarily related to device differentiation. # Unit version (0x04) provided by Florian Hanisch. modelid = 0x0004; vendorname = "RME"; modelname = "FireFace UCX"; driver = "RME"; mixer = "Rme"; }, { vendorid = 0x000a35; # Note: RME detection code compares the modelid field against the # device's unit version since RME seem to use the configrom modelid # for other things not necessarily related to device differentiation. # Unit version (0x05) provided by Florian Hofmann. modelid = 0x0005; vendorname = "RME"; modelname = "FireFace 802"; driver = "RME"; mixer = "Rme"; }, { vendorid = 0x000166; modelid = 0x0001; vendorname = "TCAT"; modelname = "DiceII EVM (1)"; driver = "DICE"; }, { vendorid = 0x000166; modelid = 0x0002; vendorname = "TCAT"; modelname = "DiceII EVM (2)"; driver = "DICE"; }, { vendorid = 0x000166; modelid = 0x0004; vendorname = "TCAT"; modelname = "DiceII EVM (4)"; driver = "DICE"; }, { vendorid = 0x000166; modelid = 0x00000020; vendorname = "TC Electronic"; modelname = "Konnekt 24D"; driver = "DICE"; }, { vendorid = 0x000166; modelid = 0x00000021; vendorname = "TC Electronic"; modelname = "Konnekt 8"; driver = "DICE"; }, { # Details provided by "Juanramon" in a comment post on the website vendorid = 0x000166; modelid = 0x00000022; vendorname = "TC Electronic"; modelname = "Studio Konnekt 48"; driver = "DICE"; }, { vendorid = 0x000166; modelid = 0x00000023; vendorname = "TC Electronic"; modelname = "Konnekt Live"; driver = "DICE"; }, { vendorid = 0x000166; modelid = 0x00000024; vendorname = "TC Electronic"; modelname = "Desktop Konnekt 6"; driver = "DICE"; }, { vendorid = 0x000166; modelid = 0x00000027; vendorname = "TC Electronic"; modelname = "ImpactTwin"; driver = "DICE"; }, { vendorid = 0x000595; modelid = 0x00000001; vendorname = "Alesis"; modelname = "io|14"; driver = "DICE"; }, { # The MultiMix-16 and MultiMix-12 share the same vendor/model IDs. # Thanks to Fourer Dominique for the information about the MultiMix-12. vendorid = 0x000595; modelid = 0x00000000; vendorname = "Alesis"; modelname = "MultiMix-12 / MultiMix-16 FireWire"; driver = "DICE"; }, { # Studiolive 16.4.2, provided by Johan Landman vendorid = 0x000a92; modelid = 0x00000010; vendorname = "PreSonus"; modelname = "STUDIOLIVE_1642"; driver = "DICE"; xmit_transfer_delay = 4; }, { # Studiolive 16.0.2, provided by Kim Tore Jensen vendorid = 0x000a92; modelid = 0x00000013; vendorname = "PreSonus"; modelname = "STUDIOLIVE_1602"; driver = "DICE"; }, { vendorid = 0x00130e; modelid = 0x00000005; vendorname = "Focusrite"; modelname = "Saffire PRO 40"; driver = "DICE"; mixer = "Saffire_Dice"; }, { vendorid = 0x00130e; modelid = 0x00000007; vendorname = "Focusrite"; modelname = "Saffire PRO 24"; driver = "DICE"; mixer = "Saffire_Dice"; }, { vendorid = 0x00130e; modelid = 0x00000008; vendorname = "Focusrite"; modelname = "Saffire PRO 24 DSP"; driver = "DICE"; mixer = "Saffire_Dice"; }, { vendorid = 0x00130e; modelid = 0x00000009; vendorname = "Focusrite"; modelname = "Saffire PRO 14"; driver = "DICE"; mixer = "Saffire_Dice"; }, { vendorid = 0x00130e; modelid = 0x00000012; vendorname = "Focusrite"; modelname = "Saffire PRO 26"; driver = "DICE"; mixer = "Saffire_Dice"; }, { # A new version of the Pro 40 has been released, described in the # ConfigROM as SAFFIRE_PRO_40_1. Thanks to Mathieu Picot for the info. vendorid = 0x00130e; modelid = 0x000000DE; vendorname = "Focusrite"; modelname = "Saffire PRO 40-1"; driver = "DICE"; mixer = "Saffire_Dice"; }, { # Casimir Westerholm has a "SAFFIRE_PRO_40_1" interface which somewhat # unexpectedly uses a different model ID to that which has been seen by # others. Thanks to Casimir for the information. vendorid = 0x00130e; modelid = 0x00000013; vendorname = "Focusrite"; modelname = "Saffire PRO 40-1"; driver = "DICE"; mixer = "Saffire_Dice"; }, { vendorid = 0x001C6A; modelid = 0x00000001; vendorname = "Weiss Engineering Ltd."; modelname = "ADC 2"; driver = "DICE"; }, { vendorid = 0x001C6A; modelid = 0x00000002; vendorname = "Weiss Engineering Ltd."; modelname = "Vesta"; driver = "DICE"; }, { vendorid = 0x001C6A; modelid = 0x00000003; vendorname = "Weiss Engineering Ltd."; modelname = "Minerva"; driver = "DICE"; }, { vendorid = 0x001C6A; modelid = 0x00000004; vendorname = "Weiss Engineering Ltd."; modelname = "AFI 1"; driver = "DICE"; }, { vendorid = 0x001C6A; modelid = 0x00000005; vendorname = "Weiss Engineering Ltd."; modelname = "TAG DAC1"; driver = "DICE"; }, { vendorid = 0x001C6A; modelid = 0x00000006; vendorname = "Weiss Engineering Ltd."; modelname = "INT 202"; driver = "DICE"; }, { vendorid = 0x001C6A; modelid = 0x00000007; vendorname = "Weiss Engineering Ltd."; modelname = "DAC 202"; driver = "DICE"; }, { # Added by david@wwns.com vendorid = 0x001c2d; modelid = 0x00000001; vendorname = "FlexRadio_Systems"; modelname = "Flex-5000"; driver = "DICE"; xmit_max_cycles_early_transmit = 4; }, { # Phonic HelixBoard 24 Universal (PHHB24U), provided by Steffen Klein vendorid = 0x001496; modelid = 0x000000; vendorname = "Phonic"; modelname = "HB 24U"; driver = "BEBOB"; xmit_max_cycles_early_transmit = 4; }, { vendorid = 0x0000A0DE; modelid = 0x0010000B; vendorname = "Yamaha"; modelname = "GO44"; driver = "BEBOB"; mixer = "YamahaGo"; }, { # Yamaha GO46, provided by Luis Pablo Gasparotto vendorid = 0x0000A0DE; modelid = 0x0010000C; vendorname = "Yamaha"; modelname = "GO46"; driver = "BEBOB"; mixer = "YamahaGo"; xmit_max_cycles_early_transmit = 4; }, { # DnR - Axum_FireWire_IO_card_16x16 vendorid = 0x00000F64; modelid = 0x00000003; vendorname = "DnR"; modelname = "Axum_FireWire_IO_card_16x16"; driver = "DICE"; }, { # Lexicon Onix-FW810S, provided by gerradblock vendorid = 0x00000FD7; modelid = 0x00000001; vendorname = "Lexicon"; modelname = "I-ONIX_FW810S"; driver = "DICE"; mixer = "Generic_Dice_EAP"; }, { # Avid Mbox 2 Pro, information provided by Yves Grenier via the ffado-user # mailing list. # Note: as of Oct 2014 FFADO requires that the device be initialised # under another operating system so routing and clock settings are # correct. When this is done and the device is transferred to Linux # without power cycling it, FFADO can stream audio to/from it. The # initialisation details need to be sorted before FFADO can claim to # properly support this device. vendorid = 0x0000A07E; modelid = 0x000000A9; vendorname = "Digidesign"; modelname = "Mbox 2 Pro"; driver = "BEBOB"; # A device-specific mixer needs to be written, there being no generic # bebob mixer modules. }, { # Avid Mbox Pro, information provided by Niels Dettenbach. # Note: this entry is for future reference only. FFADO does NOT have a # driver for this device: as of March 2013 no Avid/Digidesign interfaces # are supported or usable with FFADO. vendorid = 0x0000A07E; modelid = 0x00000004; vendorname = "Avid"; modelname = "Mbox 3 Pro"; }, { # Allen and Heath Zed R16. Information from Brendan Pike. vendorid = 0x000004C4; modelid = 0x00000000; vendorname = "Allen and Heath"; modelname = "Zed R16"; driver = "DICE"; mixer = "Generic_Dice_EAP"; }, { # Midas Venice F32. Information from Jano Svitok. vendorid = 0x0010C73F; modelid = 0x00000001; vendorname = "Midas"; modelname = "Venice F32"; driver = "DICE"; mixer = "Generic_Dice_EAP"; }, { # ICON FireXon. Information from Philippe Ploquin. vendorid = 0x00001A9E; modelid = 0x00000001; vendorname = "ICON"; modelname = "FireXon"; driver = "BEBOB"; # A device-specific mixer needs to be written, there being no generic # bebob mixer modules. } ); libffado-2.4.5/deb/0000755000175000001440000000000014206145612013413 5ustar jwoitheuserslibffado-2.4.5/deb/SConscript0000644000175000001440000001162014206145246015430 0ustar jwoitheusers# # Copyright (C) 2007-2008 Arnold Krille # Copyright (C) 2007-2008 Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from: http://www.qandr.org/quentin/writings/debscons.html import os Import('env') # exported by parent SConstruct # Here's the core info for the package DEBNAME = "libffado0" if env['REVISION']: DEBVERSION = "%s-%s" % (env['VERSION'], env['REVISION']) else: DEBVERSION = env['VERSION'] DEBMAINT = "Pieter Palmers [pieter.palmers@ffado.org]" DEBARCH = "i386" DEBDEPENDS = "libraw1394-8 (>= 1.3.0), libiec61883-0 (>= 1.1.0), libavc1394-0 (>= 0.5.3), dbus (>= 1.1.0)" # what are we dependent on? DEBRECOMMENDS = "qt5-ukui-platformtheme (>= 1.0.0)" DEBDESC = "FFADO: FireWire audio for Linux (Development build SVN r%s)" % env['REVISION'] DEBFILES = [ # Now we specify the files to be included in the .deb # Where they should go, and where they should be copied from. # If you have a lot of files, you may wish to generate this # list in some other way. ("usr/lib/libffado.so", "#src/libffado.so"), ("usr/lib/pkgconfig/libffado.pc", "#/libffado.pc"), ("usr/include/libffado/ffado.h", "#libffado/ffado.h"), ("usr/bin/ffado-dbus-server", "#support/dbus/ffado-dbus-server"), ] DEBFILES.append(("usr/share/libffado/ffado_driver_genericavc.txt", "#src/genericavc/ffado_driver_genericavc.txt")) if env['ENABLE_BEBOB']: DEBFILES.append(("usr/share/libffado/ffado_driver_bebob.txt", "#src/bebob/ffado_driver_bebob.txt")) DEBFILES.append(("usr/bin/ffado-bridgeco-downloader", "#support/firmware/ffado-bridgeco-downloader")) #DEBFILES.append(("usr/share/libffado/fw410.xml", "#src/bebob/maudio/fw410.xml")) #DEBFILES.append(("usr/share/libffado//fwap.xml","#src/bebob/maudio/fwap.xml")) #DEBFILES.append(("usr/share/libffado//refdesign.xml","#src/bebob/maudio/refdesign.xml")) if env['ENABLE_FIREWORKS']: DEBFILES.append(("usr/share/libffado/ffado_driver_fireworks.txt", "#src/fireworks/ffado_driver_fireworks.txt")) DEBFILES.append(("usr/bin/ffado-fireworks-downloader", "#support/firmware/ffado-fireworks-downloader")) if env['ENABLE_MOTU']: pass if env['ENABLE_DICE']: pass if env['ENABLE_METRIC_HALO']: pass if env['ENABLE_RME']: pass if env['ENABLE_BOUNCE']: pass # This is the debian package we're going to create debpkg = '#%s_%s_%s.deb' % (DEBNAME, DEBVERSION, DEBARCH) # and we want it to be built when we build 'debian' env.Alias("debian", debpkg) DEBCONTROLFILE = os.path.join(DEBNAME, "DEBIAN/control") # This copies the necessary files into place into place. # Fortunately, SCons creates the necessary directories for us. for f in DEBFILES: # We put things in a directory named after the package dest = os.path.join(DEBNAME, f[0]) # The .deb package will depend on this file env.Depends(debpkg, dest) # Copy from the the source tree. env.Command(dest, f[1], Copy('$TARGET','$SOURCE')) # The control file also depends on each source because we'd like # to know the total installed size of the package env.Depends(DEBCONTROLFILE, dest) # Now to create the control file: CONTROL_TEMPLATE = """ Package: %s Priority: extra Section: misc Installed-Size: %s Maintainer: %s Architecture: %s Version: %s Depends: %s Recommends: %s Description: %s """ env.Depends(debpkg,DEBCONTROLFILE ) # The control file should be updated when the SVN version changes env.Depends(DEBCONTROLFILE, env.Value(env['REVISION'])) # This function creates the control file from the template and info # specified above, and works out the final size of the package. def make_control(target=None, source=None, env=None): installed_size = 0 for i in DEBFILES: installed_size += os.stat(str(env.File(i[1])))[6] control_info = CONTROL_TEMPLATE % ( DEBNAME, installed_size, DEBMAINT, DEBARCH, DEBVERSION, DEBDEPENDS, DEBRECOMMENDS, DEBDESC) with open(str(target[0]), 'w') as f: f.write(control_info) # We can generate the control file by calling make_control env.Command(DEBCONTROLFILE, None, make_control) # And we can generate the .deb file by calling dpkg-deb env.Command(debpkg, DEBCONTROLFILE, "dpkg-deb -b %s %s" % ("deb/%s" % DEBNAME, "$TARGET")) libffado-2.4.5/doc/0000755000175000001440000000000014206145612013426 5ustar jwoitheuserslibffado-2.4.5/doc/adding_devices.dox0000644000175000001440000004373014206145246017104 0ustar jwoitheusers/** @page adding_devices Adding support for new devices to libffado @author Pieter Palmers @section intro Introduction Device support is implemented on two levels: 1) Discovery & configuration 2) Streaming layer Layer 1 is implemented by subclassing the IAvDevice interface, and adding an appropriate probe function to devicemanager.cpp. Layer 2 is implemented by subclassing the StreamProcessor class in src/libstreaming/ Basic operation of libffado is: - Create a DeviceManager that iterates over all nodes connected to the 1394 bus. For every node present, it tries the probeFunctions (probeBeBoB, probeMotu, ...). If a probefunction succeeds, the node is considered to be a freeob supported device. The probefunction should return a pointer to a AvDevice, i.e. a subclass of this interface. At this point we have access to all supported devices. - When the streaming layer is started (e.g. by jackd), it will iterate over all IAvDevice's of the DeviceManager. For every device: The streaming layer will (most likely) set some config values (at this moment only the samplerate). Then will then request the number of iso streams the device provides using getStreamCount(). Most likely this will be 2 streams, one transmit and one receive. The next step is that for every stream a StreamProcessor is requested using getStreamProcessorByIndex(i) (i starts at 0). This streamprocessor is responsible for the translation between iso stream and audio API. A streamprocessor is a class that is a subclass of StreamProcessor (see the documentation of that class for more info). - Once the streaming layer fetched all streamprocessors of all devices, it will proceed with initializing them, and setting up all support stuff (threads, iso handlers, etc...) - After this initial setup, the streaming layer will ask the IAvDevice to start the streams on the hardware device, using startStreamByIndex(). When the streaming layer shuts down, it will use stopStreamByIndex() to stop the device's streams, and free up the (possibly allocated) bus resources. \note the jackd backend also supports to specify a specific node. In that case only the AvDevice for that node is used, instead of iterating over all of them. \note Starting the hardware streams is part of the IAvDevice because this allows for a more generic streamprocessor for AMDTP streams. This stream format is used by BeBoB's, DICE-II devices, mLan devices, etc. Keeping the start/stop system separate from the streamprocessor allows the re-use of the streamprocessor should the start/stop mechanism differ. In order to add support for a device to libffado, two things should be implemented: - an IAvDevice descendant that takes care of the device discovery & configuration - a StreamProcessor descendant that takes care of the device specific stream translation @section discoverybaseclasses Streaming base class hierarchy and operation @ref iavdevice.h "" is the device interface. It should be more or less self-explanatory @section streamingbaseclasses Streaming base class hierarchy and operation This section explains the implementation details of the streaming part of libffado. The following figure shows the base class diagram, and the derrived classed that implement the AMDTP (AM824, IEC61883-6) streaming decoders. It can come in handy when trying to understand the following sections. @image html class_diagram_1.png "Streaming Class Diagram" @image latex class_diagram_1.eps "Streaming Class Diagram" The basic idea is that when the streaming layer is initialized, it creates a DeviceManager (not shown in the figure) and a StreamProcessorManager. The DeviceManager is responsible for discovering and configuring the devices, as explained in the introduction. The StreamProcessorManager will take care of the actual 'streaming'. This incorporates: - handling the Isochronous traffic (allocating handlers, iterating handles, ...) - translating the iso streams into the data format requested by the client application - handling all threading issues (creation/destruction, realtime behaviour, synchronisation, ...) - ... It joins the iso side with the Audio API side. To accomplish this, it consists of two parts: - a collection of StreamProcessor 's - an IsoHandlerManager instance Related classes: Streaming::StreamProcessorManager @subsection isoside The iso side: 1394 isochronous traffic management The IsoHandlerManager is responsible for the management of IsoHandlers. This means creating/destroying the handlers when needed, starting & stopping them, etc... An IsoHandler in its turn will serve an IsoStream. This means that the getPacket or putPacket callback of an IsoStream will be called by the IsoHandler whenever this is nescessary. \note The IsoHandler and IsoStream are separate classes because in the case of multichannel Isochronous receive, one IsoHandler can serve more than one IsoStream. The distinction lies in the fact that IsoStreams are bound to a channel and an ieee1394 port, while IsoHandlers are only bound to an ieee1394 port. [multichannel receive is however not implemented yet] The handling of an IsoStream by an IsoHandler can be started by registering the IsoStream with the IsoHandlerManager. The manager figures out if it has to allocate a new handler for this stream, and will do so if needed. It will also keep track of the IsoHandler-IsoStream relations and will clean up any unused IsoHandlers. To summarize: if we want to handle (receive from/transmit on) a channel of an ieee1394 port, the only thing we have to do is create an IsoStream, setup its parameters and register it with the IsoHandlerManager. If we then start() the IsoHandlerManager and call its Execute() function, the IsoStream callback will be called whenever activity happens. \note This abstraction is completely device independent, it only provides a mechanism to transmit or receive a certain isochronous stream. It could as well be used for video streams... Related classes: Streaming::IsoStream, Streaming::IsoHandlerManager, Streaming::IsoHandler @subsection audioapiside The Audio API side: port management \note not all stuff described here is implemented yet. The abstraction presented at the audio side is based upon Ports. A 'Port' is an entity that has the following properties: - It has an associated buffer to store 'events'. These events can be samples, midi bytes, control data, ... . The buffer can be allocated outside of the Port (external buffer) or can be allocated internally. Currently there are two buffer types available: E_PointerBuffer and E_RingBuffer. - E_PointerBuffer is a buffer that can be accessed using the getBufferAddress() method, i.e. by directly reading/writing to memory. It is assumed that routines using this method keep it's size in mind (can be obtained by multiplying getEventSize() and getBufferSize()). This buffer can be an external buffer or an internal buffer. It is important that both the reader and the writer use the correct data type. \note for this type, currently only externally attached buffers are supported. - E_RingBuffer is a ringbuffer that can be accessed using read/write type of calls. \note for the ringbuffer type, only internal buffers are supported. - DataType: The datatype defines the data type of the events in a Port's buffer. This also determines the size of the events and hence (together with the setBufferSize()) the buffer size in bytes. - PortType: The port type determines the type of events that is flowing through this port. Currently there are three port types: E_Audio, E_Midi, E_Control. In the future there might be more (e.g. E_AC3 or E_Adat). - SignalType: The signalling type defines how the 'new event' signalling should occur for this port. Currently there are two possibilities: - E_PacketSignalled: The port contents should be updated every time a packet arrives. This means that the read to/write from operation of the port is to be called for every packet that arrives (i.e. from within the packet handler callback). The most obvious use is for midi ports, as it can be a problem when midi bytes are quantized to period boundaries. - E_PeriodSignalled: The port contents should be updated every time time one period of packets is ready. This is for audio data, as this allows the code responsible for reading/writing the Port buffers to buffer the sink/source events untill one period has arrived, and then encode/decode the events all at once from/to the Port buffer. This is a big performance boost due to locallity of data (cache) and the possibility of using SIMD instructions, especially for big buffers. - Direction: A port is either a Playback or a Capture port. Playback ports are filled by the Audio API and packetized into the iso stream, Capture ports are filled by the iso stream and read out by the Audio API. \note maybe someday we'll allow any access type with any buffer type, but that doesn't seem to be nescessary now. \note there are some relations between the DataType and the PortType. These relations should be established in the derrivative classes of Port. \note one of the fishy things about Ports is the order in which you can call Port methods and change parameters. more on that later. In order to facilitate the management of a collection of Ports, a PortManager class is implemented. This class implements methods like addPort(), deletePort() etc... It also allows to initialize, prepare and reset all ports in it's collection. The idea is that we present a collection of ports to the Audio API side which from which it can read or to which it can write. Related classes: Streaming::Port, Streaming::PortManager @subsection connectingisoandaudio Connecting the iso side with the Audio API side The connection between the iso side and the Audio API side is done by the StreamProcessor class. This class inherits both from IsoStream and PortManager. It therefore can be registered to the IsoHandlerManager in order to receive/transmit an iso stream. It can also contain a collection of Ports that serve as a destination/source for the events in the iso stream. The StreamProcessor class is an abstract class, it cannot be instantiated by itself. The classes that implement a StreamProcessor should derrive from either ReceiveStreamProcessor or TransmitStreamProcessor. These classes provide some extra code that differs between directions. A ReceiveStreamProcessor implements the putPacket callback, which is called every time a packet arrives. It is supposed to buffer the events (or the decoded frames). When one period of frames can be transmitted to the Audio API, it should signal this when its isOnePeriodReady() method is called. For PeriodSignalled Ports, the actual transfer from the internal buffer(s) to the Port buffers should be done in the transfer() method. This is because it is not nescessarily so that the buffers of the StreamProcessor's Ports are valid. When the transfer() method is called, the buffers are guaranteed to be valid. The jackd backend for example sets the Port buffers to an internal address before calling transfer(). This allows for a near-zero-copy transfer of the audio: the iso stream events are decoded directly into the jackd sample buffer. For PacketSignalled Ports, the StreamProcessor should decode & write the events when they arrive (in the packet callback). A TransmitStreamProcessor implements the getPacket method to construct packets. The rules wrt Port buffer access and internal buffering are similar to those of the ReceiveStreamProcessor. A StreamProcessor can be enabled and disabled. When a StreamProcessor is disabled, it should not read from or write to it's Port buffers. However, it's putPacket or getPacket callback can be called, so especially for TransmitStreamProcessors one should make sure to generate valid packets (if the device needs them). This behaviour is because some devices need some time before they start sending data, and we want to prevent our port buffers (especially playback) from Xrun due to a StreamProcessor that is already consuming while others are not ready yet. The enable state can be tested with the m_disabled variable. Closely coupled to the enable/disable functionallity is the isRunning() function. This should return true when a StreamProcessor is ready to consume or provide events. \note Mostly, a TransmitStreamProcessor is always 'runnable', but a ReceiveStreamProcessor only becomes running when it actually starts to receive events. In order to make the streaming system work, the StreamProcessors should update the value of m_framecounter in the packet callback. For a ReceiveStreamProcessor this denotes the number of received events, for a TransmitStreamProcessor this is the number of events transmitted. Most of the time this value should be incremented by the number of frames processed in the callback. This increment should be done by calling incrementFrameCounter(nb_frames) to do this thread-safe. The framecounter will be decremented by the streaming thread. A StreamProcessor also has the init(), prepare() and reset() calls, which are still to be documented (see later). Related classes: Streaming::StreamProcessor, Streaming::ReceiveStreamProcessor, Streaming::TransmitStreamProcessor, Streaming::PortManager @subsection mappingports Mapping Ports to IsoStreams The only thing not explained up till now is how the StreamProcessor knows which iso substream to decode to which port. This is done by defining device specific subclasses of the Port class. These classes inherit both from the generic Port class and from a device specific class (e.g. PortInfo). This PortInfo class contains all information the StreamProcessor needs to map the Port onto the IsoStream, or vice versa. Due to the subclassing, these new device-specific ports can be used as if they were a normal Port. An example can be found in \ref amdtpdescription . @subsection puttingtogether Putting it all together @note this is outdated The framework is completed by introducing the StreamProcessorManager. As indicated before, this class implements a 'collection of StreamProcessors' and an IsoHandlerManager. First of all, the StreamProcessorManager is a collection of StreamProcessors, hence it implements the registerStreamProcessor and unregisterStreamProcessor methods. It maintains the list of StreamProcessors under it's control. When StreamProcessors are (un)registered, they are automatically (un)registered to the IsoHandlerManager too, creating IsoHandlers to handle them. Remember that StreamProcessor is a descendant of IsoStream, and can therefore be registered to an IsoHandlerManager. This results in the fact that the iso stream the StreamProcessor is supposed to handle, will be attached to an IsoHandler. Furthermore StreamProcessorManager is a child of the Runnable interface, and can therefore be used as the worker class for a Thread. A complicated sentence to say that the StreamProcessorManager will start up a thread that calls its Execute() function, which in its turn calls the IsoHandlerManager Exectute() method (hence iterating the IsoHandlers managed by the IsoHandlerManager). This thread also performs the synchronisation as described in the next paragraph. The third function of the StreamProcessorManager is the synchronisation between the iso side and the Audio API side. To implement this, the class provides a wait() method that waits on a synchronisation primitive. This primitive is signalled by the thread that iterates the IsoHandlerManager. This thread will signal the primitive when all StreamProcessors indicate that they have one period ready. \note this condition is not perfect, but it will do for the moment The Audio API should call wait(), and when it returns, should call transfer() to transfer the internal buffer contents to the Port buffers. It can then proceed to reading out these Port buffers. The near-zero-copy approach would be to call wait(), then change the Port buffer address to the client's audio buffer for that channel, and then call transfer(). Currently this is for PeriodSignalled Ports only. The PacketBuffered Ports require the Audio API to read/write each Port individually using read/write routines. There is no signalling for these ports. The calls to read/write are also non-blocking, meaning that the Audio API will have to contignously poll these Ports for activity. This can be done with a separate thread, possibly using a sleep() call beween Port buffer fill checks. \note A blocking-read/nonblocking write (and the other way around) version of access to PacketBuffered Ports is planned. Related classes: Streaming::StreamProcessorManager, Streaming::IsoHandlerManager, Streaming::Port @subsection callingorder Some notes on when which method is called It is not very clear when which method is called (init/prepare/reset/...). This is due to the fact that this isn't really 'clean' yet. Therefore it will be documented later. The source code contains some notes on this. Some preliminary statements for StreamProcessor's: - init() should call it's parent class' init to initialize the IsoStream - prepare() - should call it's parent class' prepare first, this makes m_nb_buffers and m_period available. These are needed to allocate the internal buffer. It should then proceed to allocate it's internal buffer(s). - should make sure all ports are ready by calling init() and prepare() for the ports. However this can only be done after all Port parameters (buffersize, port type, ...) are set. Once a Port is init()'d, there are some parameters that cannot be changed (see Port documentation) - when the StreamProcessor is an TransmitStreamProcessor, it might be good to prefill internal buffers. - reset() is called when an xrun occurs. it should clear (& prefill) all buffers, and should also be passed on to the parent in order to reset all counters, parent classes and ports. @section refimplementation Reference Implementation The BeBoB discovery with the AMDTP StreamProcessor can be considered the reference implementation of this model. You can find a description of the AMDTP StreamProcessor in \ref amdtpdescription . */ libffado-2.4.5/doc/mainpage.dox0000644000175000001440000000250114206145246015724 0ustar jwoitheusers/* * This is the main page of the FFADO reference manual, built using * doxygen. */ /** @mainpage FFADO - Free FireWire (pro-)Audio Drivers for Linux @author Pieter Palmers @section intro Introduction FFADO is intended as an intermediate layer between the Linux1394 kernel/userspace layer, and the various audio API's (ALSA, jack, ...) present. \note this is a SVN version, therefore it is highly volatile. Certain parts of the documentation can be outdated. This documentation is also auto-generated, and it can occur that unused classes and interfaces appear in the documentation. This pollution will be cleaned up as we go along. @see @section library Client side library FFADO presents an interface that can be used to implement backends for various audio API's. Currently the best support is for the JACK backend. It is also intended to implement configuration applications like mixer controls etc.. The library API's external C interface is defined in: - @ref ffado.h "" @section devicesupport Adding support for unsupported devices Initial support by FreeBoB was for BeBoB devices only, however the FFADO framework is usable for all FireWire audio devices. Take a look at the @ref adding_devices page for a detailed description. */ libffado-2.4.5/doc/motu_firewire_protocol.txt0000644000175000001440000015607314206145246021007 0ustar jwoitheusersNotes on the FireWire protocol used by MOTU audio devices ========================================================= Author: Jonathan Woithe Document version: 20100914-1 Audio/MIDI data --------------- Note: while this section focuses on the Traveler, the same basic data format is utilised for the 828Mk2, 896HD, Ultralite and possibly other MOTU interfaces. The differences lie in the number of discrete audio channels sent and perhaps the order of some channels within a data block. Audio data is sent via iso packets. With nothing else present on the FireWire bus Iso channel 0 is used by the PC to send data to the MOTU while iso channel 1 is used by the MOTU to send data to the PC. The channels used can be arbitarily chosen by software subject to availability as indicated by the Isochronous Resource Manager (IRM). The MOTU appears to utilise some ideas from IEC 61883-6. For example, each iso packet seems to include a CIP header (the iso packet header has the tag field set to 0x01 to indicate the presence of the CIP). However, the standard is not followed completely to the finest detail. Typical CIP header values from the Traveler for audio data packets at a 44.1 kHz rate with SPDIF enabled and the optical I/O ports set to ADAT mode are as follows: SID = 0x02 dependent on bus topography DBS = 0x13 expressed in quadlets FN = 0 as expected from IEC 61883-6 QPC = 0 as expected from IEC 61883-6 SPH = 1 indicating a source packet header is included. IEC 61883-6 expects SPH to be 0 for A/V transport. Rsv = 0 DBC = yyy Data block count varies from packet to packet FMT = 0x02 IEC 61883-6 expects this to be 0x10 for A/V data. MOTU seem to be using 0x02 for their own purposes. FDF = 0x22 This means the data is 32 bit floating point according to IEC 61883-6. The data isn't 32 bit floating point, so MOTU obviously use this field to mean their own thing. SYT = 0xffff IEC 61883-6 says this field is the time the event is to be presented to the receiver. Clearly this isn't used by MOTU. The FDF field is interesting in that when interpreted as per IEC 61883-6, it doesn't reflect the reality of the audio stream used by the Traveler (see below). One has to assume that MOTU have redefined this for their own purposes. The DBC field is incremented from packet to packet by the number of data blocks contained in the packet. At 1x rates there are nominally 8 data blocks per packet, so DBC increments by 8 between successive iso data packets. The DBS (Data Block Size) varies according to the sampling rate in use. On the Travler at 1x rates (44.1 kHz, 48 kHz) it is 0x13. At 2x rates it reduces to 0x10 while at 4x rates it decreases again to 0x09. Similarly the iso data length differs according to the sampling rate: 0x268 at 1x rates (giving 8 blocks per iso data packet), 0x408 at 2x rates (giving 16 blocks per iso data packet) and 0x488 at 4x rates (giving 32 blocks per iso data packet). DBS/FDF behaves a little differently with the Ultralite interface. At both 1x and 2x rates DBS is 19 (0x13) while FDF is still 0x22. Based on behaviour of earlier interfaces this would give a block size of 19*4 = 76 bytes. However, in reality it is only 52 bytes: 4 bytes SPH, 6 bytes MIDI/control data, 8x3 bytes analog data, 2x3 bytes SPDIF, 2x3 bytes Mix1 and 2x3 bytes padding (probably used as headphone send in transmit stream). It's not clear how the given DBS/FDF values relate to this block size. There are still 8 blocks per iso data packet at 1x rates, and 16 at 2x rates, giving total packet sizes (including the 8 byte CIP) of 424 bytes at 1x rates and 840 at 2x rates. The Traveler usually becomes the IRM on the FireWire bus it is plugged into; when functioning as the IRM it also broadcasts "cycle start" packets on a regular basis as would be expected. These appear to be as per ieee1394-1995. These broadcast a quadlet in the same form as the Traveler's cycle time register with a value equal to the cycle time register at the time the packet was submitted for transmittsion. The format of this register is as follows: bits 31-25 = seconds count (0-127) bits 24-12 = cycle count (0-7999) bits 11-0 = cycle offset (0-3071) Each portion of the register wraps to 0 at the end of the range, causing the next most significant component to be incremented by one. When the "seconds count" overflows it simply wraps to 0 without incrementing anything. The cycle offset is incremented by a 24.576 MHz clock. The cycle count can be thought of as the fractional part of the current second in units of 125 us. The data portion of a typical iso packet to/from the Traveler contains a CIP header and 8, 16 or 32 data blocks (depending on sampling rate) of size DBS quadlets (DBS*4 bytes). Empty packets are utilised periodically to maintain the long-term average equal to the sampling interval. For example, at 48 kHz, 3 packets with 8 datablocks are sent, followed by a single empty packet. The format of each data block varies according to whether a 1x, 2x or 4x clock multiplier is in use. At 44.1 kHz or 48 kHz (ie: a 1x clock), each data block contains the following in this order: 32 bit Source Packet Header (SPH) (+) 48 bits of control/MIDI data (&) 24 bit audio values: headphone L&R / mix1 L&R (*) 24 bit audio values: analog channels 1-8 24 bit audio values: AES-EBU L and R 24 bit audio values: SPDIF L and R 24 bit audio values: ADAT channels 1-8 At 2x clock frequencies (88.2 kHz and 96 kHz) the ADAT channels 5-8 are dropped from the datablock, leaving the following arrangement: 32 bit Source Packet Header (SPH) (+) 48 bits of control/MIDI data (&) 24 bit audio values: headphone L&R / mix1 L&R (*) 24 bit audio values: analog channels 1-8 24 bit audio values: AES-EBU L and R 24 bit audio values: SPDIF L and R 24 bit audio values: ADAT channels 1-4 At 4x clock frequencies (176.4 k kHz and 192 kHz) all digital I/O is disabled along with the separate headphone / mix1 bus. The resulting datablock is formatted as follows. 32 bit Source Packet Header (SPH) (+) 48 bits of control/MIDI, MSB first (&) 24 bit audio values: analog channels 1-8 16 bits of padding (%) Notes: (+) The SPH is defined in IEC 61883-6: Bits 31-25 = reserved Bits 24-12 = cycle count (0-7999) Bits 11-0 = cycle offset (0-3071) (&) These values have been observed to be almost always 0 in packets sent by the PC except when MIDI is sent, but for packets sent by the MOTU they are generally non-zero. See below for more details on these bytes. (*) Packets from the traveler send mix1 data here; packets from the PC send data for the headphone channel here. (%) It appears the Motu pads data in a cycle using values of either 0x0000 or 0xffff. It appears to use a regular pattern, although the pattern itself appears variable. Two patterns have been observed so far: * two blocks of 0xffff followed by two blocks of 0x0000. * a block of 0xffff followed by three blocks of 0xffff. The PC driver always appears to send pad values of 0x0000. If the optical ports have been set to TOSLINK rather than ADAT, the data sent in the iso datablocks is adjusted accordingly - the 8 (or 4) ADAT channels and 2 coax SPDIF channels are omitted and the 2 optical TOSLINK channels are sent in their place. For packets from the PC to the MOTU, the timestamp given in the SPH probably represents the cycle time at which the data given should be output by the interface. For packets coming from the MOTU to the PC this timestamp is most likely the cycle time at which the data was sampled by the interface. The cycle offset increment used for each successive datablock varies according to the sampling frequency as one would expect. While the increment value is "mostly constant" it does vary by +/-1 at times. This would be to keep the data in sync with the audio sampling frequency in use, as some audio sampling frequencies - those which are multiples of 44.1 kHz - are not integral factors of the cycle offset frequency (and even those which are sometimes deviate from the theoretical increment by 1). The increments seen in data coming from the Motu are generally a lot more consistent than those coming from the PC. It is clear from this that the audio clock is not locked to the firefire cycle clock in any way. The 48 bits Control/MIDI data appears to play a part in MIDI data transmission. When the PC is transmitting a MIDI stream to the Motu it does so byte by byte. A byte of midi data is sent using the first and third bytes following a data block's SPH, forming a midi subpacket for want of a better description. The format of the midi subpacket appears to be 0x0100nn where nn is the MIDI byte being sent. Having more than one MIDI subpacket in a single transmitted iso data packet has never been observed; it seems therefore that on this interface the timing of the MIDI subpackets is to match exactly their transmission onto the wire. Furthermore, sending data to the MOTU at a rate faster then the hardware midi rate of 3125 bytes per second (31250 baud with 8 bit data, 1 start bit and 1 stop bit) will cause MIDI bytes to be dropped or corrupted in interesting ways. For data coming from the Motu it appears that these 48 Control/MIDI bits are used for a number of purposes, although some of the details are yet to be worked out. However, it appears that MIDI data is intermingled with this. At this stage it seems that when bit 40 is set then bits 24-31 contain a MIDI byte. The contents of bits 41-47 and 32-39 do not appear to be affected by having bit 40 set, so at this stage one concludes that their function (mostly related to Cuemix setting updates) continues even when MIDI data is sent. Obviously any other function of bits 24-31 is interrupted when these bits are required for a MIDI byte. Putting all this together, bit 40 appears to be a "MIDI byte present" bit; if set, bits 24-31 contain a MIDI byte. All other bits of the Control/MIDI fields continue to function even when bit 40 is set. If this definition is adopted it applies equally well to both incoming and outgoing MIDI data. It should be noted that the Traveler has never been observed to send more than one MIDI data byte per iso packet. Presumedly therefore the timing of the bytes delivered by the Traveler is very close to the timing on the MIDI wire. However, other MOTU devices (the 828Mk2 for example) do transmit more than one MIDI byte in a single packet. On these devices MIDI messages must be buffered in the device and sent in a burst once a complete message has been received. Audio channel locations in a frame ---------------------------------- Not surprisingly the different MOTU interfaces place channels at different offsets within a frame. This section attempts to document what we know about the channel locations for various devices. 828 Mk 1: Analog 1-8: 10, 13, 16, 19, 22, 25, 28, 31 SPDIF 1-2: 34, 37 ADAT 1-4: 40, 43, 46, 49 ADAT 5-8 (1x rates only): 52, 55, 58, 61 896HD: Mix L-R (in, not at 4x): 10, 13 Phones L-R (out, not at 4x): 10, 13 Analog 1-8 (1x/2x rate): 16, 19, 22, 25, 28, 31, 34, 37 Analog 1-8 (4x rate): 10, 13, 16, 19, 22, 25, 28, 31 MainOut L-R (out, 1x/2x only): 40, 43 Unknown 1-2 (in, 1x/2x only): 40, 43 ADAT 1-4 (1x/2x only): 46, 49, 52, 55 ADAT 5-8 (1x only): 58, 61, 64, 67 AES/EBU 1-2 (1x with ADAT active): 70, 73 AES/EBU 1-2 (2x with ADAT active): 58, 61 AES/EBU 1-2 (1x/2x with ADAT off): 46, 49 828 Mk 2: Mix L-R (in): 10, 13 Phones L-R (out): 10, 13 Analog 1-8: 16, 19, 22, 25, 28, 31, 34, 37 Mic 1-2 (in): 40, 43 Main L-R (out): 40, 43 SPDIF 1-2: 46, 49 ADAT 1-4: 52, 55, 58, 61 ADAT 5-8 (1x rates only): 64, 67, 70, 73 Traveler: Mix L-R (in, 1x/2x rates only): 10, 13 Phones L-R (out, 1x/2x rates only): 10, 13 Analog 1-8 (1x/2x rate): 16, 19, 22, 25, 28, 31, 34, 37 Analog 1-8 (4x rate): 10, 13, 16, 19, 22, 25, 28, 31 AES/EBU 1-2 (1x/2x rate): 40, 43 SPDIF/Toslink 1-2 (1x/2x rate): 46, 49 ADAT 1-4 (1x/2x rate): 52, 55, 58, 61 ADAT 5-8 (1x rate): 64, 67, 70, 73 Ultralite: Mix L-R (in): 10, 13 Phones L-R (out): 10, 13 Mic 1-2 (in): 16, 19 Analog 1-2 (out): 16, 19 Analog 3-8: 22, 25, 28, 31, 34, 37 SPDIF 1-2 (in): 40, 43 Main L-R (out): 40, 43 Padding (in): 46, 49 SPDIF 1-2 (out): 46, 49 8Pre: Mix L-R (in): 10, 13 Phones L-R (out): 10, 13 Analog 1-8 (in): 16, 19, 22, 25, 28, 31, 34, 37 Main L-R (out): 16, 19 Padding (out): 22, 25 ADAT 1-8 (in): 40, 43, 46, 49, 52, 55, 58, 61 ADAT 1-8 (out): 22, 25, 28, 31, 34, 37, 40, 43 828 Mk 3: Mic 1-2 (in, 1x/2x rates): 10, 13 Phones L-R (out, 1x/2x rates): 10, 13 Unknown 1-2 (out, 4x rates): 10, 13 Analog 1-8: 16, 19, 22, 25, 28, 31, 34, 37 Return 1-2 (in): 40, 43 Main L-R (out): 40, 43 SPDIF 1-2 (1x/2x rates): 46, 49 Unknown (out, 4x rates): 46, 49 Reverb 1-2 (in, 1x/2x rates): 52, 55 [ Optical ports follow according to optical mode: first Optical-A channels, then optical-B channels. Out channels start at 52, in channels start at 58. At 1x rates 58/61 are "unknown" ahead of the optical channels, with inputs at 76/79 being unknown at 2x rates. ] Ultralite Mk 3: Mix L-R (in): 10, 13 Phones L-R (out): 10, 13 Mic 1-2 (in): 16, 19 Analog 1-2 (out): 16, 19 Analog 3-8: 22, 25, 28, 31, 34, 37 SPDIF 1-2 (in, 1x/2x rates): 40, 43 Padding (out, 1x/2x rates): 40, 43 SPDIF 1-2 (out, 1x/2x rates): 46, 49 Ultralite Mk 3 hybrid: Mix L-R (in): 10, 13 Phones L-R (out): 10, 13 Mic 1-2 (in): 16, 19 Analog 1-2 (out): 16, 19 Analog 3-8: 22, 25, 28, 31, 34, 37 SPDIF 1-2 (in, 1x/2x rates): 40, 43 Main L-R (out): 40, 43 Reverb 1-2 (in, 1x/2x rates): 46, 49 SPDIF 1-2 (out, 1x/2x rates): 46, 49 Unknown 1-4 (in, 1x/2x rates): 52, 55, 58, 61 Traveler Mk 3: Mix L-R (in): 10, 13 Phones L-R (out): 10, 13 Analog 1-8: 16, 19, 22, 25, 28, 31, 34, 37 AES/EBU 1-2 (1x/2x rates): 40, 43 SPDIF 1-2 (1x/2x rates): 46, 49 Reverb 1-2 (in, 1x rates): 52, 55 Unknown 1-2 (in, 1x rates): 58, 61 [ Optical port channels follow in a similar was as for the 828 Mk 3. The precise locations are yet to be confirmed as of 14 Sept 2010. ] SMPTE timecode -------------- It appears that the traveler itself has no way to generate SMPTE timecode internally. The SMTPE console application allows one to generate timecode, but the timecode stops as soon as this application is exitted. In addition, changing any of the settings in the SMPTE console does not appear to change any hardware settings in the MOTU with the exception of the "clock/address" control - this changes the source in a similar way to that in the main MOTU setup program. The obvious conclusion here is that SMTPE generation appears to be a feature implemented in the host software. Talkback/listenback ------------------- These features of the Cuemix console appear to be implemented in the host software. Enabling either of these appears to simply set the mixer registers to suit talkback/listenback, with the "normal" settings restored when talkback/listenback is disengaged. The motu itself does not appear to have any explicit on-board support of these modes. Bus renaming in Cuemix console ------------------------------ The naming of the 4 mix buses provided by the Cuemix console seems to be internal to the Cuemix console application. Changing the mix names results in no traffic being sent to/from the motu. MIDI in/out ----------- MIDI data is sent within the first 48 bytes of the data blocks within an iso data packet. This is described in detail in the "Audio/MIDI data" section. Controlling Cuemix in pre-Mark3 models -------------------------------------- It has been noted that some mixer settings (particularly registers 0x4yyy) have been found to utilise "write enable" bits which when set allow their associated part of the control register to be acted upon. There is no reason to think that the 0x4yyy registers are unique here; it's likely that other control registers implement a similar idea. It is thought that whenever bits are seemingly set to 1 by the Cuemix console there is a stong possibility they form some kind of enable mask. When such values are noted in the register description it is likely that they must be supplied as noted if the desired register change is to be successful. In time it is hoped that the function of these magic settings can be deduced. Finally, when manually changing Cuemix settings on the Motu's front panel, the Cuemix console is observed to update its display accordingly. The data for these updates comes via a byte stream transmitted by the Motu using a key/value system in the first two bytes of each data block in the iso stream. Within a given data block, bits 7-1 of the first byte is the key while the second byte gives the respective value. Recall that bit 0 of the first byte is a "MIDI byte present" flag, indicating that the third byte in the data block contains a MIDI data byte - refer to the "Audio/MIDI data" section for more details. The following describes all keys observed so far from the Traveler. Note that the key values reported here are for bits 7-0 of the first byte with bit 0 (the MIDI present bit) masked to zero. 0x04 = Some kind of timer/counter. Details not yet identified. 0x0c = Mix bus sync key. The mix bus to which the next mix bus parameter(s) apply to is given by bits 7-5 in the data byte. 0x14 = channel gain value (0x00-0x80) 0x1c = channel pan value (0x00-0x80, 0x40=centre) 0x24 = channel control: 0x01=mute, 0x02=solo, 0x08=paired 0x2c = mix bus gain (0x00-0x80) 0x34 = mix bus destination/mute. Bits 3-0 are as per "output destination" in register 0x0c20; bit 4 is the mute bit. 0x3c = main out volume (0x00-0x80) 0x44 = phones volume (0x00-0x80) 0x4c = phones assign (values as per "output destination" in register 0x0c20) 0x54 = at 48 kHz remains fixed at 0x00. Purpose unknown. 0x64 = at 48 kHz remains fixed at 0x00. Purpose unknown. 0x6c = +6 dB boost (analog channels 5-8 only). Bits 4-7 = channels 5-8. 0x74 = ref level. Bits 4-7 = channels 5-8. Bit set/clear = +4/-10. 0xfc = Some kind of timer/counter. Details not yet identified. For many of these keys there are multiple values to communicate; for example, for key 0x14 (channel gain) there are up to 20 values for each of 4 mix buses, corresponding to the 20 input channels available (analog 1-8, SPDIF, AES/EDU, ADAT 1-8). In such situations the multiple values for a given mix bus are transmitted sequentially (each having the same key) following a mix bus sync key which gives the mix bus to which the values apply. In the case of the Traveler the following sequence of keys is used. Timing/counter events have been omitted from this sequence. N is the number of channels active. Mix bus sync key N channel gain keys Mix bus sync key N channel pan keys Mix bus sync key N channel control keys Mix bus sync key Mix bus gain key Mix bus destination key Main out volume key Phones volume key Phones destination key Input 6dB boost key Input reference level key The device-wide keys are therefore seen 4 times as often as the per-mixbus keys. Curiously enough, the gain trim and pad settings for analog channels 1-4 use a different method to communicate their changed status; for these settings the Motu writes to a specific address on the PC and in response the PC reads a register from the Motu. When a channel's gain trim or pad setting is altered: * Motu sends 0x20ffffff to address offset 0x100000000 on the PC * In response the PC reads register 0x0c1c from the Motu to obtain the new gain trim value and pad setting. Controlling Cuemix in Mark3 models ---------------------------------- With the introduction of the "Mark3" interface models (Ultralite, Traveler, 828) MOTU completely changed the method used to control the Cuemix features of the interface. This was partly out of necessity - with the additional Cuemix features being added to the "Mark3" models MOTU otherwise faced an unbounded expansion in the number of device registers needed. Protocol analysis of an Ultralite-mk3 was provided by MG and GQ. The Mark3 interfaces use a variation of a key-value protocol involving block writes to register device register 0x10000 (bus address ffc0-ffff-0001-0000 nominally. Either 2 or 3 quadlets are written depending on the message being sent. For this documentation we will refer to the bytes within a message as b00 .. b11, with b00 being the first byte sent. Byte b00 and b01 form a heartbeat word. Precisely how this is constructed is not currently known, but it seems it might just be a monotomically increasing number probably used by the device to detect missing data or something. Byte b02 seems to be some kind of class identifier. Currently identified classes are: 0x66: Faders (including bus master), pan, input trim 0x69: Mute/Solo, Routing selection Specific message formats are described below. Trim: 3 quadlet message b00-b01 - heartbeat b02 - 0x66 b03 - channel select (0x00-0x09) b04 - 0x02 (select trim control) b05-b06 - unknown, TBA b07-b10 - data (big endian float, 0.0 - 24.0) b11 - 0x00 Reverb send: TBA Channel faders: 3 quadlet message b00-b01 - heartbeat b02 - 0x66 b03 - bus select (0x00-0x09) b04 - 0x03 (select fader control) b05 - channel select (0x02-0x09) b06 - 0x02 b07-b10 - data (big endian float, 0.0 - 1.0) b11 - 0x00 Pan: 3 quadlet message b00-b01 - heartbeat b02 - 0x66 b03 - bus select (0x00-0x09) b04 - 0x02 (select pan control) b05 - channel select (0x02-0x09) b06 - 0x02 b07-b10 - data (big endian float, -1.0 - 1.0) b11 - 0x00 Bus master fader: 3 quadlet message b00-b01 - heartbeat b02 - 0x66 b03 - bus select (0x00-0x09) b04-b05 - 0x02-0x00 (select bus master control) b06 - 0x02 b07-b10 - data (big endian float, 0.0 - 1.0) b11 - 0x00 Bus routing: 2 quadlet message b00-b01 - heartbeat b02 - 0x69 b03 - output select (0x00-0x06, 0xff=disabled) b04 - bus select (0x00-0x07) b05-b07 - 0x00-0x00-0x02 Mute: 2 quadlet message b00-b01 - heartbeat b02 - 0x69 b03 - state (0x00=disable, 0x01=enable) b04 - bus select (0x00-0x07) b05 - 0x00 (select mute control) b06 - channel select (0x01=bus master, 0x02-0x0b) b07 - 0x02 Channel solo: 2 quadlet message b00-b01 - heartbeat b02 - 0x69 b03 - state (0x00=disable, 0x01=enable) b04 - bus select (0x00-0x07) b05 - 0x01 (select solo control) b06 - channel select (0x01=bus master, 0x02-0x0b) b07 - 0x02 Controlling sample rate ----------------------- Sample rate is mainly controlled by the clock control register (0x0b14) although other registers do play a part. It seems that the front panel sample rate control is locked out by any write to the clock control register; once locked it is only unlockable by power cycling the MOTU or by unplugging it from the computer. Device identification --------------------- The ieee1394 vendor ID field currently seems to be set to 0x000001f2 to signify MOTU. The ieee1394 model ID is a little more interesting. Originally it was thought that 0x00101800 represented the 828MkII while 0x00104800 was the Traveler. However, following an update to Traveler firmware 1.07 the model ID field changed to 0x00107800. From this it appears that the model ID field probably does not identify the MOTU model but rather communicates the firmware version along with other information. It appears that bits 19-12 of the model ID are a binary-coded decimal representation of the minor firmware version number while at least bits 23-20 (and possibly 27-20) give the major firmware version number. Further datapoints will be needed to verify these details, particularly the span of the major version number. The meaning of bits 11-0 are currently unknown; they seem to equal 0x800 in all 828MkIIs and Travelers. The ieee1394 GUID (globally unique ID) is preserved for a given unit across firmware updates, but is unique to each particular unit. Therefore it cannot be used to unambiguously differentiate between different MOTU interfaces. However, the unit directory version entry does appear to be set for particular models across firmware upgrades (0x00000003 for 828MkII, 0x00000009 for Traveler). Therefore the best bet at this stage appears to be a probe for a vendor ID of 0x000001f2 with one of the above version values in the unit directory of the config ROM. Unit directory versions for MOTU hardware are as follows: 0x00000001 = The original 828 0x00000003 = 828 Mk 2 0x00000005 = 896HD 0x00000009 = Traveler 0x0000000d = Ultralite 0x0000000f = 8pre 0x00000015 = 828 Mk 3 0x00000019 = Ultralite Mk 3 0x0000001b = Traveler Mk 3 0x00000030 = Ultralite Mk 3 hybrid Alternatively one could probe for registers known to exist on only one of the interfaces. The trim gain / 20 dB pad status register (0x0c1c) for example can be used to differentiate a Traveler from an 828Mk2 since these features are only present on the Traveler. At this stage however the unit version number is probably sufficient and far less complicated in the long run. Firmware updates ---------------- A firmware update is initiated by powering the unit up in "update" mode by turning on while the "cursor" button is held. After verifying "update" mode, registers 0x010000, 0x010004, 0x010008 and 0x01000c are read; with firmware 1.04 loaded the values returned were 0x23ba58f, 0x86e5aa30, 0xe7fb2202 and 0x4b85130d. These match exactly the first 16 data bytes of firmware 1.07 as downloaded to the device, so it's thought that these first 4 quadlets contain some kind of magic number signature designed to confirm that the device really is a MOTU, or (somewhat less likely) to perhaps confirm that the MOTU is in update mode. Next 0x4d4f5455 ("MOTU") is written to register 0x0c00; the new firmware is then loaded by writing to sequential registers 0x010000 - 0x019ffc and 0x01c000 - 0x02fffc. At the end of writing, 0x00 is written to register 0x0c00. Verification is by way of reading back the firmware registers from the device. The process of confirming that the device is in update mode is not yet fully understood. In terms of the startup sequence it appears that the only difference between normal and update modes is that register 0x0400 is set to 0x410df42 in normal mode and 0x410f3a8 when in update mode. The Travimage.bin firmware file provided by MOTU seems to comprise some kind of header in bytes 0x00 - 0x17, with the actual firmware data starting at byte 0x18. The firmware is stored in network byte order (ie: big endian) quadlets - precisely the same format used to send them to the MOTU device. The file also appears to include bytes 0x01a000 - 0x01bfff even though the traveler updater doesn't appear to send this range to the MOTU device. The firmware file for 1.07 seems to contain 0x020000 bytes of firmware data; given that the firmware updater writes up to register 0x02fffc the updater must infer data values of 0xff once the file ends. Register map ------------ MOTU registers currently known. The base address is 0xfffff0000000. 0x0b00 - Iso streaming control 0x0b04 - Device initialisation/discovery? 0x0b08 - Device initialisation/discovery? 0x0b10 - Optical port control? 0x0b14 - clock control register 0x0b1c - 896HD register possibly related to programmable meters 0x0b24 - 896HD programmable meter register (always probed during discovery/init) 0x0b28 - copy of model_id unit directory 0x0b2c - something related to selections in mixer application 0x0c00 - firmware programming control 0x0c04 - routing / port configuration 0x0c08 - input level (for analog channels 5-8 only) 0x0c0c - "Main out" volume control 0x0c10 - Phones volume control 0x0c14 - boost controls (for analog channels 5-8 only) 0x0c18 - something related to selections in mixer application 0x0c1c - gain trim / 20 dB pad controls (for analog channels 1-4 only) 0x0c20 - mix1 routing control 0x0c24 - mix2 routing control 0x0c28 - mix3 routing control 0x0c2c - mix4 routing control 0x0c60 - ASCII name of current clock source (characters 0-3) 0x0c64 - ASCII name of current clock source (characters 4-7) 0x0c68 - ASCII name of current clock source (characters 8-11) 0x0c6c - ASCII name of current clock source (characters 12-15) 0x0c70 - gain trim / phase invert (Ultralite only, analog channels 1-4) 0x0c74 - gain trim / phase invert (Ultralite only, analog channels 5-8) 0x0c78 - gain trim / phase invert (Ultralite only, SPDIF 1 and 2) 0x4000 - mix1 gain/pan/solo/mute, analog channel 1 0x4004 - mix1 gain/pan/solo/mute, analog channel 2 0x4008 - mix1 gain/pan/solo/mute, analog channel 3 0x400c - mix1 gain/pan/solo/mute, analog channel 4 0x4010 - mix1 gain/pan/solo/mute, analog channel 5 0x4014 - mix1 gain/pan/solo/mute, analog channel 6 0x4018 - mix1 gain/pan/solo/mute, analog channel 7 0x401c - mix1 gain/pan/solo/mute, analog channel 8 0x4020 - mix1 gain/pan/solo/mute, AES-EBU 1/L 0x4024 - mix1 gain/pan/solo/mute, AES-EBU 2/R 0x4028 - mix1 gain/pan/solo/mute, SPDIF 1/L 0x402c - mix1 gain/pan/solo/mute, SPDIF 2/R 0x4030 - mix1 gain/pan/solo/mute, ADAT channel 1 0x4034 - mix1 gain/pan/solo/mute, ADAT channel 2 0x4038 - mix1 gain/pan/solo/mute, ADAT channel 3 0x403c - mix1 gain/pan/solo/mute, ADAT channel 4 0x4040 - mix1 gain/pan/solo/mute, ADAT channel 5 0x4044 - mix1 gain/pan/solo/mute, ADAT channel 6 0x4048 - mix1 gain/pan/solo/mute, ADAT channel 7 0x404c - mix1 gain/pan/solo/mute, ADAT channel 8 0x4100 : - mix2 gain/pan/solo/mute, order as for mix1 0x414c 0x4200 : - mix3 gain/pan/solo/mute, order as for mix1 0x424c 0x4300 : - mix4 gain/pan/solo/mute, order as for mix1 0x434c 0x010000 : - firmware, block 1 0x019ffc 0x01c000 : - firmware, block 2 0x02fffc Register details ---------------- 0x0b00 - Iso streaming control This register is primarily for the control of iso streaming. It has been observed to be set to one of two values: 0x80810000 or 0xc0c10000. The latter value appears to activate iso streaming while the former turns it off. On readback bits 31 and 23 are zero no matter whether the previously written value was 0x80810000 or 0xc0c10000. This suggests that these two bits are effectively "write enable" for iso receive/send configuration on the Motu, with bits 30/22 being "iso receive/send enable" (again as viewed by the Motu). Experiments indicate that bits 29-24 give the iso channel number the Motu should expect data on (ie: the PC->Motu iso channel number) while bits 21-16 specify the iso channel the Motu should send its data on (ie: the Motu->PC iso channel number). This makes sense since there are 64 iso channels available on the FireWire bus requiring 6 bits to specify. If it is then assumed that bits 15-0 are reserved we have the following layout for this register. bit 31 = enable configuration change of PC->Motu iso streaming bit 30 = PC->Motu iso streaming enable bits 29-24 = PC->Motu iso channel bit 23 = enable configuration change of Motu->PC iso streaming bit 22 = Motu->PC iso streaming enable bits 21-16 = Motu->PC iso channel bits 15-0 = reserved, always 0 0x0b04 - Device initialisation/discovery? This register seems to come into play only during device discovery/initialisation. The driver writes 0xffc20001 to this register which is accepted by the MOTU. The purpose of this is not known. 0x0b08 - Device initialisation/discovery? This is another register which shows up only during device discovery/initialisation. A value of 0x00 is written to this register which is accepted by the MOTU. The purpose of this is not currently understood. 0x0b10 - Optical port control? It is not yet clear what this register does. It plays some part in setting the mode of the optical ports (see also register c04). Bit 7 is set to 1 by Cuemix console whenever the optical input port is set to "off" or "TOSLink" and is 0 if set to "ADAT". Bit 6 is set to 1 by Cuemix console whenever the optical output port is set to "off" or "TOSLink" and is 0 if set to "ADAT". Bit 1 seems to be set most if not all of the time in values written by the Cuemix console. Originally it was thought that bit 7 might control the availability of the 8 ADAT inputs while bit 6 controlled the ADAT outputs. However, subsequent tests seemed to indicate that this was not the case - setting these bits in isolation did not affect the hardware cuemix display in any way. When this register is read back its value appears to be 0x00001800 no matter what the optical inputs are set up as. 0x0b14 - clock control register This register is used in conjunction with others (like 0x0c0-0x0c6) to control the sample clock. Not all bits have been identified yet. Any write to this register seems to lock out the front panel sample rate control until the MOTU is turned off or the MOTU is unplugged from the computer. The details of the clock control register are summarised below. bits 31-29: unknown at present. Seem to always be set to 0. bit 28: unknown at present. For 896HD this seems to be set to 1 whenever the sample rate, sample rate conversion or clock mode is changed. For other interfaces it appears to always be set to 0. bit 27: in 2x or 4x clock mode, this bit indicates whether word clock out should follow the system clock (bit 27 set to 1) or should be forced to the base rate (bit 27 set to 0). bits 26: purpose unknown at present. Bit 26 appears to be set in every write to this register for the Traveler while for the Ultralite it is always 0. Behaviour on other interfaces are unknown at present. On the Traveler, if bit 26 is set then bits 25-24 seem to control device muting in some way. Based on other systems we currently take bit 26 to be an "enable" bit for bits 25-24 on the Traveler. For other interfaces we take it to always be zero. bits 25-24: related to device muting, although behaviour differs between devices and is dependent in some ways on bit 26. Bits 26-24 are toggled at various staged of rate changes. On the Traveler if bit 26 is set then the device is muted if both bits 25 and 24 are off. Having either bit set seems enough to unmute the device, but other systems seem to set both on. On the Ultralite the device is muted if both bits are off irrespective of the setting of bit 26. The nature of the behaviour on other interfaces is yet to be determined. Based on other systems we'll assume bits 25-24 together control device muting: both off means device muted, both on means device unmuted, and other combinations (which seem to still unmute the device in practice) are unknown. bits 23-6: unknown at present. All bits appear to always be set to zero. bit 9-8: Sample rate conversion setting (896HD only, always 0 on other interfaces): 0 = off 1 = AES/EBU in 2 = AES/EBU out slave 3 = AES/EBU out 2x bits 7-6: unknown at present. Always observered to be 0. bit 5: 4x rate multiplier selector Enables the 4x clock multiplier (giving access to 176000 Hz and 192000 Hz rates) bit 4: 2x rate multiplier selector Enables the 2x clock multiplier (giving access to 88200 Hz and 96000 Hz rates) bit 3: Base rate selector: 0 = 44100 Hz 1 = 48000 Hz bits 2-0: clock source: 0 = internal (and SMTPE if in the Audio console) 1 = ADAT optical 2 = (coaxial) SPDIF or (optical) TOSLink 3 = SMTPE (set only by SMTPE console) 4 = Word clock in 5 = ADAT 9 pin 6 = [ reserved? ] 7 = AES-EBU Note: when changing the rate multipliers additional work needs to be done involving bits 11-8 of register 0x0c04 (routing/port configuration register), bits 6 and 7 of 0x0b10 (optical control register) and other as yet unidentified bits of 0x0b14. While 0x0b10 and 0x0c04 are always written, they only seem to change value only when moving to a 4x multiplier. to ensure the ADAT/SPDIF optical port is disabled at 4x sample rates. The rest of the details associated with these other registers are yet to be deduced. Note for instance that the 0x0b10 changes only appear to affect ADAT controls - verified by observing that the same changes to 0x0b10 are made when the optical ports are turned off in the Audio console program. When the clock source is changed via this register, the textual name of the source is always sent immediately afterward to 0x0c60-0x06c. This is used to report the current clock as selected by the PC on the LCD via the setup menu. See the description of these registers for more details. SMTPE is interesting: its distinct value (3) is configured only if one requests SMTPE as the clock source under the SMTPE console. Selecting SMTPE as the source in the Audio console appears to use the "internal" setting for bits 0-3. 0x0b1c - 896HD register associated with programmable meters The role of this register is currently unknown. Whenever the programmable meters are configured via register 0x0b24, 0x0400 is written to this register after the write to register 0x0b24. 0x0b24 - 896HD programmable meter register Register 0x0b24 doesn't exist in the MOTU Traveler/828Mk2 hardware, but a read request is always sent to it twice during device discovery/initialisation. In response these interfaces return "type_error" with data equal to 0x40000. The precise purpose of this is not known but it could be part of the device probing sequence. Register 0xb24 does however exist on the 896HD and appears to control the programmable meters: bits 31-14: Unknown at present. Always observed to be set to 0. bits 13-11: Peak hold time: 000 = off 001 = 2 sec 010 = 4 sec 011 = 10 sec 100 = 1 minute 101 = 5 minutes 110 = 8 minutes 111 = infinite bits 10-8: Clip hold time: 000 = off 001 = 2 sec 010 = 4 sec 011 = 10 sec 100 = 1 minute 101 = 5 minutes 110 = 8 minutes 111 = infinite bits 7-3: Unknown at present. Always observed to be set to 0. bit 2: AES/EBU meter source: 0 = AES/EBU out 1 = AES/EBU in bits 1-0: Programmable meter source: 0 = Analog out 1 = ADAT in 2 = ADAT out 0x0b28 - copy of model_id unit directory This register appears to be a copy of the model_id unit directory, normally found in config ROM register 0x0434. The presence and contents of this register have only been verified on the 828MkII - the driver reads this register when a 828MkII is connected but the Traveler initialisation sequence does not appear to read it. 0x0b2c - something related to selections in mixer application This register has something to do with destination of the selected mix bus in the mixer application. When a mix bus is selected, 0xbXX is written to this register where XX is the output destination of the selected mix bus. The interpretation of XX is the same as for the 0x0c20 register - see below. The point of this is not currently known. If "Cuemix input includes computer output" is selected in cuemix console: 0xc01 is written to register 0x0b2c 0x001 is written to register 0x0c18 When "Cuemix input includes computer output" is turned off in cuemix console: 0xc00 is written to register 0x0b2c 0x000 is written to register 0x0c18 When Cuemix console is started, 0xb00 is written to register 0x0b2c after the rest of the startup sequence is complete; some time later a value of 0x0b02 is written. This sequence is possibly related to the mix inherently selected when cuemix is started; as noted above, when a mix is selected it does cause 0xb00 to be written to. Register 0x0b2c appears to be read-only. 0x0c00 - firmware programming control It appears that when the MOTU is in "update" mode this register is used to enable writing to the device's firmware area (registers 0x010000 - 0x019ffc and 0x01c000 - 0x02fffc). Writing 0x4d4f5455 ("MOTU") seems to enable writes to the firmware while writing 0x00 is written when the firmware update is complete. 0x0c04 - routing / port configuration This register appears to have something to do with routing and port configuration. On some devices (the Ultralite for example) this register must be initialised to something sensible before the device will accept streaming enable commands via the streaming control register. Bits 7-0 control the source for the phones output. The numbers used are the same as for the output destination of the mix routing control (0x0c20 etc) - see below for details. Bits 9-8 specify the mode of the optical input port: 00 = off, 01 = ADAT, 10 = TOSLink (numbers given are in binary) The "TOSLink" option is not available on the 896HD. Register 0xb10 also plays a role in setting the optical input port mode. Bits 11-10 specify the mode of the optical output port in the same way as for the optical input port. Again, register 0b10 seems to play a role in setting the optical output port mode. Bit 24 enables the setting of the phones source. Bit 25 probably allows the optical modes to be set. Bits 31-26, 23-12 are unknown at present. They seem to always be zero. 0x0c08 - input level (for analog channels 5-8 only) This sets the input level for analog channels 5 to 8. The default is +4 dBU, but this register can be used to select -10 dBU if desired. Only bits 4-7 appear to be significant in this register when setting input level; all other bits are set to zero. bit 4: analog channel 5 input level bit 5: analog channel 6 input level bit 6: analog channel 7 input level bit 7: analog channel 8 input level It seems likely that bits 0-3 are reserved for analog channels 1-4 respectively. When a channel's bit is set (the default condition) an input level of +4 dBU is assumed. If the bit is zero it selects an input level of -10 dBU for the respective channel. A write to this register sets the input level for all channels. The MOTU mixer application ties analog channels 5/6 and 7/8 together (presumedly as a stereo pair) and activates the input level for the pair whenever one channel is selected (even when the channels are not "paired"). However, it has been verified that the input level of channels can be individually controlled though this register. 0x0c14 - boost controls (for analog channels 5-8 only) Bits 4-7 of this register controls the activation of an additional 6dB of gain for analog channels 5-8. All other bits are set to zero. bit 4: analog channel 5 boost bit 5: analog channel 6 boost bit 6: analog channel 7 boost bit 7: analog channel 8 boost Once again, bits 0-3 are probably reserved for analog channels 1-4 if they were ever to acquire this functionality. When a bit is zero (the default condition) the additional 6 dB boost is not active. When set to 1, the boost becomes active for the respective channel. The boost status of all 4 channels is always updated by a write to this register. 0x0c18 - something related to selections in mixer application If "Cuemix input includes computer output" is selected: 0xc01 is written to register 0x0b2c 0x001 is written to register 0x0c18 When "Cuemix input includes computer output" is turned off: 0xc00 is written to register 0x0b2c 0x000 is written to register 0x0c18 0x0c0c - "Main out" volume control Bits 7-0 of this register control the device's "main out" volume (0x80 = 0 dB, 0x00 = -inf). Bits 31-8 appear unused at present. 0x0c10 - phones volume control Bits 7-0 of this register control the device's phones volume (0x80 = 0 dB, 0x00 = -inf). Bits 31-8 appear unused at present. 0x0c1c - gain trim / 20 dB pad controls (for analog channels 1-4 only) This register controls the trim gain and 20 dB pad available on analog channels 1-4. Byte 0 (the least significant byte) controls analog channel 1. Bytes 1-3 are for analog channels 2-4 respectively. Bit 7 in each byte appears to always be set to 1. The effect of setting bit 7 to 0 is currently unknown. On write, bit 7 may be a write-enable control. Bit 6 of a byte controls the 20 dB pad for the channel. When bit 6 is set (ie: equal to 1) the pad is engaged. When bit 6 is zero the pad is switched out. Bits 0-5 set the gain trim value; 0 sets a gain of 0dB (the default) while a value of 0x35 (53) sets the maximum gain of 53 dB. The trim gain acts in steps of 1 dB. It seems the gain trim and pad settings of all channels can be set with a single write to this register. However, if a channel's byte is written as zero its settings remain unchanged. 0x0c20, 0x0c24, 0x0c28, 0x0c2c - mix1, mix2, mix3 and mix4 routing controls These registers control routing for the mix buses. The format used by the four mix buses is the same, so only 0x0c20 (mix1 routing control) is explicitly discussed. bits 31-26: function currently unknown. All bits seem to be set to 0. bit 25: enable setting of mute status and destination. bit 24: enable setting of fader. bits 23-16: function currently unknown. All bits seem to be set to 0. bits 15-13: purpose unknown. Seems to be set to 0. bit 12: mute control. 0=unmute, 1=mute. bits 11-8: output destination (see below). bits 7-0: mix output fader. 0x80 = 0 dB, 0x00 = -inf The output destination is defined as follows: 0x0 = disabled 0x1 = headphone output 0x2 = analog 1-2 0x3 = analog 3-4 0x4 = analog 5-6 0x5 = analog 7-8 0x6 = AES-EBU (Main-out on 896HD) 0x7 = SPDIF (AES-EBU on 896HD) 0x8 = ADAT 1-2 0x9 = ADAT 3-4 0xa = ADAT 5-6 0xb = ADAT 7-8 Note that it is not possible to set the mute and destination status separately - they are both set when bit 25 is set. 0x0c60, 0x0c64, 0x0c68, 0x0c6c - ASCII name of current clock source These registers appear to hold the ASCII name of the current clock source. If the source name is less than 16 characters it is padded with spaces (0x20). The most significant byte of each register holds the leftmost character of the group of 4 characters held by a that register. For example, if a register needs to hold the string "ABCD", the most significant byte will be 65 ("A"). Currently defined strings are as follows. "Internal " - internal clock source "SMPTE " - SMPTE syncing "AES-EBU " - use clock from AES-EBU "SPDIF " - clock from (coaxial) SPDIF "TOSLink " - clock from (optical) TOSLink "Word Clock In " - use the word clock input "ADAT 9-pin " - use the 9-pin ADAT interface for the clock "ADAT Optical " - sync from ADAT optical interface When a computer is connected to the MOTU the only way to set the clock source is from the computer - the front panel controls for this are disabled. The string sent via these registers is used to display the name of the clock source as selected by the computer. If the clock source is changed (via register 0x0b14) but nothing is downloaded using these registers, the "current clock source" display on the LCD via the setup menu will be blank. One would have thought the Motu would be smart enough to provide the display string by itself given that it seems to manage to set the string fine by itself when there's no PC connected and the front panel is used to change the clock source. 0x0c70 - gain trim / phase invert (Ultralite only, analog channels 1-4) On the Ultralite, gain trim is controlled using a different register than with the other MOTU devices. This register controls gain trim and phase inversion for analog channels 1-4. Byte 0 (the least significant byte) controls analog channel 1. Bytes 1-3 are for analog channels 2-4 respectively. Bit 7 in each byte appears to always be set to 1 when changing the corresponding channel's setting. It therefore seems that bit 7 is a write-enable bit for the channel's setting. Bit 6 of a byte controls the phase inversion setting. When set (ie: equal to 1) phase inversion is active. When bit 6 is zero phase inversion is not enagaged. Bits 0-5 set the gain trim value; 0 sets a gain of 0dB (the default). The maximum gain available is determined by the type of channel. For analog channels 1-2 (the mic inputs), the maximum effective setting is 0x18 (24). For analog channels 3-8 the maximum is 0x12 (18) while for SPDIF channels the maximum is 0x0c (12). The value written is in dB - thus each gain trim control operates in steps of 1 dB. Setting a channel's byte to zero causes its settings to remain unchanged. This allows any combination of the 4 channels to be changed without interference to those not being changed. 0x0c74 - gain trim / phase invert (Ultralite only, analog channels 5-8) This register operates as described for 0x0c70 except it controls analog channes 5 to 8. The least significant byte corresponds to analog channel 5. 0x0c78 - gain trim / phase invert (Ultralite only, SPDIF 1 and 2) This register operates as described for 0x0c70 except it controls SPDIF 1 and SPDIF 2. The least significant byte corresponds to SPDIF 1. Bytes 2 and 3 are currently unused and should always be set to zero when writing to this register. 0x4y00-0x4y4c (y=0-3) - gain/pan/solo/mute registers These registers control the fader, pan, mute and solo settings for a given input channel in each of the 4 mix buses. y=0 is mix1, y=1 is mix2, y=2 is mix3 and y=3 is mix4. Bit 31: enable setting of pan Bit 30: enable setting of gain Bit 29: enable setting of bit 21 Bit 28: enable setting of bit 20 Bit 27: enable setting of pairing Bit 26: enable setting of bit 18 Bit 25: enable setting of solo Bit 24: enable setting of mute Bit 23: seems to be unsettable; set to zero Bit 22: seems to be unsettable; set to zero Bit 21: can be set to 1 but no effect apparent Bit 20: can be set to 1 but no effect apparent Bit 19: activate pairing Bit 18: can be set to 1 but no effect apparent Bit 17: channel solo. 0=solo off, 1=solo on. Bit 16: channel mute. 0=unmuted, 1=muted. Bits 15-8: pan. 0x40=0 (centre), 0x80=+64 (right), 0x00=-64 (left) Bits 7-0: gain. 0x00 = -inf, 0x80 = 0 dB. When reading these registers in normal operation, bits 31-27 and bits 23-18 seem to be all zero while bits 26-24 are all 1. The Cuemix console however appears to set bits 31-30 and zero bits 29-18. In other words, a typical read-back value might be 0x0700406a while to actually set the mixer to this one would need to use a value of 0xc300406a. It seems likely that at least some of the bits have different meanings when read and written; for instance there doesn't seem to be much point in bits 24-26 being set on readback given the above definitions, but they are. If channel pairing is to be activated it must be set in both channels which form a stereo pair. However, paired channels are still controlled individually by the mixer registers - for example, if channels 1 and 2 are paired it is still possible to raise channel 1's volume in mix1 by writing only to register 0x4000. In other words, the device does not enforce linked controls when channels are paired. The "paired" flag is really just a hint to the mixer at the end of the day. Gain and pan value-dB mappings for the Traveler =============================================== The following gain map runs from 0x00 (-inf) to 0x80 (0 dB). The dB values were taken directly off the Cuemix console application. Gain steps (dB): -inf, -84, -72, -65, -60, -56, -53, -50, -48, -46, -44, -43, -41, -39.7, -38.4, -37.2, -36.1, -35.1, -34.1, -33.1, -32.2, -31.4, -30.6, -29.8, -29.1, -28.4, -27.7, -27.0, -26.4, -25.8, -25.2, -24.6, -24.1, -23.5, -23.0, -22.5, -22.0, -21.5, -21.1, -20.6, -20.2, -19.8, -19.4, -19.0, -18.6, -18.2, -17.8, -17.4, -17.0, -16.7, -16.3, -16.0, -15.6, -15.3, -15.0, -14.7, -14.4, -14.1, -13.8, -13.5, -13.2, -12.9, -12.6, -12.3, -12.0, -11.8, -11.5, -11.2, -11.0, -10.7, -10.5, -10.2, -10.0, -9.8, -9.6, -9.3, -9.1, -8.8, -8.6, -8.4, -8.2, -7.9, -7.7, -7.5, -7.3, -7.1, -6.9, -6.7, -6.5, -6.3, -6.1, -5.9, -5.7, -5.5, -5.4, -5.2, -5.0, -4.8, -4.6, -4.5, -4.3, -4.1, -3.9, -3.7, -3.6, -3.4, -3.3, -3.1, -3.0, -2.8, -2,6, -2.5, -2.3, -2.2, -2.0, -1.9, -1.7, -1.6, -1.4, -1.3, -1.1, -1.0, -0.8, -0.7, -0.6, -0.4, -0.3, -0.1, 0.0 Pan: (approximate findings) full pan on: +3 dB gain 0: 0 dB 20/64 pan off: -3 dB 40/64 pan off: -6 dB 55/64 pan off: -15 dB 59/64 pan off: -24 dB full pan off: -inf gain Pan dB map. This table runs from value 0 (pan full left) to 0x80 (pan full right). The gain values are for a left channel signal. 2.505, 2.505, 2.505, 2.499, 2.499, 2.492, 2.486, 2.479, 2.473, 2.460, 2.453, 2.440, 2.427, 2.414, 2.400, 2.387, 2.367, 2.347, 2.331, 2.308, 2.288, 2.267, 2.241, 2.220, 2.194, 2.166, 2.139, 2.105, 2.078, 2.044, 2.016, 1.982, 1.940, 1.905, 1.870, 1.828, 1.786, 1.743, 1.700, 1.657, 1.607, 1.563, 1.512, 1.461, 1.409, 1.358, 1.298, 1.245, 1.185, 1.124, 1.063, 1.001, 0.931, 0.868, 0.797, 0.725, 0.653, 0.580, 0.507, 0.424, 0.341, 0.265, 0.257, 0.087, 0.000, -0.087, -0.176, -0.274, -0.373, -0.473, -0.584, -0.687, -0.801, -0.916, -1.033, -1.151, -1.271, -1.403, -1.526, -1.662, -1.800, -1.940, -2.083, -2.239, -2.386, -2.548, -2.713, -2.881, -3.058, -3.240, -3.437, -3.614, -3.814, -4.018, -4.228, -4.457, -4.678, -4.920, -5.168, -5.489, -5.688, -5.969, -6.259, -6.568, -6.889, -7.222, -7.568, -7.928, -8.327, -8.722, -9.160, -9.621, -10.108, -10.624, -11.205, -11.810, -12.460, -13.182, -13.971, -14.886, -15.855, -16.976, -18.264, -19.819, -21.715, -24.143, -27.526, -33.143, -inf dB factors for values of 0, 1 and 2 are all 2.505 dB while those for pan values of 3 and 4 are both 0.499. This is because the voltage variation across each of these groups of values was below the resolution of the meter used to measure the voltages (0.001 Vrms). One can probably safely assume that the actual variation across these groups is approximately linear, so "corrected" dB values for the first 5 pan values can probably be taken to be 2.508, 2.505, 2.504, 2.501, 2.499 The pan map was measured by injecting a 0.992 Vrms sine wave at 308 Hz into analog1 input channel of the Motu. Channel 1's -20 dB pad was switched in to avoid overloading the preamp and a gain trim of +11 dB was used. This combination resulted in a signal of 0.999 Vrms being emitted from analog1 output with the analog1's fader set to 0 dB. The output RMS voltage was then measured for each setting of the pan control. Resolution of these measurements was 0.001 Vrms. The dB gain value was calculated relative to the centre pan position using dB = 20log10(V/ref) where ref was the voltage measured with the pan control set to the centre (ie: a pan value of 0x40). A frequency of approximately 300 Hz was chosen since this was firmly within the flat response region of the multimeter used for measuring the voltage. If the frequency drifted slightly the frequency response of the meter would therefore not artificially change the voltage measurement. The input signal was generated using a precision audio signal generator. The amplitude is tightly regulated and did not change for the duration of these tests. The meter used was not able to measure voltages below 0.004 Vrms. This was a limitation of the meter - it was not an additive offset which affected voltage measurements. Putting all this together it is expected that the dB values for the pan map are accurate to at least 1 decimal place. libffado-2.4.5/doc/rme_notes/0000755000175000001440000000000014206145612015421 5ustar jwoitheuserslibffado-2.4.5/doc/rme_notes/rme_config_register_map.txt0000644000175000001440000011165514206145246023047 0ustar jwoitheusersRME Fireface-400 / Fireface-800 register map ============================================ Version: 0.26 Author: Jonathan Woithe Date: 11 April 2013 Definitions ----------- CBA = Command Buffer Address FF800 = Fireface-800 FF400 = Fireface-400 Multi-byte values sent to/from the Fireface in async packets are generally little endian - that is, the device interprets quadlets in asynchronous packets as little endian even though the bus definition is big-endian. If writing a driver for use on a little endian machine, this means that a lack of byte swapping (to account for the bus endianness standard) will cause bit 0 on the host to be bit 0 on the device. By default, FFADO however adheres to the bus standards and byteswaps on little endian machines. Under this regime, bit 0 on the host will in fact be read by the device as the least significant bit in the most significant byte. In order to retain consistency with device documentation, the FFADO RME driver currently sends async packet data in the little endian format which the RME device expects. Although this is technically wrong (in so far as the FireWire standard is concerned), at this stage it is considered that introducing an endianness difference between the FFADO driver and the documentation is likely to result in maintenance issues down the track. The bit maps in this document regarding the configuration registers are written from the device's point of view. That is, the values quoted should be sent to the device in little endian format unless otherwise stated. Curiously enough, preliminary investigations suggest that audio data appears to be sent on the bus in big endian format (although this is to be confirmed). The FF800 includes a number of instrument options for input 1 which are described using several different terms interchangeably: - "Drive" (also referred to as "fuzz") activates 25 dB extra gain - "Speaker emulation" (also referred to as "filter") removes LF noise and some HF - "Limiter" activates a soft-limiter with a threshold of -10 dBFS. This can only be switched off if the Front input is used for channel 1. Device address space location ----------------------------- While some register addresses are common between the two interfaces, the absolute addresses of the settings and control differ. These are defined relative to the device "command buffer" address: FF800: command buffer address (CBA) = 0xfc88f000 FF400: command buffer address (CBA) = 0x80100500 The location of the configuration (settings) registers relative to the command buffer is consistent across devices: conf reg 1 address = command buffer address + 5*4 For a FF800 this equates to 0xfc88f014. Controlling sample rate (DDS) ----------------------------- Sample rate is controlled by writing the desired sample rate in Hz to the sample rate control register located at offset 0 from the command buffer address (0xfc88f000 on a FF800). The hardware DDS allows a wide range of frequencies to be requested (possibly anything from 30 kHz up to 210 kHz). The more common rates are of course 32k, 44.1k, 48k, the pull-up/down rates (44.056k, 44.144k, 45.937k, 46.080k, 47.952k, 48.048k) and the corresponding 2x and 4x rates. Software connecting to the Fireface device is restricted to the normal rates of 32k, 44.1k, 48k and the related 2x and 4x rates. If the device is in master clock mode and the user has not made an explicit DDS setting, the hardware DDS will be determined by the sampling rate requested by the application opening the device. If a DDS frequency has been requested by the user the actual rate used by the device will be that DDS frequency regardless of what the application has asked for. In this case a device open will only succeed if the software has requested a speed whose multiplier matches the DDS configuration. If the device is locked to an external clock, a device open will succeed only if the multiplier of the requested sampling rate matches that of the external rate. The device status registers allow the PC to determine the sampling rate when an external clock is in use. However, there is no way to read the sampling rate when in master clock mode. It is therefore necessary to cache this in the driver so it can be provided when requested. In terms of multipliers the RME treats sample rates greater than 112000 Hz as 4x rates, with rates greater than 56000 Hz as 2x rates. Rates less than 30000 Hz and greater than 210000 Hz are invalid. Configuration registers 1, 2 and 3 ---------------------------------- Most RME device configuration is done using configuration registers 1, 2 and 3. For the ff800 these are: config1 = configuration register 1 (FF800: 0xfc88f014, FF400: 0x80100514) config2 = configuration register 2 (FF800: 0xfc88f018, FF400: 0x80100518) config3 = configuration register 3 (FF800: 0xfc88f01c, FF400: 0x8010051c) In essence the configuration registers start at CBA+5*4 for both interfaces. When making a configuration change these registers are always written in a block of 12 bytes starting at 0xfc88f014 with a block write operation. Configuration register 1 (FF800: 0xfc88f014, FF400: 0x80100514): bits 31-18: unknown, set to 0 bits 17-16: Phones level: 00 = +4 dBu 01 = -10 dBV 10 = hi-gain bits 15-13: unknown, set to 0 bits 12-10: Output level control (part 1 of 2: FPGA LED drive): 001 = hi-gain 010 = +4dBU 100 = -10dBV bit 9: FF800: Instr option: Drive (part 1 of 2: FPGA LED drive) (active = 1) FF400: Channel 3 "instrument" switch bit 8: FF800: Phantom power, mic 10 (active = 1) FF400: Channel 3 "pad" switch bit 7: Phantom power, mic 8 (active = 1) bit 6: unknown, set to 0 bits 5-3: Input level control (part 1 of 2: FPGA LED drive): 001 = lo-gain 010 = +4dBU 100 = -10dbV bit 2: FF800: Instrument option: speaker emulation (aka "filter") (part 1 of 2: FPGA LED drive) (active = 1) FF400: Channel 4 "instrument" switch bit 1: FF800: Phantom power, mic 9 (active = 1) FF400: Channel 4 "pad" switch bit 0: Phantom power, mic 7 (active = 1) Configuration register 2 (FF800: 0xfc88f018, FF400: 0x80100518): bits 31-12: unknown, set to 0 bit 11: Input #1 front switch (active = 1) bit 10: unknown, set to 0 bit 9: Instrument option: Drive (part 2 of 2: CPLD function) (active = 0) bit 8: Input #8 rear switch (active = 1) bit 7: Input #8 front switch (active = 1) bit 6: Input #7 rear switch (active = 1) bit 5: Input #7 front switch (active = 1) bits 4-3: Output level control (part 2 of 2: CPLD function): 00 = undefined 01 = -10dBV 10 = hi-gain 11 = +4dBU bit 2: Input #1 rear switch (active = 1) bits 1-0: Input level control (part 2 of 2: CPLD function): 00 = lo-gain 01 = undefined 10 = +4dBU 11 = -10dbV Configuration register 3 (FF800: 0xfc88f01c, FF400: 0x8010051c): bit 31: "Drop and stop": always set to 1 bit 30: Unit option: TMS (active = 1) bits 29-27: set to 0 bit 26: set to 1 for FF400, 0 for FF800 bits 25-17: set to 0 bit 16: P12DB_AN0 (normally set to 0) bit 15: set to 0 bit 14: Toggle TCO (normally set to 0) bit 13: Word clock single speed: 0 = off, 1 = on bits 12-10: Sync reference source: 000 = ADAT1 001 = ADAT2 011 = SPDIF 100 = Word clock 101 = TCO bit 9: SPDIF input source: 0 = coax, 1 = ADAT2 port bit 8: SPDIF output option: ADAT2 bit 7: SPDIF output option: non-audio bit 6: SPDIF output option: emphasis bit 5: SPDIF output option: professional bit 4: QS control (set to 1) bit 3: DS control (set to 1) bit 2: Freq1 control (set to 1) bit 1: Freq0 control (set to 1) bit 0: Clock mode: 0 = Master, 1 = Autosync On the FF400, writing to these registers with valid values for the first time after power up has the side effect of extingishing the "Host" LED. Device status registers ----------------------- There are up to 4 read-only device status registers available, starting at address 0x801c0000. There seems to be a slight difference in the mapping of status register 0 depending on the size of the read. If only 2 registers (quadlets) are read the "general" layout is assumed. If on the other hand 4 registers are used (used when determining the status of the device's streaming system) the layout of register 0 is slightly different. Status register 0: bits 9-0: on a 2-quadlet read these bits are all zero bits 9-0: on a 4-quadlet read when in autosync mode, these bits contain SR/250, where SR is the sample rate to be passed to the streaming subsystem when starting streaming. bit 10: ADAT1 lock achieved bit 11: ADAT2 lock achieved bit 12: Device is synced to ADAT1 bit 13: Device is synced to ADAT2 bits 17-14: SPDIF frequency: 0000 = undefined 0101 = 88.2k 0001 = 32k 0110 = 96k 0010 = 44.1k 0111 = 128k 0011 = 48k 1000 = 176.4k 0100 = 64k 1001 = 192k bit 18: Device is synced to SPDIF bit 19: Over detected bit 20: SPDIF lock achieved bit 21: undefined (read as zero) bits 24-22: Primary sync source: 000 = ADAT1 100 = Word clock 001 = ADAT2 101 = TCO 011 = SPDIF bits 28-25: autosync (external) frequency (defined as for SPDIF frequency) bit 29: Device is synced to word clock bit 30: Word clock lock achieved bit 31: undefined (read as zero) Status register 1: bit 0: master clock mode active bits 21-1: undefined bit 22: Device is synced to TCO bit 23: TCO lock achieved bits 31-24: undefined Status register 2: bits 31-0: (FF800 only) FireWire iso channel used for data from FF800 to PC Status register 3: bits 31-0: unused Interfacing to device flash --------------------------- To preserve the device's settings across power cycles the settings are stored in a flash memory on the device. This is read during driver initialisation to ensure the driver's status agrees with that of the device. There are several classes of things stored in flash: operational settings, volumes (ie: the mixer status) and configuration/firmware. Device settings start at address 0x3000f0000 on the FF800 and 0x00060000 on the FF400; mixer data starts at 0x3000e0000 on the FF800 and 0x00070000 on the FF400. Reading blocks from the flash (flash command 0x2) For the FF800 the entire buffer is read directly from flash as a single block if the block is less than a sector (256 bytes, 64 quadlets). Polling for "device not busy" should commence after a wait of 5 ms. For anything larger, writes are split into sector-sized sub-blocks. For the FF400, the buffer is read in 32-quadlet sub-blocks. A partial block is read at the end if the total buffer size is not a multiple of 32-quadlets. To read a sub-block, the address is placed in register 0x80100288 and the sub-block size (in bytes) in 0x8010028c. A 0x02 is then written to CBA+(8*4) to initiate the read. Polling for "device not busy" should commence after a wait of 2 ms. Once not busy the data is available for reading from 0x80100290. Writing blocks to the flash (flash command 1) For the FF800, the buffer is written to flash in 256-byte (64 quadlet) sectors. Polling for "device not busy" should commence after a wait of 5 ms. A write request for a length less than a sector will be honoured by the device (this is done when writing device settings). For the FF400, the buffer is written in 32-quadlet (128-byte) sub-blocks via a bounce buffer. If the final sub-block is not 32-quadlets the write is only as big as the sub-block (that is, no padding takes place). The sub-block data to be written is sent to the block starting at 0x80100290. The 2-quadlet register at 0x80100288 is set with the flash address to write the block to and the size (in bytes) of the data block. Finally, a 0x1 is written to CBA+(8*4) to initiate the write. Polling for "device not busy" should commence after a wait of 2 ms. Getting other data from flash There are a few other commands issued to the flash memory system for obtaining data about the connected interface: * Device revision On the FF800 this is read directly from register 0x200000100. On the FF400, 0xf is written to CBA+(8*4). Poll for "not busy" after a wait of 2ms. Once not busy the revision is read from register 0x80100290. Erasing the flash The flash is divided into sections and it is possible to erase each section separately. Therefore one only has to erase section of interest when changing something. On the FF400, erasure is controlled by writing a special magic number to the the flash control register (CBA+8*4): Erase volume: write 0xe Erase settings: write 0xd Erase configuration (firmware): write 0xc On the FF800, erasing is controlled by writing 0 to the applicable register: Erase volume: register is 0x3fffffff4 Erase settings: register is 0x3fffffff0 Erase firmware: register is 0x3fffffff8 Erase configuration: register is 0x3fffffffc It's not clear what the distinction between "configuration" and "firmware" is. The FF400 appears to only support "configuration" but treats this as "firmware". The FF800 supports both as distinct options. After issuing the erase command one should wait for 500 ms before polling the device for the "not busy" status. Waiting for flash When interacting with the device's flash memory one must wait for the completion of an operation before attempting another. The location of the "device busy" flag differs between the FF400 and FF800. On the FF800 is part of the quadlet register at 0x801c0004 (part of the read-only status register block beginning at 0x801c0000). The device is ready to accept another command when bit 30 is set. On the FF400 the wait state is found by reading a quadlet from CBA+8*4. If this quadlet is zero the FF400 is ready to accept another command. Most device flash operations have a minimum time to complete. There's no point in polling the device busy flag until at least this much time has elapsed. Device settings format ---------------------- The device settings are stored in flash as an array of 59 32-bit unsigned integers. These are: 0 - Device ID (FF400=0x77e1f4ea) 1 - Device revision (FF400=0x004af3d8) 2 - ASIO latency (FF400=0x00000001) 3 - Samples per frame (FF400 default is 0x30) 4 SPDIF input mode (1=coax, 0=optical) 5 SPDIF output emphasis active 6 SPDIF output is "professional" (ie: AES/EBU) 7 Clock mode (0=master, 1=autosync) 8 SPDIF output is non-audio (eg: AC3 passthrough) 9 Sync reference 10 SPDIF output mode (0=coax only, 1=coax+optical) 11 - Check input 12 - Status (FF400 idle=0x77e691d0) 13 - Register[4] (FF400 = 0x004adbc8,0x001377c0,0x000301ee,0x00000001) 17 - Iso receive channel (FF400=0x7ffde000) 18 - Iso transmit channel (FF400=0x77f43664) 19 - Timecode (FF400 example: 0x004b35c8) 20 - Device type (FF400=0x00000001) 21 - Number of devices (FF400=0x77f43664) 22 TMS (FF400=0x00000000) 23 - Speed (FF400=0x00000000) 24 - Channels available (high) (FF400=0x0012f2e4) 25 - Channels available (low) (FF400=0x00000000) 26 Limit bandwidth setting (0=all channels on, 1=no adat2, 2=analog+spdif only, 3=analog only) 27 - Bandwidth allocated (FF400=0x00000000) 28 Stop on dropout (FF400=0x00000000) 29 Input level (0=lo-gain, 2=+4dBU, 1=-10dBV) 30 Output level (2=hi-gain, 1=+4dBU, 0=-10dBV) 31 Mic level [0] - FF400: Phoneslevel-1 FF800: Channel 7 front/rear select (0=rear, 1=front, 2=front+rear) 32 Mic level [1] - FF400: unused FF800: Channel 8 front/rear select (0=rear, 1=front, 2=front+rear) 33 Mic phantom power [4] 37 Instrument - FF400: unused FF800: Channel 1 front/rear selector (0=rear, 1=front, 2=front+rear) 38 Filter (aka speaker emulation) 39 Fuzz (aka drive) 40 - Sync align 41 - Device index (FF400=0x77e24d0d) 42 - Advanced dialog (FF400=0x000201f8) [but might be related to TCO control) 43 Sample rate (eg: 0x0000ac44) [set to 0x00000000 unless DDS enabled] 44 - Interleaved (FF400=0x00000000) 45 - Sn (FF400=0x77e14925) 46 Word clock single speed (1=single speed) 47 - Number of channels (FF400=0x000000f0) 48 - Dropped samples 49 p12db_an[0] - Disable limiter, settable only if channel 1 front jack active 50 - p12db_an[1-9] "-" = elements not used (under MacOSX at least) Total size: 59 quadlets The default state of these quadlets is 0xffffffff, which is taken to indicate that the respective setting has not been written to flash. This in turn causes the driver to assume its own default value. While these settings can be changed in real time by writing to the relevant control registers, these are not persistent across device power cycles. To make them persistent it is necessary to store them into the flash. Flash mixer settings layout --------------------------- Mixer (volume) data starts at 0x3000e0000 on the FF800 and 0x00070000 on the FF400. There are several control groups in the mixer: 0xe0000 (FF800): "mixer shadow", FF800 only 0xe2000 (FF800) / 0x70000 (FF400), 0x0800 bytes: 16-bit volume array 0xe2800 (FF800) / 0x70800 (FF400), 0x0800 bytes: 16-bit pan array 0xe3000 (FF800) / 0x71000 (FF400), 0x0040 bytes: 16-bit "vol3" array + "enable MIDI control" + "submix" + zero padding to 64 quadlets All group allocations are written in their entirety (that is, 0x0800 bytes), with zero padding on the end as needed. Each write is grouped in sectors, with each sector being 256 bytes (64 quadlets). It is not known why the "mixer shadow" is stored in the case of the FF800. It comprises a copy of the 32-bit matrix mixer settings (see "Fireface-800 mixer controls" below), and the information is essentially a duplicate of what's in the "volume" and "pan" arrays. In any case, what's done is done. The "mixer shadow" values are written in 64-quadlet (256-byte) blocks, one per hardware output channel. The FF800 has 28 hardware input channels (I) and 28 software playback channels (P). Each output has a 64-quadlet block formatted as follows: Faders for physical inputs 1..I, zero pad to 32-quadlet boundary Faders for software playbacks 1..P, zero pad to 32-quadlet boundary There are 28 hardware input/output channels on the FF800 and 18 for the FF400. The "volume" and "pan" arrays are arranged in blocks of N (32 for FF800, 18 for FF400) 16-bit elements. Each block contains the volume/pan value for each of N possible physical inputs or playbacks when sent to one of the N/2 physical stereo output pairs. Elements are ordered in the standard Fireface channel index order. The arrangement of the blocks are as follows: Inputs 1..N to output pair 1+2 Playbacks 1..N to output pair 1+2 Inputs 1..N to output pair 3+4 Playbacks 1..N to output pair 3+4 : Inputs 1..N to output pair N/2-1 and N/2 Playbacks 1..N to output pair N/2-1 and N/2 In the case of the FF800, N (32) is greater than the number of physical inputs and mono outputs available (28). Array elements corresponding to non-existent inputs, outputs or playbacks are filled with zeros. The "vol3" array represents the hardware output volume settings. The 16-bit volume data for each of the hardware output channels is included in the standard Fireface channel index order. The array has room for 30 outputs while the FF400/FF800 has only 18/28 outputs; elements corresponding to outputs not physically present are set to zero. In addition, a boolean indicating whether MIDI control is enabled is stored in zero-based array index 30 while a submix index is stored in array index 31 (array elements are considered 16-bit here). The meaning of the submix index isn't known; it's thought that the GUI mixer applications in other systems use this as a convenient place to store the submix index that the user was last editting (assuming they were editting in submix mode). All 16-bit values are written in little endian byte order. The "volume" and "vol3" values are unsigned 16-bit values: 0x0000 = -inf (minimum) 0x0255 = -6 dB 0x0323 = 0 dB 0x03ff = 6 dB (maximum) When panned hard left or right, the value F written to the flash as a channel's volume given a fader value of V (0..0x10000) appears to be: F = (1023.0/3) * ln( V*(exp(3)-1)/65536 + 1) F is rounded to the nearest integer value. The "pan" values are unsigned 16-bit values. 0x0000 is hard left, 0x0080 is centre and 0x0100 is hard right. Therefore if the pan value is represented as a floating point number Pf from 0.0 (hard left) to 1.0 (hard right), the pan value Pv written to flash will be Pv = 0x0100 * Pf In the hardware, each channel of a stereo pair is controlled independently the mixer registers. It is therefore necessary to convert bidirectionally between fader value pairs and the volume/pan pair as used in the flash. Let V0 and V1 be the fader value (0..0x10000) for each of the two channels. The volume (V) and pan (P) values can be calculated as follows. V = V0 + V1 Pf = V1 / (V0 + V1) P = 0x0100 * Pf V is then transformed into the equivalent flash value F according to the expression given previously: F = (1023.0/3) * ln( V*(exp(3)-1)/65536 + 1) When starting with the volume/pan pair, V is first calculated from F by rearranging the above equation. Pf is then calculated: Pf = P / 0x100 This allows V0 and V1 to be found: V0 = V * (1 - Pf) V1 = V * Pf Careful attention to round-off is required to ensure that flash and fader values remain unchanged through fader-flash-fader and flash-fader-flash round trips. User interfaces under other operating systems include a "pan law" control which sets the gain when panned to centre. This setting is not sent to the device at any time; the default in the mixer software is -6 dB. Other options are -4.5 dB, -3 dB and 0 dB. Changing these affects the values sent to the individual mixer registers (and hense the "mixer shadow"), but not the values stored in the "volume" and "pan" flash arrays. In the case of the FF400, the power on state obtained from flash is therefore independent of the pan law control (the FF800 stores the mixer shadow data and could make use of it if it wanted to). Experimentation shows that when powering up, the FF400 assumes a pan law of -6 dB when mapping from the volume/pan flash arrays to individual mixer element registers. Tests are still to be done on the FF800 to see if it uses the "mixer shadow" values instead of the volume/pan arrays. TCO (TimeCode Option) --------------------- The TCO is an optional card for the FF800 which adds video timecode generation and clock locking capabilities to the FF800. It is controlled by writing a block of 4 quadlets to register 0x810f0020 while its status can be retrieved by reading a block of 4 quadlets from register 0x801f0000. The configuration space is as follows. Quadlet 0 (written to register 0x810f0020): bit 31: MTC active if set to 1 bits 30-0: reserved (equal zero) Quadlet 1 (written to register 0x810f0024): bits 31-12: reserved (equal to zero) bits 11-10: LTC format (00=24fps, 01=25fps, 10=29.97fps, 11=30fps) bit 9: dropframe active bit 8: set timecode request bit 7: reserved (set to 0) bit 6: PAL format video input bit 5: NTSC format video input bit 4-3: reserved (set to 0) bits 2-1: word clock input rate (00=1x, 01=2x, 10=4x) bit 0: reserved (set to 0) Quadlet 2 (written to register 0x810f0028): bit 31: set sampling frequency from application bits 30-29: input select (00=wordclock, 01=video, 10=LTC) bit 28: input termination active bit 27: Base frequency (0=44.1 kHz, 1=48 kHz) bit 26: Pull up flag bit 25: Pull down flag bit 24: Pull up/down amount (0=0.1%, 1=4.0%) bit 23: reserved (set to 0) bit 22: Flywheel select bit 21: Jam sync select bits 20-19: dropframes select (unused, set to 0) bits 18-17: word clock conversion (00=1:1, 01=44.1->48, 10=48->44.1) bit 16: set TC run bits 15-0: reserved, set to 0. Quadlet 3: bits 31-0: reserved, set to 0 The 4 quadlets returned by a TCO status query are mapped as follows. Quadlet 0: bit 31: set to 1 bits 30-24: LTC, hours field in BCD(*) bit 23: set to 1 bits 22-16: LTC, minutes field in BCD bit 15: set to 1 bits 14-8: LTC, seconds field in BCD bit 7: set to 1 bits 6-0: LTC, frames field in BCD Quadlet 1: bit 31: set to 1 bits 30-24: reserved (equal to zero) bit 23: set to 1 bits 22-16: reserved (equal to zero) bit 15: set to 1 bits 14-12: reserved (equal to zero) bits 11-10: LTC format (00=24fps, 01=25fps, 10=29.97fps, 11=30fps) bit 9: dropframe active bit 8: reserved (read as zeros) bit 7: set to 1 bit 6: PAL format video input bit 5: NTSC format video input bit 4: Word clock input valid (0=invalid, 1=valid) bit 3: LTC input valid (0=invalid, 1=valid) bits 2-1: reserved (read as zeros) bit 0: TCO lock flag (0=no lock, 1=locked) Quadlet 2 bit 31: set to 1 bits 30-24: reserved (equal to zero) bit 23: set to 1 bits 22-16: reserved (equal to zero) bit 15: set to 1 bits 14-8: upper 7 bits of PLL phase bit 7: set to 1 bits 6-0: the lower 7 bits of the PLL phase Quadlet 3: bit 31: set to 1 bits 30-16: reserved bit 15: set to 1 bits 14-0: set to 0 Notes: (*) BCD is Binary Coded Decimal. The high nibble (which is only 3 bits in these cases) contains the "tens" digit while the lower nibble contains the "units" digit. The calculation of the PLL phase from quadlet 2 (q2) is as follows: phase = (q2 & 0x7f) + ((q2 & 0x7f00) >> 1) which then allows the incoming frequency to be calculated using freq = (25000000 * 16) / phase To detect the presence of a TCO in a FF800, read the 4 TCO status quadlets. If a TCO is present: - bits 31, 23, 15 and 7 in quadlets 0, 1 and 2 will be 1, AND - bits 31 and 15 in quadlet 3 will be 1, AND - bits 14 to 0 in quadlet 3 will be 0 Streaming control registers --------------------------- There appears to be a number of registers involved in the setup of device streaming. Device (streaming) initialisation register (FF800: 0x20000001c, FF400: CBA) This register comprises the 3 quadlets starting at address 0x20000001c on the FF800 and the 5 quadlets starting at the CBA on the FF400. The first quadlet contains the sample rate in Hz. The second quadlet is mapped as follows: bits 31-11 = number of audio channels bits 10-0 = iso tx channel (PC to interface) In all local tests with a FF800 the value of this quadlet was always equal to 0x0000e000 (28 channels, PC transmits on iso channel 0). The third quadlet is mapped as follows. bits 10-0 = number of audio channels bit 11 = speed flag; set to 1 if FireWire bus is at 800 Mbps In local tests with a FF800 the value of this register was always 0x0000001c (28 channels, 400 Mbps FireWire bus). The forth and fifth quadlets (used only by the FF400) are zero. After this register is configured, 4 quadlets are read starting from 0x801c0000. When read, these are the device status registers. Device (streaming) start register (FF800: 0x200000028, FF400: CBA+0x0c): The start of streaming differs between the FF400 and FF800 in more than just the address of the relevant register. On the FF800 this register is mapped as follows: bits 10-0 = number of audio channels bit 11 = bus speed flag; set to 1 if FireWire bus is at 800 Mbps On a FF400 the register is as follows: bits 4-0 = number of audio channels bits 9-5 = iso tx channel (PC to interface) During initial testing with a FF800 the value of this register was always 0x0000001c (28 audio channels, PC tx on iso channel 0). Channel mute setup register (write to 0x801c0000): After writing to the streaming start register, 0x70 bytes (28 quadlets) are written starting at 0x801c0000. Each quadlet represents one channel on the Fireface800. A value of 1 mutes the respective channel - indeed on closing down streaming each quadlet is set to 1. During startup some values are set to zero - the ones set to zero may be determined by the channels which have active software data sources although this is yet to be confirmed with more testing. Irrespective of the setting of these registers it appears that data for all channels is always sent to/from the Fireface-800. Note that when register 0x801c0000 is read it functions as the device status register. It is read during streaming setup, but obviously it bears no relationship to the channel mute status. Streaming end register (FF800: 0x200000034, FF400: CBA+0x4): On the FF800, streaming is stopped by writing 3 zeroed quadlets to consecutive registers starting at address 0x200000034. For the FF400 one writes 3 zeroed quadlets to consecutive registers from CBA+0x4, followed by a 0x00000001 to CBA+0x10 (making a 4-quadlet write in total). Iso data -------- Audio/midi data is sent and received on isochronous channels previously configured by the driver. On a dedicated bus with nothing else present, the stream to the fireface is sent on iso channel 0 while data from the fireface is sent on iso channel 1. No CIP header is included in the iso data packet. Fireface data follows immediately after the standard 2-quadlet FireWire iso packet header. Each iso packet contains a number of samples across all 28 device channels (18 channels in the case of the ff400). For 1x rates, 7 samples per channel seem to be sent. Thus the size of the data portion of a 1x iso packet is 28*4*7 = 784, with a total packet size being 784 + 8 = 792. The data is sent with one audio channel per quadlet. The audio data is a 24 bit integer stored in the most-significant 3 bytes of a quadlet. The LSB (low byte) of certain channels in the stream sent by the Fireface is used to send synchronising information: Low byte of channel 6 = current frame Low byte of channel 7 = phase Low byte of channel 1 = rx sample counter, low byte Low byte of channel 4 = rx sample counter, high byte Low byte of channel 0 = tx buffer size, low byte Low byte of channel 5 = tx buffer size, high byte Low byte of channel 2 & 3 = unknown (midi?) The low byte data from channels 0-7 is repeated in channels 8-15 and 16-23 respectively, with channels 24-27 containing the low byte data from channels 0-3. This repetition holds for the low bytes of channels 2-3 in all data seen so far, it might not necessarily be the case in general - it depends what the low byte data from channels 2 and 3 are used for. The rx sample counter appears to be used to detect missed samples. The current frame and phase from a received packet is used in conjunction with the stored values of these from the previous frame to track the phase of the audio clock. A "frame" consists of a fixed number of samples across all channels of the device. At 1x rates this appears to be 7, but it might not be fixed. Even though this is the same as the number of samples per channel per packet, a given packet can experience a change in the "current frame" part way through. In other words, the "current frame" is not necessarily constant for all samples in a packet. With the number of samples per channel contained in each iso packet it is not necessary for the RME to send audio data in every iso cycle. When it is deemed that a cycle can be skipped the RME simply doesn't send any packet at all in that cycle. This contrasts with other devices which tend to send empty "placeholder" packets when the need arises. This means that a cycle without a packet from the RME is not necessarily an error condition. To detect dropped packets one must instead rely on the rx sample counter embedded in the audio data stream. Input preamp / output amp gain control -------------------------------------- On the Fireface-400 the gain of the mic/instrument preamps and output amplifiers can be set. Mic channel gain is in steps of 1 dB from 10 dB up to 65 dB, with 0dB also available. Instrument input gain ranges from 0 dB to 18 dB in 0.5 dB steps. Output gains range from +6 dB down to -53 dB (a 58 dB range) in steps of 1 dB, with compete "mute" also available. The gains are set using the register at 0x801c0180. bits 31-24: unknown (set to 0) bits 23-16: channel being set (see below) bits 15-8: unknown (set to 0) bits 7-0: the gain value For mic channels the gain value is the dB value. For instrument channels, a value of 2G is written for a gain value of G (thereby allowing a stepsize of 0.5 dB). For output gain, 0 = +6 dB, 0x3b = -53 dB, 0x3f = mute. The definition of the "channel being set" is as follows. 0 = mic input 1 gain 1 = mic input 2 gain 2 = instrument input 3 gain 3 = instrument input 4 gain 4-9 = analog outputs 1-6 level 10-11 = phones output level 12-13 = SPDIF output level 14-21 = ADAT outputs 1-8 level Firefice-400 mixer controls --------------------------- The Fireface-400 matrix mixer is controlled using a block of registers starting at 0x80080000. An 18x18 matrix mixer is implemented allowing any hardware input to be sent to any device output. Pan control is effected by manipulating the "left/right" controls within an output pair. For each input channel block the order of channels is Analog 1-8, SPDIF 1-2, ADAT 1-8. 0x80080000 - 0x80080044: input channel sends to Analog 1 output. 0x80080048 - 0x8008008c: playback channel sends to Analog 1 output. 0x80080090 - 0x800800d4: input channel sends to Analog 2 output. 0x800800d8 - 0x8008011c: playback channel sends to Analog 2 output. : 0x80080990 - 0x800809d4: input channel sends to ADAT 8 output. 0x800809d8 - 0x80080a1c: playback channel sends to ADAT 8 output. 0x80080f80: matrix mixer analog 1 output fader 0x80080f84: matrix mixer analog 2 output fader : 0x80080fc4: matrix mixer ADAT 8 output fader Each fader control ranges from 0x00000000 (-inf) through 0x00008000 (0.0 dB) up to a maximum of 0x00010000 (+6.0 dB). -52.7 dB appears to correspond to a value of 0x0000004c, -46.6 dB is 0x00000099. From this we can see that if v is the value being written, the dB gain applied can be found using dB = 20.log10(v/32768) Alternatively, to set the gain to G dB, one calculates the value to send to the device (v) using v = 32768 * exp10(G/20) When setting the output fader controls, the associated output amplifier gain control (see previous section) are generally kept in sync. That is, if register 0x80080f80 (analog 1 output fader) is set to 0 dB, so is the analog output 1 level via register 0x801c0180. Fireface-800 mixer controls --------------------------- The matrix mixer on the Fireface-800 is controlled using a block of registers starting at 0x80080000. A 28x28 matrix mixer is implemented allowing any device input to be sent to any device output. The pan controls are synthesised by manipulating the "left/right" controls. In each sub-block, the order of channels is in fireface numeric order. That is, Analog 1-10, SPDIF, ADAT1 1-8, ADAT2 1-8. 0x80080000 - 0x8008006c: input channel sends to Analog 1 output. 0x80080080 - 0x800800ec: playback channel sends to Analog 1 output. 0x80080100 - 0x8008016c: input channel sends to Analog 2 output. 0x80080180 - 0x800801ec: playback channel sends to Analog 2 output. : 0x80081b00 - 0x80081b6c: input channel sends to ADAT2-8 output. 0x80081b80 - 0x80081bec: playback channel sends to ADAT2-8 output. 0x80081f80: matrix mixer analog 1 output fader 0x80081f84: matrix mixer analog 2 output fader : 0x80081fec: maxtrix mixer ADAT2-8 output fader Each fader control ranges from 0x00000000 (-inf) through 0x00008000 (0.0 dB) and up to a maximum setting of 0x00010000 (+6.0 dB). As for the Fireface-400, if v is the value being written, the dB gain applied can be found using dB = 20.log(v/32768) Mute is synthesised by setting the respective send value to -inf (0). Conversely, solo is synthesised by muting all sends to the selected bus except the send being soloed. Note that a different scale is used when writing mixer settings into flash. Refer to the "Flash mixer settings layout" section for further details. Metering values --------------- The Fireface-800 appears to provide hardware support for metering. The RME mixer application periodically sends block read requests for register 0x80100000 with a size of 0x3f8. What is returned is a set of two datablocks with data in little-endian (least significant bit/word first) format. The first block contains arrays of 64-bit floating point numbers representing channel amplitude with decay, presumedly useful for metering display. Arrays are: 28-element array for input channel amplitude with decay 28-element array for playback amplitudes with decay (educated guess) 28-element array for output amplitudes with decay The second data block contains signed 32 bit integers representing the input amplitudes without decay. Valid range is 0 - 0x7ffffff. Again there are 3 arrays: 28-element array for input channel ampltude 28-element array for playback amplitudes (educated guess) 28-element array for output amplitudes At the end of this second block are two zero quadlets. Their purpose is unknown at this stage. In each 28-element array the channel data appears in standard fireface order. Host LED -------- The "host" LED of the FF800 is controlled by a dedicated register at 0x200000324. Note that this register address goes beyond the 32-bit boundary. On the FF400 the host LED is controlled internally. On power up it is turned on. Once the host PC programs the configuration registers with valid values the host LED will automatically turn off. libffado-2.4.5/doc/rme_notes/rme_protocol_notes.txt0000644000175000001440000003241614206145246022107 0ustar jwoitheusersRME Fireface800 protocol notes ============================== Author: Jonathan Woithe Date: 24 March 2008 Introduction ------------ This document contains random notes made while observing the protocol used by the RME Fireface 800 interface. This document is not necessarily all that coherent but fully documents what is seen on the bus when various actions are carried out. For a distilled version of our current knowledge of the protocol please refer to rme_config_register_map.txt. The information contained here was observed from a Fireface 800 device: RME vendor ID: 0x000a35 GUID: 0x0093e1daf1 Node capabilities: 0x0083c0 Unit spec ID: 0x0a35 Sw version number: 0x0001 Model ID: 0x101800 Setting device configuration options in general ----------------------------------------------- It seems that generally device configuration is effected by writing 0x0c bytes to register 0xfc88f014. However, the existing drivers do much more than just this. For example, when setting DDS inactive (which doesn't appear significantly different to setting it active): Read 0x10 @ 0x801c0000: 01c000b1 a0001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000b0 a0001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000b0 a0001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read @ 0xfffff000040c: 0xa3500 Write 0xfc88f000: 0000ac44 Read 0x10 @ 0x801c0000: 01c000b1 80001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000b1 80001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000b0 80001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000b0 80001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000b1 80001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 When setting to 48k: Read 0x10 @ 0x801c0000: 01c000b0 a0001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000b0 a0001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000b0 a0001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000b0 a0001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000b1 a0001001 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read @ 0xfffff000040c: 0xa3500 Write 0xfc88f000: 0000bb80 Read 0x10 @ 0x801c0000: 01c000c0 a0001007 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000c0 a0001007 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000c0 a0001007 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000bf a0001007 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000c0 80001007 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 Read 0x10 @ 0x801c0000: 01c000c0 a0001007 ffffffff ffffffff Read @ 0xfffff0000410: 0x93e1daf1 When using the "course" control, the frequency seems to be set by: Read quadlet @ 0xfffff000040c: 0xa3500 Write @ 0xfc88f000 Each write to 0xfc88f000 seems to be preceeded by a read of 0xfffff000040c, resulting in a value of 0xa3500. Before and after this is a variable number of repeats of the sequence block read of 0x10 bytes (4 quadlets) at 0x801c0000 quadlet read at 0xfffff0000410 These reads seem to be some kind of status check on the device. However, there doesn't seem to be any consistent pattern as to when the write is performed relative to changes in these registers, nor does the process terminate in response to a particular setting of these registers. It seems that most if not all parameter settings are done with this pattern of reads above and below the actual write which (presumedly) activates the respective change. Looking for patterns, consider the settings of quadlets 0 and 1 in the last block from 0x801c0000 before the respective set operation finishes: 32k: 01c00080 80001003 44.056: 01c000b1 a0001001 44.144: 01c000b0 80001001 44.1: 01c000b1 80001001 45.937: 01c000b8 a0001001 46.080: 01c000b8 80001007 47.952: 01c000c0 a0001007 48: 01c000c0 a0001007 48.048: 01c000c0 a0001007 mult 2x-1x: t=866835 01c0017f 8000100f 876846 01c00180 8000100f 377579 01c00180 8000100f t=512691 write t=868285 01c000c0 80001007 878299 01c000c0 80001007 379027 01c000c0 80001007 mult 2x-4x: t=278863 01c00180 a000100f 769567 01c00180 a000100f 779580 01c00180 a000100f 280315 01c00180 a000100f t=536803 write t=771004 01c002ff a0001017 781034 01c00300 a0001017 281784 01c00300 a0001017 mult 4x-1x: t=784082 01c00300 a0001017 794099 01c00300 a0001017 294837 01c00300 a0001017 t=753719 write t=785530 01c000c0 a0001007 795553 01c000c0 a0001007 296297 01c000c0 a0001007 786993 01c000c0 a0001007 797001 01c000c0 a0001007 mult 4x-2x: 01c00300 80001017 01c00300 80001017 write 01c00180 8000100f 01c00180 a000100f 01c00180 a000100f 01c00180 a000100f DDS active: 01c000b1 a0001001 01c000b1 a0001001 01c000b0 a0001001 write 01c000b0 a0001001 01c000b0 80001001 01c000b1 a0001001 01c000b0 80001001 01c000b1 80001001 01c000b1 80001001 DDS inactive: 01c000b1 a0001001 01c000b0 a0001001 01c000b0 a0001001 write 01c000b1 80001001 01c000b1 80001001 01c000b0 80001001 01c000b0 80001001 01c000b1 80001001 Before and after settings: x - 32k: - 01c00080 80001003 32-44.1: 01c00080 80001003 - 01c000b1 80001001 44.1-48: 01c000b0 a0001001 - 01c000c0 a0001007 48-96: 01c000c0 a0001007 - 01c00180 8000100f 48-192: 01c000c0 a0001007 - 01c00300 80001017 96-192: 01c00180 a000100f - 01c00300 a0001017 96-48: 01c00180 8000100f - 01c000c0 80001007 192-48: 01c00300 a0001017 - 01c000c0 a0001007 192-96: 01c00300 80001017 - 01c00180 a000100f (jitter in bit 0 of 1st quadlet, and possibly bit 29 of 2nd quadlet) For now there's little we can do - we'll ignore these additional reads (or maybe just do a few token ones of our own) and see how far we get. It seems that some clock rate information is included in here, but not the complete picture. Perhaps the base rate is available here, but little else. In fact there doesn't appear to be anywhere which conveys the device's rate back to the PC; the PC seemingly sets the rate to its desired rate and then effectively caches the sample rate. Buffer sizes ------------ All settings: 0xfc88f014 = 00000810 0000035e c400101f It seems that device configuration is not affected by this setting. Obviously *some* setting is sent to the device whenever the buffer size is changed though. Clock mode ---------- Master: 0xfc88f014 = 00000810 0000035e c400101f Autosync: 0xfc88f014 = 00000810 0000035e c400101e DDS (Frequency setting) ----------------------- 32k: 0xfc88f000 = 00007d00 (32000) 44.056: 0xfc88f000 = 0000ac18 (44056) 44.144: 0xfc88f000 = 0000ac70 (44144) 44.1k: 0xfc88f000 = 0000ac44 (44100) 45.937: 0xfc88f000 = 0000b371 (45937) 46.080: 0xfc88f000 = 0000b400 (46080) 47.952: 0xfc88f000 = 0000bb50 (47952) 48k: 0xfc88f000 = 0000bb80 (48000) 48.048: 0xfc88f000 = 0000bbb0 (48048) Multiplier (base freq of 48k): 1-2: 0xfc88f000 = 00017700 (96000) 2-1: 0xfc88f000 = 0000bb80 (48000) 2-4: 0xfc88f000 = 0002ee00 (192000) 1-4: 0xfc88f000 = 0002ee00 (192000) 4-2: 0xfc88f000 = 00017700 (96000) 4-1: 0xfc88f000 = 0000bb80 (48000) Set DDS active: 0xfc88f000 = 0000ac44 Set DDS inactive: 0xfc88f000 = 0000ac44 To set the RME Fireface800 frequency, write the actual frequency to register 0xfc88f000. Setting DDS active doesn't appear to make any changes to the actual hardware. Input level ----------- +4dBU: 0xfc88f014 = 00000810 0000035e c400101f -10dBV: 0xfc88f014 = 00000820 0000035f c400101f lo-gain: 0xfc88f014 = 00000808 0000035c c400101f Inputs ------ #1 set front: 0xfc88f014 = 00000810 00000b5a c400101f #1 set front+rear: 0xfc88f014 = 00000810 00000b5e c400101f #1 set rear: 0xfc88f014 = 00000810 0000035e c400101f #7 set front: 0xfc88f014 = 00010810 0000033e c400101f #7 set front+rear: 0xfc88f014 = 00020810 0000037e c400101f #7 set rear: 0xfc88f014 = 00000810 0000035e c400101f #8 set front: 0xfc88f014 = 00000810 000002de c400101f #8 set front+rear: 0xfc88f014 = 00000810 000003de c400101f #8 set rear: 0xfc88f014 = 00000810 0000035e c400101f Instrument options ------------------ none-drive: 0xfc88f014 = 00000a10 0000015e c400101f none-lim: 0xfc88f014 = 00000810 0000035e c400101f none-sp_emu: 0xfc88f014 = 00000814 0000035e c400101f *-none: 0xfc88f014 = 00000810 0000035e c400101f "Lim" would appear to be a software setting since the hardware setting for "lim" seems to be the same as for "none". Output level ------------ +4dBU: 0xfc88f014 = 00000810 0000035e c400101f -10dBV: 0xfc88f014 = 00001010 0000034e c400101f hi-gain: 0xfc88f014 = 00000410 00000356 c400101f Phantom ------- mic 7 on: 0xfc88f014 = 00000811 0000035e c400101f mic 8 on: 0xfc88f014 = 00000890 0000035e c400101f mic 9 on: 0xfc88f014 = 00000812 0000035e c400101f mic 10 on: 0xfc88f014 = 00000910 0000035e c400101f SPDIF in -------- coax: 0xfc88f014 = 00000810 0000035e c400101f ADAT2: 0xfc88f014 = 00000810 0000035e c400121f SPDIF out --------- ADAT2: 0xfc88f014 = 00000810 0000035e c400111f emphasis: 0xfc88f014 = 00000810 0000035e c400105f non-audio: 0xfc88f014 = 00000810 0000035e c400109f professional: 0xfc88f014 = 00000810 0000035e c400103f Sync reference source --------------------- ADAT1: 0xfc88f014 = 00000810 0000035e c400001f ADAT2: 0xfc88f014 = 00000810 0000035e c400041f SPDIF: 0xfc88f014 = 00000810 0000035e c4000c1f TCO: 0xfc88f014 = 00000810 0000035e c400141f Wordclock: 0xfc88f014 = 00000810 0000035e c400101f Unit options ------------ Start with all options on. Turn each of separately: -checkinput: 0xfc88f014 = 00000810 0000035e c400101f -interleaved: 0xfc88f014 = 00000810 0000035e c400101f -syncalign: 0xfc88f014 = 00000810 0000035e c400101f -tms: 0xfc88f014 = 00000810 0000035e 8400101f This tends to indicate that TMS is the only "unit option" which affects the hardware. All the others would appear to be software features. Word clock ---------- Single speed on: 0xfc88f014 = 00000810 0000035e c400301f Single speed off: 0xfc88f014 = 00000810 0000035e c400101f Streaming start --------------- Frequency was 44100 Hz. Playback: Read from 0xfffff000040c: 0xa3500 Read from 0xfffff000040c: 0xa3500 Write 0x0c bytes to 0x20000001c: 0000ac44 0000e000 0000001c Read 0x10 bytes from 0x801c0000: 81c000b0 80001001 00000001 00000001 Write quadlet to 0x200000028: 0x1c000000 Write 0x70 bytes to 0x801c0000: 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000000 00000000 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 Recording: Reads from 0x801c0000 and 0xfffff0000410 as for parameter setting. 0x801c0000 = 81c000b0 80001001 00000001 00000001 0xfffff0000410 = 0x93e1daf1 Read from 0xfffff000040c: 0xa3500 Read quadlet from 0x801c0000: 81c000b1 Read from 0xfffff000040c: 0xa3500 Read quadlet from 0x801c0000: 81c000b1 Read from 0xfffff000040c: 0xa3500 Read from 0xfffff000040c: 0xa3500 Write 0x0c bytes to 0x20000001c: 0000ac44 0000e000 0000001c Read 0x10 bytes from 0x801c0000: 81c000b0 a0001001 00000001 00000001 Write quadlet to 0x200000028: 0x1c000000 Read 0x10 bytes from 0x801c0000: 81c000b0 80001001 00000001 00000001 Read from 0xfffff0000410: 0x93e1daf1 ... Streaming end ------------- Frequency was 44100 Hz. Playback: Write 0x0c bytes to 0x200000034: 00000000 00000000 00000000 Write 0x70 bytes to 0x801c0000: 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001 Read quadlet from 0xfffff000040c: 0xa3500 Recording: Reads from: 0x801c0000 = 81c000b1 80001001 00000001 00000001 0xfffff0000410 = 0x93e1daf1 Write 0x0c bytes to 0x200000034: 00000000 00000000 00000000 More reads from 0x801c0000 and 0xfffff0000410 Streaming data format --------------------- The PC functions as the IRM and thus provides the cycle timer. The packets do not contain an SPH. It is not clear how device synchronisation is done. Channels are sent in Fireface numeric order: Analog 1-10, spdif, ADAT1 1-8, ADAT2 1-8 (a total of 28 channels). Each sample is 32 bits, seemingly ordered least significant byte first. By default iso channel 0 is for PC->Fireface800 data and channel 1 carries Fireface800->PC audio data. In an example capture, packets carrying 6 frames were observed, reportedly at 44.1 kHz. Packets of varying lengths were observed (0x230, 0x2a0 from the PC, a constant 0x310 from the Fireface800). It is not known how the device maintains the correct flow of packets for the various odd-ball frequencies it supports. It appears that in certain iso cycles an iso packet is simply not sent in order to resync, but how either side knows when to do this is a mystery at present. The quadlets in a packet normally used by a CIP header seem to correspond to analog1+2. libffado-2.4.5/doc/streaming.xmi0000644000175000001440000547230514206145246016161 0ustar jwoitheusers umbrello uml modeller http://uml.sf.net 1.5.6 UnicodeUTF8 libffado-2.4.5/doc/SConscript0000644000175000001440000000065613246707102015450 0ustar jwoitheusers#! /usr/bin/env python Import( 'env' ) env = env.Clone() # At this point BUILD_DOC is either 'all' or 'user' doxygen_dir_list = [env["top_srcdir"] + "/libffado"] if env["BUILD_DOC"] == 'all': doxygen_dir_list += [env["top_srcdir"] + "/src", env["top_srcdir"] + "/doc"] env["DOXYGEN_INPUT"] = " ".join(doxygen_dir_list) env.ScanReplace( "reference.doxygen.in" ) env.Doxygen( "reference.doxygen" ) libffado-2.4.5/doc/reference.doxygen.in0000644000175000001440000017377713246707102017417 0ustar jwoitheusers# Doxyfile 1.6.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = LIBFFADO # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = $VERSION # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = reference # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it parses. # With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = YES # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = YES # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = $DOXYGEN_INPUT # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.cpp \ *.h \ *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = *.svn* \ *reference* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = . # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 3 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = YES # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = YES # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = LIBFFADO # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = reference # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = YES # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # When the SEARCHENGINE tag is enable doxygen will generate a search box for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP) or Qt help (GENERATE_QHP) # there is already a search function so this one should typically # be disabled. SEARCHENGINE = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = $top_srcdir/src # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = NO # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = YES # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = NO # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = YES # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES libffado-2.4.5/doc/motu_firewire_protocol-mk3.txt0000644000175000001440000005145311375520625021475 0ustar jwoitheusersProtocol details for MOTU "mark 3" devices, obtained in the first instance using an 828 Mk 3. Version: 20100517-1 Author: Jonathan Woithe Audio data streaming packets ---------------------------- It appears the streaming packets in the "mark 3" devices use a very similar protocol to the earlier interfaces. Each iso packet begins with a 64-bit CIP header. Following this are N data blocks, where N varies according to the sample rate selected (8 for 1x, 16 for 2x, 32 for 4x). Each data block comprises a 32-bit SPH field (consisting of a standard SPH timestamp), 48 bits of control/MIDI data and then the audio data itself. The audio data is sent as packed big-endian signed integer data, as for the earlier interfaces. At 1x rates, the maximum packet size sent to the MOTU is 0x328 (808) bytes: 8 bytes CIP header 8 data blocks Each data block contains: 4 bytes SPH timestamp 6 bytes control/MIDI data 90 bytes of audio data (30 channels) The audio data is ordered as follows: phones, analog 1-8, main out, SPDIF, ADAT1 1-8, ADAT2 1-8. At 1x rates the MOTU sends 0x388 (904) bytes per packet, providing space for 34 audio channels (order to be investigated): mic/guitar 1-2, analog 1-8, return 1-2, reverb 1-2, SPDIF, Unknown 1-2, ADAT1 1-8, ADAT2 1-8. At 2x rates, packets sent to the MOTU have a maximum size of 0x4c8 (1224) bytes while those sent by the MOTU are 0x0588 (1416) bytes. 16 blocks are send in each packet. 22 channels sent to the MOTU (order to be confirmed): phones, analog 1-8, main out, SPDIF, ADAT1 1-4, ADAT2 1-4. There is space for 26 channels sent from the MOTU (order to be investigated): analog 1-8, mic/guitar 1-2, SPDIF, return 1-2, reverb 1-2?, ADAT1 1-4, ADAT2 1-4, unknown 1-2. At 4x rates 0x0508 (1288) bytes are sent to the MOTU and 0x0688 (1672) are sent from the MOTU. For sending to the MOTU there are 32 blocks with room for 10 audio channels: unknown 1-2, analog 1-8. The MOTU sends 14 audio channels (order to be investigated): analog 1-8, mic/guitar 1-2, return 1-2, unknown 1-2. The following matrix represents the current best guess as to the layout of the audio channels in the iso data. Offset refers to the number of bytes from the start of the data block. "?" signifies that the channel assignment is yet to be confirmed and/or is positioned as a result of educated guesswork. Offset 1x Rates 2x Rates 4x Rates Playback Capture Playback Capture Playback Capture ------------------------------------------------------------------------- 10 Phones-L Mic-1? Phones-L Mic-1? Unknown-1 Mic-1? 13 Phones-R Mic-2? Phones-R Mic-2? Unknown-2 Mic-2? 16 Analog 1 Analog 1 Analog 1 Analog 1 Analog 1 Analog 1 19 Analog 2 Analog 2 Analog 2 Analog 2 Analog 2 Analog 2 22 Analog 3 Analog 3 Analog 3 Analog 3 Analog 3 Analog 3 25 Analog 4 Analog 4 Analog 4 Analog 4 Analog 4 Analog 4 28 Analog 5 Analog 5 Analog 5 Analog 5 Analog 5 Analog 5 31 Analog 6 Analog 6 Analog 6 Analog 6 Analog 6 Analog 6 34 Analog 7 Analog 7 Analog 7 Analog 7 Analog 7 Analog 7 37 Analog 8 Analog 8 Analog 8 Analog 8 Analog 8 Analog 8 40 MainOut-L Return-1? MainOut-L Return-1? Return-1? 43 MainOut-R Return-2? MainOut-R Return-2? Return-2? 46 SPDIF-1 SPDIF-1? SPDIF-1? SPDIF-1? Unknown-1 49 SPDIF-2 SPDIF-2? SPDIF-2? SPDIF-2? Unknown-2 52 ADAT1-1 Reverb-1? ADAT1-1? Reverb-1? 55 ADAT1-2 Reverb-2? ADAT1-2? Reverb-2? 58 ADAT1-3 Unknown-1? ADAT1-3? ADAT1-1? 61 ADAT1-4 Unknown-2? ADAT1-4? ADAT1-2? 64 ADAT1-5 ADAT1-1? ADAT2-1? ADAT1-3? 67 ADAT1-6 ADAT1-2? ADAT2-2? ADAT1-4? 70 ADAT1-7 ADAT1-3? ADAT2-3? ADAT2-1? 73 ADAT1-8 ADAT1-4? ADAT2-4? ADAT2-2? 76 ADAT2-1 ADAT1-5? ADAT2-3? 79 ADAT2-2 ADAT1-6? ADAT2-4? 82 ADAT2-3 ADAT1-7? Unknown-1? 85 ADAT2-4 ADAT1-8? Unknown-2? 88 ADAT2-5 ADAT2-1? 91 ADAT2-6 ADAT2-2? 94 ADAT2-7 ADAT2-3? 97 ADAT2-8 ADAT2-4? 100 ADAT2-5? 103 ADAT2-6? 106 ADAT2-7? 109 ADAT2-8? ------------------------------------------------------------------------ #ch 30 34 22 26 10 14 When an optical port is configured in Toslink mode, the two Toslink channels take the place of that port's 8 ADAT channels in the data stream. That is, ADAT-x 1-8 are removed and in their place Toslink-A 1-2 are added. So in the case of 1x playback, the Toslink channels appear at offset 52, with ADAT-B's channels commencing at offset 58 (the total packet size in this case being 0x02c8 bytes). Unlike previous MOTU generations, the Toslink outputs in the Mark 3 devices are present as channels in their own right - they do not mirror the SPDIF channel. Device control -------------- Optical port modes The modes of the two optical ports can be controlled independently. The primary mechanism for this is via a quadlet write to register 0xfffff0000c94. Bit 22: optical port B output mode (0=ADAT, 1=Toslink) Bit 20: optical port B input mode (0=ADAT, 1=Toslink) Bit 18: optical port A output mode (0=ADAT, 1=Toslink) Bit 16: optical port A input mode (0=ADAT, 1=Toslink) Bit 9: optical port B output enabled (0=disabled, 1=enabled) Bit 8: optical port A output enabled (0=disabled, 1=enabled) Bit 1: optical port B input enabled (0=disabled, 1=enabled) Bit 0: optical port A input enabled (0=disabled, 1=enabled) Other areas of the driver also appear to refresh the device status with writes to other registers at the time the optical mode is updated: 0xfffff0000c04 0xfffff0000c60 - 0xfffff0000c6c (ASCII name of clock source) 0xfffff0000c94 0xfffff0000b14 0xfffff0000b38 0xfffff0000b3c 0xfffff0000b04 0xfffff0000b08 0xfffff0000b38 0xfffff0000b3c 0xfffff0000b1c It is not known whether these additional writes are necessary. Clock source control and sample rate The clock source is set with a quadlet write to bits 0, 1, 3 and 4 of register 0xfffff0000b14. Values for bits 4-3-1-0: 0-0-0-0 = internal 0-0-1-0 = SMTPE 0-0-0-1 = Word clock 1-0-0-0 = SPDIF 1-1-0-0 = ADAT port A / Toslink-A (depending on current optical port mode) 1-1-0-1 = ADAT port B / Toslink-B (depending on current optical port mode) The sample rate is selected using bits 10-8 of register 0xfffff0000b14: 0x0 = 44.1 kHz 0x1 = 48 kHz 0x2 = 88.2 kHz 0x3 = 96 kHz 0x4 = 176.4 kHz 0x5 = 192 kHz Bits 25 and 24 of register 0xfffff0000b14 are used in connection with streaming control (see separate section). All other bits not mentioned are set to zero. Writes to other registers (as for ADAT modes) accompany the setting of register 0xfffff0000b14. MainOut/Phones assign Register 0xfffff0000c04 controls the MainOut assign via bits 7-4: 0x0 = MainOut 0x1 = Ana 1-2 0x2 = Ana 3-4 0x3 = Ana 5-6 0x4 = Ana 7-8 0x5 = SPDIF 0x6 = Phones 0x7 = ADAT-A 1-2 (or Toslink if ADAT-A optical out mode is Toslink) 0x8 = ADAT-A 3-4 0x9 = ADAT-A 5-6 0xa = ADAT-A 7-8 0xb = ADAT-B 1-2 (or Toslink if ADAT-B optical out mode is Toslink) 0xc = ADAT-B 3-4 0xd = ADAT-B 5-6 0xe = ADAT-B 7-8 Bits 3-0 of register 0xfffff0000c04 control the Phones assign. The meaning of the bit combinations is as per the MainOut assign case. Bits 11-8 of register 0xfffff0000c04 control the "Return" assign. Again, the meaning of the bit combinations is as per the MainOut assign case. All other bits in register 0xfffff0000c04 appear to be zero at present. Streaming control Bits 31 to 16 of register 0xfffff0000b00 control the streaming channels in the same way as for earlier MOTU devices. To start streaming: - a value of 0x00000002 is written to register 0xfffff0000b10. - bit 24 of register 0xfffff0000b14 is set to 1 (other bits are set to their current values) - a bunch of other registers are written to which, given experience with earlier MOTU interfaces, are not strictly required - register 0xfffff0000c04 is polled. A result of 0x00000111 requires another poll. Polling stops when the value is 0x00000303. - register 0xfffff0000b14 is read. - bits 24 and 25 in register 0xfffff0000b14 are set (other bits remain unchanged) - another bunch of (possibly inconsequential) registers is written to. - register 0xfffff0000b14 is read again. - writes to the same collection of registers again. To stop streaming: - bit 25 of register 0xfffff0000b14 is cleared (other bits remain unchanged; this includes leaving bit 24 set to 1) - register 0xfffff0000b00 is used to stop streaming in the same way as for earlier MOTU interfaces - write to other registers of potentially little value - register 0xfffff0000b14 has bit 24 cleared (all other bits remain unchanged, so bits 25 and 24 are both zero) - immediately, register 0xfffff0000b14 has bit 24 set (all other bits remain unchanged). - other registers are read again. It appears based on this that the streaming control of the Mark3 MOTU devices is the same as for previous generations. CuemixFX control ---------------- Control of the CuemixFX system is via writes starting at register 0xffff00010000. Either 1, 2 or 3 quadlets are written depending on what is being done. Each packet includes a serial number which is incremented after sending each control packet. The same serial number counter is used regardless of the type of control packet being sent. CuemixFX heartbeats ------------------- The MOTU periodically sends a heartbeat packet back to the PC. This is sent to address 0x000300000000 and consists of a single quadlet length write block request of the following form: 00 aa 00 00 "aa" is the serial number ranging from 0x00 to 0xff. It appears that heartbeat packets are sometimes sent to the MOTU. These take the form of single-quadlet writes to the CuemixFX address. The quadlet takes the following form: 02 aa 00 00 "aa" is the serial number of the packet ranging from 0x00 to 0xff. It's not yet certain what purpose these packets serve and whether they are in fact necessary. It is also not yet known whether the MOTU or the PC initiates the process (that is, which one sends a packet with a given serial number first). Pedal events ------------ When a pedal event occurs the MOTU sends a quadlet write request to address 0x000300000000 on the PC. The format of the quadlet is as follows. bb 00 00 c2 "bb" is 0 for a "pedal up" event and 1 for a "pedal down" event. The pedal is "down" when the pedal switch is closed. CuemixFX variable controls -------------------------- The control of continuously variable controls (for example, faders, pan controls) is by way of 3-quadlet writes to the CuemixFX register. The format of these writes is as follows: 02 aa 66 bb - cc dd ee v1 - v2 v3 v4 00 where: aa = packet serial number as previously described bb = the bus/channel number (0x00 = mix bus 1, mic 1) cc-dd-ee = the key of the control being changed v1-v4 = the value being written. v1 is the most-significant byte. The value appears to be a 32-bit float. That is, v1..v4 is a big endian 32-bit float. Keys presently identified are as follows. 00-01-02 = Bus reverb send (0-0x3f800000) 00-0c-01 = Input reverb send (0 ("-inf") to 0x3f800000 ("0 dB")). bb is the zero-based input channel index for this key. 01-01-02 = Bus reverb return (0-0x3f800000) 02-00-01 = Input trim control (mic inputs: 0 ("+0 dB") - 0x42540000 ("+53 dB"); line inputs: 0xc2c00000 ("-96") - 0x41b00000 ("+22")). For this key, bb is the zero-based channel index. 02-00-02 = Bus master fader 02-mm-02 = Channel pan. mm is the standard channel index. Values range from 0xbf800000 (L) to 0x3f800000 (R). 03-mm-02 = Channel fader. mm is the channel index starting at 0x02 for the first channel (on the 828-mk3 this is mic 1). Values range from 0 (-inf) to 0x3f800000 (0 dB). 05-mm-02 = Channel balance (active only for stereo paired channels). Values are from 0xbf800000 ("-1") to 0x3f800000 ("1"). 06-mm-02 = Channel width (active only for MS-paired channels). Values are from 0 ("0") to 0x3f800000 ("1"). 07-00-00 = Focus select. v4 = 0x01 for channel select, 0x03 for bus select. For channel select one is focusing the actual input, not the channel in the current bus. Therefore the bus select byte (bb) is always zero for this key. v1 = 0x00 (mic 1 or 2), 0x01 (ana 1 or 2) and so forth for channel pairs. Bus focus focuses the output assigned to the bus, not the bus itself. v1 in this case is 0x00 for Main, 0x01 for Analog 1-2 and so forth. Again the bus select byte is always zero. v2 and v3 are always zero. Channel EQ controls: 02-yy-01 = Input EQ frequency. Value range 0x41a00001 (20 Hz) to 0x469c4004 (20 kHz) 03-yy-01 = Input EQ gain. Value range 0xc1a00000 (-20 dB) to 0x41a00000 (+20 dB) 04-yy-01 = Input EQ Q. Value range 0x3c23d70a (0.01) to 0x40400000 (3.00) 02-yy-03 = Output EQ frequency. Value range 0x41a00001 (20 Hz) to 0x469c4004 (20 kHz) 03-yy-03 = Output EQ gain. Value range 0xc1a00000 (-20 dB) to 0x41a00000 (+20 dB) 04-yy-03 = Output EQ Q. Value range 0x3c23d70a (0.01) to 0x40400000 (3.00) The EQ number ("yy" in this table) is as follows: 02 = EQ F 03 = EQ A 04 = EQ C 05 = EQ D 06 = EQ E 07 = EQ B 80 = EQ G Input channel dynamics controls: 01-0a-01 = compressor threshold. Value range 0xc2400000 (-48 dB) to 0x00000000 (0 dB). 02-0a-01 = compressor ratio. Value range 0x3f800000 (1.0:1) to 0x41200000 (10.0:1). 03-0a-01 = compressor attack. Value range 0x41200000 (10 ms) to 0x42c80000 (100 ms) 04-0a-01 = compressor release. Value range 0x41200000 (10 ms) to 0x44fa0000 (2 s) 05-0a-01 = compressor trim. Value range 0xc0c00000 (-6 dB) to 0x00000000 (0 dB) 02-0b-01 = leveler makeup gain. Value range 0x00000000 to 0x42c80000. 03-0b-01 = leveler gain reduction. Value range 0x00000000 to 0x42c80000. Output channel dynamics controls: 01-09-03 = compressor threshold. Value range 0xc2400000 (-48 dB) to 0x00000000 (0 dB). 02-09-03 = compressor ratio. Value range 0x3f800000 (1.0:1) to 0x41200000 (10.0:1). 03-09-03 = compressor attack. Value range 0x41200000 (10 ms) to 0x42c80000 (100 ms) 04-09-03 = compressor release. Value range 0x41200000 (10 ms) to 0x44fa0000 (2 s) 05-09-03 = compressor trim. Value range 0xc0c00000 (-6 dB) to 0x00000000 (0 dB) 02-0a-03 = leveler makeup gain. Value range 0x00000000 to 0x42c80000. 03-0a-03 = leveler gain reduction. Value range 0x00000000 to 0x42c80000. Channel index values (mm in the above tables) are as follows on the 828 Mk3. 0x02 = mic 1 0x03 = mic 2 0x04 - 0x0b = analog 1-8 0x0c - 0x0d = SPDIF 1 and 2 0x0e - 0x15 = ADAT A 1-8 0x16 - 0x1c = ADAT B 1-8 If a channel pair is flagged as a stereo pair then only the first control register is written to. Reverb parameters: 02-00-04 = predelay. 0x00000000 (0 ms) to 0x42c80000 (100 ms). 03-00-04 = shelf filter frequency. 0x447a0000 (1 kHz) to 0x469c4000 (20 kHz). 04-00-04 = shelf filter cut. 0xc2200000 (-40 dB) to 0x00000000 (0 dB). 05-00-04 = reverb time. 0x42c80000 (100 ms) to 0x476a6000 (60 sec). 06-00-04 = reverb design: low time. 0x00000000 (0%) to 0x42c80000 (100%). 07-00-04 = reverb design: mid time. 0x00000000 (0%) to 0x42c80000 (100%). 08-00-04 = reverb design: high time. 0x00000000 (0%) to 0x42c80000 (100%). 09-00-04 = reverb design: crossover low. 0x42c80000 (100 Hz) to 0x469c4004 (20 kHz). 0a-00-04 = reverb design: crossover high. 0x447a0000 (1 kHz) to 0x469c4004 (20 kHz). 0b-00-04 = reverb design: width. 0xbf800000 (-100%) to 0x3f800000 (+100%). 0d-00-04 = early reflections size. 0x42480000 (50%) to 0x43c64000 (400%). 0e-00-04 = early reflections level. 0x00000000 (-inf) to 0x3f800000 (0 dB). CuemixFX switches ----------------- Switches and discrete controls in CuemixFX are controlled through a 2-quadlet write to the CuemixFX register. The format of this is 02 aa 69 v1 - bb k1 k2 k3 where: aa = packet serial number bb = bus/channel index (0x00 = mix bus 1 or mic 1, depending on the control) v1 = the value of the switch / discrete control k1-k2-k3 = the key of the control being set Depending on the control, the channel number being set is communicated either as part of the key (for controls which operate on a per-mixbus basis) or in the "bb" field of the packet (for controls which operate on a per-input/output basis. The keys appear to be rather structured. The value of k3 appears to indicate what kind of object the key applies to: 00 = global resource (eg: talkback) 01 = input channel (corresponding to a physical device input) 02 = a channel in a bus mix 03 = output channel (corresponding to a physical device output) 04 = global configuration Keys presently identified: 00-00-01 = Input channel phase. Value is 0x00 (normal), 0x01 (invert) bb is the input channel index (0 is mic 1). When two adjacent channels are paired the phase of each can still be independently controlled. FIXME: also under the 00-cc-01 entry. 00-00-02 = bus output assignment. Values are: 0xff = disabled 0x00 = main L-R 0x01-0x04 = Analog 1-2 though 7-8 0x05 = SPDIF 1-2 0x06 = Phones L-R 0x07-0x0a = ADAT A 1-2 through 7-8 0x0b-0x0e = ADAT B 1-2 through 7-8 00-03-00 = talkback channel select. The value is the 0-based channel index of the talkback channel. 00-cc-01 = input channel EQ/dynamics switches. Value is 0x00 (off) or 0x01 (on). cc values identified to date: 00 = phase (0x00 normal, 0x01 invert) 02 = EQ F enable 03 = EQ A enable 04 = EQ C enable 05 = EQ D enable 06 = EQ E enable 07 = EQ B enable 08 = EQ G enable 0a = Compressor enable 0b = Leveler enable FIXME: work out some way to identify the EQs. 00-cc-03 = output channel EQ/dynamics switches. Value is 0x00 (off) or 0x01 (on). cc values identified to date: 01 = EQ F enable 02 = EQ A enable 03 = EQ C enable 04 = EQ D enable 05 = EQ E enable 06 = EQ B enable 07 = EQ G enable 09 = Compressor enable 0a = Leveler enable 00-04-00 = talkback listen channel select. The value is the 0-based channel index of the talkback listen channel. 00-mm-02 = mix channel mute. mm is the channel index as for other keys. Value is 0x00 (off) or 0x01 (on). 00-01-02 = bus mute. Values are 0x00 (off), 0x01 (on). 00-0c-03 = output channel monitor enable (0x00=off, 0x01=on) 01-00-01 = input channel mono/stereo mode. Value is 0x00 (mono), 0x01 (stereo). bb (bus select byte) is always zero for this key since this acts globally on the input. 01-00-04 = Reverb split point. Value is 0x00 for "mixes", 0x01 for "outputs". 01-mm-02 = mix channel solo. mm is as for the mute control. Value is 0x00 (off) or 0x01 (on). 01-0a-03 = output channel leveler mode (0x00=compress, 0x01=limit) 01-0c-03 = output channel talkback talk (0x00=off, 0x01=on) 02-0c-03 = output channel talkback listen (0x00=off, 0x01=on) 03-00-01 = input channel swap L and R. Value is 0x00 (no swap), 0x01 (swap). bb is the input channel index. 04-00-01 = input channel stereo mode. Value is 0x00 for normal stereo, 0x01 for MS. Relevant only if input channel is in stereo mode (and thus part of a stereo pair). 06-00-01 = input channel Vlimit on (mic channels only). Values are 0x00 (off), 0x01 (on). 06-0a-01 = input channel compressor mode (0x00=peak, 0x01=RMS) 06-0b-01 = input channel leveler mode (0x00=compress, 0x01=limit) 06-09-03 = output channel compressor mode (0x00=peak, 0x01=RMS) 07-00-01 = input channel Vlimit lookahead (mic channels only). Values are 0x00 (off), 0x01 (on). 08-00-01 = input channel soft clip (mic channels only). Values are 0x00 (off), 0x01 (on). 0c-00-04 = Reverb early reflections model. Value is 0x00-0x04 for "Room A" through to "Room E". bb is zero. libffado-2.4.5/doc/amdtpstreamprocessor.dox0000644000175000001440000000025610604255365020433 0ustar jwoitheusers/** @page amdtpdescription The AMDTP StreamProcessor @author Pieter Palmers @section intro Introduction ( still to be written ) */ libffado-2.4.5/doc/class_diagram_1.eps0000644000175000001440000150042310441126147017154 0ustar jwoitheusers%!PS-Adobe-1.0 EPSF-3.0 %%BoundingBox: 36 207 872 820 %%Creator: Qt 3.3.6 %%CreationDate: Mon Jun 5 17:23:50 2006 %%Orientation: Portrait %%Pages: 1 %%DocumentFonts: NimbusSansL-Bold NimbusSansL-Regu %%EndComments %%BeginProlog % Prolog copyright 1994-2005 Trolltech. You may copy this prolog in any way % that is directly related to this document. For other use of this prolog, % see your licensing agreement for Qt. /d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D /D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D /CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D /TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88 0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{ LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{ gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7 bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL 0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1 eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if 64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3 i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3 1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D /sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3 colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1 QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2 add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq {false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d /DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7 DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d /BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4 DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{ pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{ 1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie} if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4 2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0 exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0 6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT }for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1 ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT 0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R {/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul 200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90 x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0 -90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS} D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255 div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0 B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25 /LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3 -1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch /fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d} D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT 1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D /RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP} D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D /LArr[ [] [] [ 13.333 4.000 ] [ 4.000 13.333 ] [ 4.000 4.000 ] [ 4.000 4.000 ] [ 6.667 4.000 4.000 4.000 ] [ 4.000 6.667 4.000 4.000 ] [ 6.667 4.000 4.000 4.000 4.000 ] [ 4.000 6.667 4.000 4.000 4.000 4.000 ] ] d /pageinit { 36 24 translate % 184*280mm (portrait) 0 794.25 translate 0.75 -0.75 scale/defM matrix CM d } d %%EndProlog %%BeginSetup % Fonts and encodings used /NimbusSansL-ReguList [ [ /NimbusSansL-Regu 1.0 0.0 ] [ /NimbusSansL 1.0 0.0 ] [ /Helvetica 0.988 0.000 ] ] d % Font resource %!PS-AdobeFont-1.0: NimbusSansL-Regu 1.06 %%Title: NimbusSansL-Regu %%CreationDate: Sat Sep 4 15:58:56 2004 %%Creator: frob %%DocumentSuppliedResources: font NimbusSansL-Regu % Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development; Cyri % Generated by FontForge 20040824 (http://fontforge.sf.net/) %%EndComments FontDirectory/NimbusSansL-Regu known{/NimbusSansL-Regu findfont dup/UniqueID known{dup /UniqueID get 4095404 eq exch/FontType get 1 eq and}{pop false}ifelse {save true}{false}ifelse}{false}ifelse 11 dict begin /FontType 1 def /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def /FontName /NimbusSansL-Regu def /FontBBox {-174 -285 1022 1196 }readonly def /UniqueID 4095404 def /PaintType 0 def /FontInfo 10 dict dup begin /version (1.06) readonly def /Notice (Copyright \050URW\051++,Copyright 1999 by \050URW\051++ Design & Development; Cyrillic glyphs added by Valek Filippov \050C\051 2001-2004) readonly def /FullName (Nimbus Sans L Regular) readonly def /FamilyName (Nimbus Sans L) readonly def /Weight (Regular) readonly def /FSType 0 def /ItalicAngle 0 def /isFixedPitch false def /UnderlinePosition -151 def /UnderlineThickness 50 def end readonly def /Encoding StandardEncoding def currentdict end currentfile eexec 743f8413f3636ca85a9ffefb50b4bb27302a5955dc23e0f3397300c8fca519e5 6c902cb3b617f2a7538b6dc265a7cf20d53eb36e373340044ed3c3212b846132 e1960981d2006ccde888fd9671cdd7ed1352864a4b63c0967db2112fa1c98585 65c8fbd0ddd08b57b7fceb3d24b86e4b8f343e1d177e9f46bdf43b991761555a 2bd0087d171a8d4e0704d7976ec07f0120f5004a55851e3d7f1b101ccddc3f8f 02070217787e5d3d718929d6e6b9f17d30a17eb6ace1f85be11ba385884d9d00 1bf3ff9cce9ea1f0b0ab744dd526509fcdf64158c62aa1cb3291ff7366804024 16786710a83edd39fb5680e8afb4754d6141d843d8a0fcbc57c15daf7ac9d506 b9b8c2ff41b6fbdd8545d760da8a23868b017b34eb16a584d2d421063572d2c5 2e7abdfecf267f260ba26ba2c91f02f6919737b1fbf601d8139cad84b23eed0c 6f704c14677829d72acb3468e9b4833622726ad6ec0cad8f7395586491972c60 6576dc156cc7719b66525e3c5bdaf621fe1dafb2f9f64e3b8bfb11332c8e26a5 bf836a2ef8166dccde5136f14dca3e9e1d1c6ad39240b64fe8d8ba2f4a524aca eb0b854af443bd6cfef9e95d27b6881a66ff7feb8b9f06ebd84fd14be7409379 ed65ad5e1e045fcc55b554c1c6e9f573b6a1827309a2229634a8f0c84249616d d08cd17e467fc66a69ba0fa1f736d80d949adeea1f86bf7ebbaa577df96d267d 901d56d61159edff1868138d8414da43e2b9243b20deb8edc7153581d9a4ba84 03cea0cabb0888f56e1942b1a58c57052b6496f97d1e92084374bb60f94fd697 af2a4c05f01411a8edecc569f44a5b5def29e344a28cd438b41c06deda5eb531 96f03961dac7871fe1e915cdc99e5226e610eab9625c25cf6d859b5d0bc21359 dfe416af33c2de4b119d5a57d911ed47d4383342ffed04c461fca1bdd96b3723 b17e0150fcbe0c45033d663e067aa0cc3902d2397dadc77352b4740acab53b8b 1d27d2e97c5f500ea19191812af6c6a845e095c12ff2d4d73431e18446168fab 5fc2b561bc282dc05711ac9d96a1f006f33eff91584f7d57a6a2b41568116bad 128f111b3af6e5d1e965a6e2c97e9f8934cffdc6d017c10ad915f35d79ea94ea 67ea9e7fa67f6980c92157576df80f37896283c0a768c5591cfcbd40150ff1be 848921a1d91877aee63426c9a6fc7139b9f5cc77bc9edb6cc3dd75ef6bb95707 7cae7ebd8f64573a081ca828955cbf51d3c9a4b0f43ee1ed059eca8577850f64 a98501bf933ef80df9a5a269a00126e3b279a17394de3948f26feb527f630e20 bff6dd07c2a513d205c6a122ce1168ac9efb96cd227bb77614f6cd851ce93123 ee0ce5d9561a94df1d6e82a6cade76cbe9e7542bbcbe83688a6b2facfe692d56 f2ec8efe2cfcddb2ad31de40d19541e61783246c91ade32e75314429133442c7 81a90c58d2fc738867e362b488587a12b7343c74f5a70ca624d56547ba9c0b4a d7755efff3f454ba7cbba779cb3d52261c8e1be78d6a14d211b153d82350b38d 5a2aeacc05d5ac56ec358ab0025e70045fb3c9588f605c950e57e6ded3f69292 780324da0d28bef378e5528c4bb9a1074e1ea3f9170b2d766a73f3e51fcee031 cf974331f2a54c9d44c22cf37fcb015d8ee8a11252bc119ecbf218c6bcce816c 33e97e4aafae256337be81803542a4870a2afd7543612eadd87d6abadcdd1fb1 6f48981953dfafc04ff72434c7f63f3ec5e3350009a9af65ccd9180268832e54 31ce448d52594e6540fee70442fb6bfc8f19d44bd76af7cf78b081ec54c86257 32f98f48d473bbc564f3d1bb2c2d177adb04193d02841f0611825d53a04ff283 9382d8ab0e7fb100ddab56e3f9a2b89179ca4fa267bfbefce5bea4ee0edfebf5 5127f31a67b740a7821d1aa851f370c5ecb8e6c39841ef22b6a9d9de1486dd21 13624aadf66af256723bf850d09b9053376a446172923f986abdf3374f07715c c5671228224d5df6b308d90908616d5c73c9b95f5eefc2b31165556b3b7f0ba3 ca4d50a719e65bc02eb4698488db79f63fd703f6cf638f0a7c74e25118a43468 06ae05e5d22b1def20759382bd1fcb38a87ae5fefede6a0bed5ac7902cf4b8e0 308a8540f9348355182934eecb3001898379cd38ff86cc181a748fd10dc8100e ea23eb64f01354fd6e649b1364d13cbee3ec817dd6bb20dd5b2d08df0dbfae4e 13d3a393eb9d1cb73d5658f7118e5255c760f6fdf672d24a6c0cafb741c387fe 4dd6d3dc95ba927d609f4d257148095f0186d98ae263c17192a6bc3f1698107a 510baefe6ca7606a46e39cbe846d98849c94ee556f5714d512882dca4186639e 9378443ec0ec35cbf7c1938f7d256a653a0754e365b060b491aa51431fd36bb8 0a4f93fe933eef1cf29ba7263ea0b3fd1a784823ba62cfde6dd8aa9ee68ec056 8476076a4af1d7b4c1757e9903f53089152a9af0f2f39b46de7a6d5f3882b587 f7bf0daca257ee71089813b180b19acf4c51c35518ab547e31779c2adf0a3e4c 69328d4b8e013734f9072d79628987d1d2746d77d3342edc6c78e0ad52d2adc8 ff5e375de0684738079808e3012cd24f755b5e0dd651cfa4009872f985b795af 1d37fc1dad0e7171d0261b3c94fda70d0ec5f1d6f0d0ea2bd36a4f106c11c14b 56ca7c5a90ff6f6d7e854848992de617de552dfe8bec2d3fea6689e215ed3c43 5346e37607c1d5c0fefaf641e3bb9e1d6691528bcfa844d1a19d4a80ffbe50a4 c1d37d1bc0c5a5684182bb7a03120137c592c93ca6551999ecd883e1fad354b0 75d9f5a0e2fd2f5871d00e307df675a3885d0e9decb59753f29cf0f54e9a4261 2ff3833c860a74fd04b340d1bb216e0864e540ec9eb2f2443075958b8d3dabec c5fc0a507dfbea0d26ad21dfac24096ae1a9ec1e7098a753e5ba46ce12480320 163f8de943e60d875796b4a897bc8b840f39a0d5bdeb2f42879f6f8b049c86a3 86387a8301a86997d7a8332fc848f7127f497732357e2eb74b051a0e2d647350 5b3e457d96538f1cd4046fa1f3dfc27f6017d8b983a84edb92793376c67f4cfc 598741426d0552d1438444f8cfc83ba80a1e4e2a873c75f8bb2333cec60d5dbb f14d158eb6d1fcb95fc553c27cbef47e6ed0fba29cc68a64b5b6d99aeae08352 74efcc0dbd27a1a18d4f0a4829d6d1cbe876f7f79e71323175361ae8e8970f9e dacc93d211b4738f13b96217aa1af066257f6c2f595da272ace492e5763968c0 eef844dd8dc6ef550f47ee2366f4d4285b5621dfc3c9888cbc2cadc5e6989a9f f297c4b0227e56e665a7138b32ac85fb78210b6c431043820bd416a55e7d8f4a 91a842e889371755f2bf94b9c0f187e11e999344dfbcfb5888b879e9adfca294 8696f5702dc73362055335453c72086a29961dc8a7bea74cbb9db0ec7aa9bc2b 1d9ed6d09d9050ffb47d07022e9fef701dea45e17cbb7d6b2e0914cfe921253a 08afde725b7163804607aafdc0c3b5505fbbb9098dbc4655d75113b34e1cbcf7 8a7703a6c66801b55de92c936782eaebee313f925397b81cce16946aaa44fecd e499d17ebb392e760314a35a26b29bf11cd620a9fab1feeaa8fb7e57574f65ef 3ddc6cffebc6af3d887c975a68c0108a030b7074fd392a2ee4d9735c72ad3c5d cc278d820afdf8499ea43ed69c48c0a291a779e9f19baae56d978ab35d32a9c1 996445dc89cd2e85f06c59222ebf56e65df8e68feb85bc17f12135e2f0395245 fe864741f1d078eb4695191d8bd8ed8bb021873b77bb81d95673b916006a43a7 a25d4e71b9ec641c026d767125b906d123550c7f0db77555deae1342d2d1c1e6 e71e62fe37c9fefe74f79fc5c57983b9cccffb99882ce6064a98e41c2d53f2a0 6438bfc08d1ed2afbd5abf37e539882e1bb36a93f2af3fd56a69dd2edbfcd31a c7a65804cb397e8ae3cecd62300b75e69d6e6522c79dd119340d4a0bcf5d2153 04a8934372e5f11c22d4a0e2309b8e8cc620991c197896813f12a170774d19d7 39b4dbafd5a2d89ade785ad8ad978f7afc787b984bcc00b933b76de6c2c4ff93 8ba58db35e16cc7895a64597b1b004d931a0957e72c96ffc5de8efa7d5110d77 4c227cd8689c04ddce2322c7acff5c7d39e62ac75a20837191e12c6208f892ee 737c4b7a9b999b338943c0c9a2f934e4c24a4481598cd344e0e9ae4583c93d95 489f12bf0550cd449c95025fa6c59bae268962b0078fa1fac6b58c449148ecdb 6ab6e65d1174fc75af7d3e205ae51b3a96d4cc3169eaa5e02a3169f7a0393059 475cba0e35df4cf8edc7276895329b21d9c3af7819e9be50a36564dcef530750 48083259c6c642d7d99dda2049c654139ed8662a139fe1c80d3de43bef2628c3 38d1510012bc17b89ae25d8d381508d81c02c1fdae9a0947ec24b1a87177d631 386fad62c51073a9f723d9d1d5d3dcbae0eb3d2295b0337bbd8e4bf27ad0b07b 9fad81ed86f6aa04e6497fab0f341006453f96ab1dfbfef5db17caad07b320fc bf9f2d4d859fd43ec7d9c5be237e8700c76108f95f5dae5762f45597ca585563 d6bb979d7ed3cb7753fe947846b73357a19b7cf0521153399e9c600a94d851be 6ad10c12d85706324794aaf43bb0a979d4f173b776cae5a4755393d3b545b64e deb22d7f4e033cb259658b2c64327e94e1e223f112981c9f3dac9626931684a4 48b4e8c7b946d58fc041f1a58805ca689ae366a49451a257bfb519e1f4daadc7 c06c7df549cf3fb1168458d378f136110de61967c726888e69c38185068145a4 11e04fec34d20cd91bc45eabd103220728fc7deb9bc811b82bd534656f062c4a ccce035844b59c02b9b8959b6e2ce80d0c735723c2f77a3da3e9a0d4ebd89de6 72735cd768dc00d7c49d400d20cb1efd273b655bcd03b9a040390701d4b7c1bc ad9e4539b41d9ecb852b0881670768774c843dcd9eec2072717afcbb262a70ed 2b36ab5c8ebe038be36774383b92aec6249e8bdd5337e4cf71783fd879c8fdce 2c62808aa9377d1b1c60d282698f76983ef18d013346f88772e43b06339467ad bce48329422863e8118b15c550cd3c0bfdb8018c0f87a0a579ce23d625a62d70 e053efdf3c6c1be5b4d1301a623e2c2e46b8a62a379e4ebbf11cf671082eff23 2a8bebed16a079e8540b38c18c959952fef19cc2b3d314d69f9897029e74b51b 8cf85d43bc42fc2173db77d9ea70370a468c31c89a745c2e1c96e1861e9d9336 c3a3a4e7d94a0913749b0158178f31d123c5cd8422540056599b138b1709076c 950395a677d6a1656846bf988da23117f5a468b87909c26fad871f3d7d95cd9a 9de992e5400df7ad77308e731683e85a86f20271e3144a68d06291bd9b134f46 b9f641b26f495da5d227721d3b09b9475eeffef7c2dd807dd9119f0cf2d8f1a6 b0ebed030f65aac99ead62c3d8c4c59a223e199a6263a61e67ef7e55c41598af e5b124dffe5ffa3604c5aac39bac71e2235897db370c0352026ba5460a596fb6 965a6b5a10a0312fb9ca5797d4fa732c4e9f6ccb8af1014202bd76e04ecc5506 cf970bb71f72a600259e5cdff9ce8e190a92b5979a35a22df7b49eb583a39423 9446ef4f270e275f49f5606bc408038193a566ec0ea60c1a46e1df2b88481711 659491ba8aeb831ed0a273d715d12bcce2040b652c74e6cb2b40a87fc07366a5 efe6f0fff3613aa8b2659c9586bbb6eaf65f4b27b5aeb9dbfc0b608337d4cfcf 56366559e930330861864e9b03bb4c559c9309fb4152bdc97a90cfa5e9aa649d d8a090dd24070419921499c61fa56f574929649b6a80d84683ee4783f131d149 b12f23e18d5e012e592da0dc18c24b7a2141f58db72fe1de536161cfb9425996 9ef7276fada54845a46c23070fe9badbd79123db19e25ffde0b1ef08112e332b b3f1aed16220d289cb0c59e4c65b3dab135b6708ca4a5b5aff292e412499a369 df98d830773398ea7de81fcde779d6fe1edaab9145ebeae8d45685c326d41a08 35c5b211fc7e93f5334dcfbdacaa6a6cdf4ce532fac62cb80e3fb646fedf675b ee9422fa2021a08633ffc893a317473f8b6dba84405075bcdd06f2c0bee7bb6b c2bf7d341a7bd77e4d5c7ed964dde13acb197fbe10e6c1648e18dd4b5260d61a 5e219c9a08612f7ec8b50ccba576a6a29a6d848afd04d754b18a745576fa8edc ab6b2caa4935bfbe5c659c41e047da36e4ddc9bf04cf5ffe8512cc2830c4f6a6 8132c8b3056960ea102494867054b3e7d016305784d835dce4a636dcc49757c2 395709cb7afd06f23456aa2fc739b87ace0f766a5c21a8e48c9c8b77b7161905 9c34c956dd58525d6eba9987e0d9da375e4d74633bd06eb1c813155e40cc3e16 2e43914799aca218295b9d9222b603d56d67cb985d769ca9488baafa37b70050 4bb4ae2f4ecde32aafd1e8d6da43ed5cfccbd28cdbd7df57501358f85892e412 adf9c207fdcf922659125efced634f2bd6d64e95e5516dbbe8e6ee1ed9c4251d 39624540c29fe0a0fa72451f8bcfde9081010c6a3fc4569d80df21fec34e1629 88b0e09cc4a801c1c6e4c10f1c9e32e829e5c75b86f7153ce19052fa3a593b97 b0f73e11e42b9a33adaab0def989fd8fa7557e1acaa866c70b6ad9e4fea68890 d94df460d5ab99fcf3551ffad27711fdbd1b4bba756a2307edfa68efa2c96328 50b05eb06e4d02f8f3a9d731c41ca6d55c02f8d0712b054619f31686b159261b 85c6b9a42cb6419b24dc9e2891f3c125acc5a651a60542c1fd753ef549e611c8 d458e0fa31d8e4f12b3299a024d8fb19d898da955b0febdfecfa335d1fbf243b 518023c28016819c93fd5de0d584baa55e6ba184f591081d7c4cf3a7a2ae458c 295fd4fdf34754324a31961eebcbf36c9931d62e0a01acf83c5c28987eaa4422 2c71d8fe4450e802328e4c217de9d20c8091b685670bf9b88bb37f9976e272c1 f291bd1094f2bac577ab5cd77ece0066dd8dbbbbf866226afe1bb13e4e93092b c412dcae799e4f533f08c609bfefd826d1fc5721ddb7dd8ee23406e229ffa72c 4db12301e70997cb9970f411e8c3f3ef06d3fdc55c2a80f5042f1b9e48e4a700 c0a484586974c897e3aa0685484b09049e01a3f1d46b8bba57c7f2a66aa30c5e a0d45604e53eb934142699ae88bbcc3d5eb1564e34a35b5de4e1d8cf120ef7f6 212cac2dd6f3f9a7752b608f0ff548567bed2b85be9b097955fed21e8460000e b7ab68e3b48cca79ac3ae78b79c541e51ff58de85da19d8c92442f2f6865efc8 a050684f849d1123e244748bf5e5ea0d2be2825bfafebbf6e484c2231996abf5 847752466f58e65946a2f52bff40603bb05da95b03d5af03f4014a408dc556e0 f879a6c1aea5d34c5a9399f1784afed929bcc3244214172b0306178b9a6b9cab 78ac619cb08750e62b5e847bc73d8558003aa2e3fa9ba2837d33fc3c7de9cbe6 00dbfdd5ab61d0b8c481e50e8b62865e7728fcdf99d820a7fd0f738f0be6f641 746fc74f8632a75d676a6bd5957989d40d2877332f25c5cc5a61f328bf92e94c 0ebf439b2e6921c3b465c1a8277d0c3050db075ba5844b512d2da571a7d656a7 6e2bef22762904bd9a1c95825cb4b5ecb9237e1227a0777fbab880ee8dcff8b0 bc1e0a2127de9455e546ef259aecf2826ea6c6b7562550f965a5c7a922f2813b e6538a3a423bebc7a3fd0f38a43a2e03418fe1640af682ce600ebadb8c65e730 80695e1adcaee1b0f80c8c64ed3435a9b9203e38931c1a89b0a3ade28990a2cd 0fb13d5e57a77389b26c32c1e2f1a228a2c18fd87e8f933f86f4d6852d93f21d 8ede9e1f6175cee6ee86882289564161b3aa8916fb6dda5000faef6f915c1ab2 0a13a2b89aece0cf33ce493c72ba30375a408f099a4425306f14a3f9a45a9327 107f6cfed4d4f7ec60f3af29288fdd29d976b27995dcfd1093b4b4e83eab4882 896be8a8fe39eba6ed88f2e8ceea897efe14dd3c8756cd688569615995d8adb5 ccdb30f9c25ed1a4e4232b9504708973ee5aa944afffd2a1c3cf0912acc3db8d 9380adfa309d44921dfd32eb75b8ee4daca997bce1618fb8fa7cc3c3b8511829 e251ffaacab3ef9cc1e97d7ca96b9c6dbe2404324f527993b9645e6d653487d9 b21ddbaae1cfcfe43a3c7953b8d969b2681647f99668bd415ce748f11460cb4e a16645b1997852024b1e7712d2a9716cc675badb16714ca4616eef27f92a4fdc f6c2abc231ecdfc762215c46b438bab7574a52da5031ee4a849b9754f9669c4f e9f7c001c6f32aa388d73c00f6f0fdc7f93c359337c75b05755006cfee89071d b7dab43a91457125239dbd06cebb7acc3383a89efb8bc9d9bfc5f9232228f229 8ed28bdbd430f2e1301a90e6421b44c201a79caf0340f399d95c2546d514b100 ea16242c2cc6bfcb457463b5bc5f89b0bd3353f5008f48f26c04f9ca7779f501 400c8cdae6618707fc8656ef9de576ef8da67e650eb7b1d96ae111ffa488f8f4 5b30cb96ae35a6ba0b59619a45079205ea3b586955e8c545b301b5801f7525c0 f1210550193785e67fa3b14f828cd6e2a39dcd8f955e5a7c1569199bf387b45c 3797058c9ca8d86f74cfe9e43cc2675254f0dfe9c41c462d9b8fbb2b5a4b3f03 c950598d243fa22077898d3ae323e145f4d1e816ef971ab103780de2d801dc4e 30fffc449253111facdab4f3001a3d0e7b61a419cd530b4b2035d56f8ffa0966 0c9ec64724b35f5529fff9ccfa1185a2f0ea576723dce36120017e0bf2e23050 82f7b537df8db02711eed2f8efdc085e78c46a07fa5ad65e2b3772f3357f715a 46b375f2553e5029ccce04c62ef20cc800419b3004bc3162856786c5e0c465c1 903e026f887868d6303a1d589dc73eb64597216f23b53b789fcfe3baf05ed7cb df6d7ccac332ce905574b89bfc76d18d16abffb166443aee6e9c8e2dd3c3dbe6 d7db420fbf9e4e8d3ead3836a2691bfad08120b512ae464ce9da2025082719ed 908925bf284326d4c4a3b7e78466b2ecce191ab1ddf66e94d6ed4f53f574f4d9 7784bcd075eaa4c002b57a0804cc34d1f749fa558efa7a85f5d0078ad6850533 7865c7fa67681eb18750ce5cd2716d70229c6dd001c38124f033ca1c910f8f10 7e0c138cf6e68737bab55f28d9fb17c5fa830e473c5fa59d2b1c6846ac271b9b 5575cdec3e22ca1a68ea6cc4fd5fc025ad218d67532ee83fb6732ff6529fc9ff bcfaf958cc125adfafd97b5ee4b04e0b479aa4cbcfddbfe9546ec31e3d83385a d0a85ecc23f1ae8de7ce95ae36c2d6ba7da12602456d3a911b5115f7affb6dfe 8015011fc680eef6a623243e2cf4cb9c39e511c3125870ebc6131b4f32c6bb7f 74a4ed9f594d745f64adf5d4c9ead5bd6fe180816565730b8620a4d71bacaf1c 7c532668ee560c192f3cfa2d0bfdbc644303acc2450c7e20e75c2cc659a2ead9 65241320660da8239c0f6058873097b470f157509520cbd06c85108f7943df89 183f2cbcd7e16591d4472ad6654cf991f753cf7b95ac1fda5c2b425f32c8c372 675d77a34f8749608248e964fca69a8029c05895682b2d5dee6110b3bda64117 431391eb571ae75bf5aa45dcc38eb82cf68a50a7528ba315b2f4110beb7d2be3 832dd59f069ecdcbbb8785d054358287fa6efc25b6973c075ea14b305c0c2875 b0f50f1d881ddf1e772c045b69fd30f5268395f055285d48d2eae810aeedb224 42cceb8402d9aecd45bfdf588d63bbb4a69e329ad2f81df2148ee4f74707d086 0d66e528b593e649a4bceda32fbd1fcf50b0333d16a6663b15c7743af91c0c16 53dcd3c6f5b82419f53269a7493af9f83295bd8472731c777b35bd1874e91c0c 944594271694e79430612f9732c9ab96a1fc160e40f07f64c88009591281285d c3d400fac3e6ef8a887e2e48d37cf51c92e506ce8fd58f898aa98f4a439930d1 68d60fd3591da6c4a51cea92c485fa5745789f5557975b885d72841a08be2dc7 7fc39e73eae0e4c162e5c5f9dc869ff15a61481687a44f655e2dc84d6e67e51f 5ad392006f951fc4064d8b66417a099cca78c953a0501a50b90dfb9396a8a395 fe2cd399ce5e770be1e002560d8f3b912d34199d932231166377104f6c24241b 1644c4d249ae268d3c2393b0145ca01a9237453caacdf129b2bb843a335030b0 17a9169eb840e4f2af1f02604a4e1ab46d4a6ce4cd13d914251d465a083ebd52 bb0b2bd8605f0e3da61516938aa132866925f99df3a7eccd0851d8f4ead5de97 09be2b86c947ad2bf3258a67b5154a3170d141b5b8734a3d9f61753c9830092b f5e0582c2db209479f091f0986cc9b26294f3e6df5706ee8d071c2bc6468706d e31b2a4ae292fdc6eca90bd63dd79a1b796131ea6fc90c2dc7cefcb07aa56ada cfd3f8328afb7387999beced0868747f2110ac5858c10e8060c58a433b052902 29a96d44b87648c7a4ee7a3fd16219ceaa2accaa63361939e5f599c7f7117b38 6cf3c882dc53a69478766cc2daf7b5d21f6cfd57c05e11eff746fa53fd4a97d6 07c21ba356037d1e0742659fd32e5d060621572bf17f31b2701321f8de946270 5e7f900c2aadd7e456a84bbabe59f8b626077a7a56a59579dc8a8d1f58d8266e 0bd08eadbb9ebe4b1f6f486d08303950eff9736b4b0c2a7084d65622995a7f38 2bdb76c72962ee4f64774dd9278fe8cb356b5b756baae77d7670d83e94fdc7eb db4e9ddb33d0f380ade780551c1c745582cbcfdfe64255537bd2f47472592808 c260c04f66ce245004f9240a05ad37839f59ed573a530fead7854d9147598585 731baa2e8a3c77197d448994d1ec57e3f08b7cd08d8b8d9ff27600503370de02 e22523c39edaf5cd7f0e2abc45bbd3af7ed6dad10529e0cf141550d6e4a8c0f9 05cdf8a75153ec4a3fbd9eeb97a33c2a80756a54ce5f50aead8210f7ac185dd2 52a0415db3b914721b4685e974dcda85fb667ea1b24ff6c382ada5075fc9186f 4bd4709c13f70d0dbaa8593925a8488879c26149f64f2403e84932780779776a 12b74becda5d99221c57f0763168079a22ba4f3c6571363d8672ded8ab7c17b9 df095741d4a50dbef9135fa3e075e354057603ee04853c9811b3b3488b70d8bb 6def81b0780adac61a3a8e149efb58a698e609a8817294fcf2bc0fb79a51a74d d7eba2535176375348789be20c7ead87d29a2812ba83ef4d42314f1bb1c3a4ac a41c167aff72dfee7d9f86df2b4df1a4cc5c2d2433d5de5f8d382b4802daa345 9b5f90ad23d801bf0767c2771db146d3d081b9ee3c1de927e536bd7398ff5417 467ebb5fa1628343afdd5df49d2b0fb1583b05df76cdb22e1f984c220c76e23c d621ec44967047edf637600d8a8bbdef1aa3a419aa552939f9bb68261dc40089 7b7d2c0dc1eed23f2d09233cf15ad6e4201e25800685f9123bfd7bf069701ca6 8734b4c18a9a7997e2e16e350396b74e3620dab0ea81d84cbba9dbdbffc999e3 95ecde58eb46dd271efe69c67b6e0be603c5dac6d452f04dd76b892e9e0da0d7 b8df8f75f09e33606e4375b05428e02eec2487b22267c638ccf14a0f0153b2eb d56a34b8fa2ff46ecd2cfc7f92802dfd57c0bf673a5db8e6475585dda1c060b0 b877e3c58b1c541e8201360161d045a3927cce49cc6a339894a9acdeff26a459 f4a2593310245b0d46d42862fbb696a3e67e276661f25b9c3f36a6b68c2495cf 91fbfd4aceaf39249f47283b9825ae4acdf0fc74b262bad2d3df54a22d72073b 0dfe7351627757709f06ecc05374b491a0f78459504ecb9470d4d841a9760628 5b8b836611a801490f591ee616dd21e8d5a96d604d2c8de2c7c501cf19fa7b17 2937f6cdbec94a649587dcd69d8d9497fbad78abd811b693f5aebb6b834e41b5 44678a9b463b4a198320c8811d0269195f952b7b717e00afd518f20655fcac89 e9d1375930ee2faa04fba739dcc7cbdb8e5c2a6ee01b8c9e5da99dc5618a16e0 2c87e5c0b7554a026698aab6e8c1116b6943e00696c6fdeacdc7f266295b0a2e c9c568a5ff0e205f3c3709750b94c8dd871a8af789e1da1a7850dd4fdc0c0e2f 1a2fe67632740499a6c2178038c828737633d1beb6be9fd9de57cc35175db7d9 6f4dca7e88924638717bd1ef5a41f24f8d89bb7d5b85b8c2da2948176a4b31e6 44de434283570dbc57f42ac69ab281b5af48f89e59c11001c1f822e67211a56d ee454b2a7cfd79dd4ab7b1692efcc011c9262d553d7cb13158e8946fdc1ba4a8 58345549249b5fbbf13ac9163ab000d66e36ae53c8bb1fd69c3c24fcadc9f8c0 b531a0149b69bdd3854d6ebbfb1fa225ea697d02450e6bd7168664ed07c1ffe9 8c5582ad994029df4bdaf4fa2f411725486cbc599cd71974214461fb072f3ae1 2cf68fb73885671ba1a0891eb6608d70ae170c427d588b38230b1b07e7298adf 498101a83536b60f85adea9dcc4d4e9bb45a402bb06d99cac86e0795efc71b5a 6c6ef7f500181fa185cec705e9d67ae89ca85b720f1560ae779c873359dd0c9e aae6aacf5237ce041b9af54300b6e76a69528cd4014548b26af9b171fc44a225 d45e78f38aacb5bf27f2f528db29a05cb1d9ec09e266a7335fe0ee18b59569e3 b505c43af13cb17e52820eaa45670162ea896c806fa7d46a774eca6367220e02 bddb7234f0fee5f97f9060b88ebc99e7e0b8bcb02210d375b4bbe694f4f4ff47 779a5523f06bb1fd21f497d993db96414ba5025709c448a58a6e7e51f1657268 f248d54f40616650b24c7ed4f9b7abb95f57b5d221ae2a5e2d143d9415537b44 df3bb102887628a9a8a5208ebece0fb2e37430951c7b25c14e70bf9df94e28a5 6d7164819cc810f3b2b069ca8d80059115fe2b036feb3c2d0b1b30784bc31de5 251ad937a323d419b5fe9f17baaa3a1c71b5ec2519e31db52069f6dbcfca4eb3 e5bb458a081b733d470b6ffcce993f35735256cd053edc8cd07af0d0edbf29c1 e6d25485bbb66f6a16c66cb4f0b7766d448b39bddaa4271dd99eefcb15ce9cd1 0410221c47777be26ca0a1b8421ed159fbe958c56aa99d8a62bc84a3ce896050 0e2dd7356c3166017fa2f3528521c1815333fd3f47ca5cb812450b22db266526 1772325820e9563209040d73c71ae8c8203622c72902c139a99e8bbbc3172112 5cdb573b5a60a8a8eae3608f08034b047882176a93892b3360a35ca57756b911 d617d0bc4cbcdfca43f1a879449112feab7901f20db006189948c45e108aea48 91e6adf40b9d1f66e6bdd025878ac558fd6c217f55e668b8ca147bb7886b2c43 c86c29deb4405e285f62582dfb8ebbbd87f7c7f9b64b028913dda664bed4d6b1 c1ce535452f7f2a0d02a459cbb548505b9d3f253c0b613b0750c3146ca29c674 b96e2fe4d7ff9839f27e414034ff69f7ae6f453dbe57c3cac978937e5855aba8 cfadc0adb175c4e5a961dfe73b570ee8a7b6c825ad721f50da8f21539e3499e1 680b1fc414654bb6dc7b31b778794e876a7a1660d3996277c97eb092e8b98b60 f4311a006376538f012e60b89dccca7c924059a3aa5d1db5a92c75ef733a0244 04a81b3d0cbf27510248ec325bfb498f9283ee5ca0820273858ffe571636ed2b 5a9da76badcf59fbb3ed03123f33c94e419b9794ce014a73dadafa590fe51683 7efef9b902428b88c0a68337c9816cbc340925f08444174fe582f6989b89d790 5696d5f83ccbf61b2e8fe34ae010cb902aef07de3b598f620bdf85bd44ca1b3c 8b2e16adadf24378421f10e78478060dccafef7130b0801968bf8e6892e3d324 a307c440566d8413d148981059cf05778bdc9d68eb1aa86598c9f1612c518a75 342ed87cd90005573602a4c96f84d2d1a2e436bdf133c02c1b48ce90a5dacc1c 5e06ad6eac3945549c4ebcc1aca9b309b619b2336b99077020341d0c81bb11d4 67f5c97ba80341fc0fb542068b883da5d2cd5fb780f5bfb22590e1da5e0f51c6 9a1186c962d26847b0cc2d563c101c9e640becd754a6bad70d13e48a8eac8880 7bbf38ae23ee7ef5da88d45a5ccfdd69cd16d06fcd3a7788700b671b8d1ed1eb 49e224fd029618d40f2b2d2084c28715c78668d8257fdaea09462e9c20ca3e8c 2718fdd6948a6ef90bfafcd136544797d3db9ae51bdad43a2bafa4423177abaa 0463a1d97fbc1e64b469a2276ea82cb696c3d1ec32ec56219c335876db2f6e82 348b86e6d1928d8d8c6bdc7d7e89d5287ce0300a32c24d7d72ef354dd638e084 450aba0e4ee992477bdae8844f700a89d03e2de885b01dd0c1940d42e5a6d164 47e31449b132f93e91ae8dea7ab5b3c31cbae9775e4be242017eaeaeba211a8f c70541d4e7f4ca10dfc38ed322e6da56e0a03ec0de291a88a2bcd96fc01f52a9 3ba9bc1206fb82b98bb86d1c77d807f7b852b1f5778a69b362a8e8e9c5c2c6b9 3c78f4a702bf5fde5fa3f1313f058aadebb6ad2f9772c7c7cbd08b1685d08cec fc12fb52f3567b674f6eda30949a9cd163f7190184fc1a3b27f0f1a69e39512f d56175716e33c8316d5fab45050fc413a50373e17669b9290e245291f9f07b95 2269e7e91d5859dd0feae55aae5f597cac9c32228cba5632c5c642a1a62be4b0 83ff3f30b6c05f92394167e350dccf45d61ab9b031d565801fbb6bcf66c40f22 f2334343e4c39f7755723fbd4d21a3529130bc0c5f89ed583497aab0d303a116 fcd47db62e7e8c0d68a4b89535ff060e6f41cac899eba85724d841f99c93f427 0762772fa6356f8361822d759e292bcb9532323c4d302eec337f9750b87a0e33 297ceca56447db08ca3430629a9d33e64b5e2579f6812d9491a77d5f75f96212 937441aa375265af6ed847d0b1517789d5f2718b7f3fa44ef854a2aacffe7f84 8d35b89366f1ec0cf30f1dd54025e90c1739b4e243b11a0b5cfa8905aa11781a 072b9b711f0f1595221e655a868ffcae766d56b395c444bafb13276ed6874820 c52986ee61aebb2558ef18e9408bd34d8a5c4c2a06919da7ca733b4b3cc8c9de eb3e969d64e265d75026ba35b2a0f95cd0373fe56dfb02a73836f46378c72f1a fb9d7ae59677eaeaf55306c39e2801d757b83e2090337d4373cbd76038bcb61b a26b0d8a8e5cb05c04daaae6c8813e7d2f45f5fd8dd10f696d66169bdc648aaa ecf3b1df3b5f1ad84f69db46e661703d3ab89e25b11366e74ac7a5a1b1570ac4 0a16a35865b057f75e9f036b6970fd366bf90a65181129faffa940fcdd33fbdc 72daa6bdd852810d07ba9a666a041c4b9e299da11501eda0181d35eee2aaa601 d14bc542428f8b958f6225f7c24968b44d1b489defbf6dc7611eae15b659abd3 7502cc94422f5f6c8c364366e4dd0992f9fff62a1f71eebd59b715dd5462ba30 de7bdf74b0dd2cbff670fa0168c456a59f738087e7e1bb02b02588c996b6f84a 4884476411f40a55717c052f0667884ea0bf767bc5a3aa29b84853b55ddb99ec 072a55ee751bb0a2171e9cf983f661f02595865c49e1c5fb11eeeddde9dfcdc8 f446fb7704232ecacf0797b892fa6ef37e3a10494e4d00cac3b4b86ce3055cbf 3d15ff20cc36b4125cd8ff980a995af2effd9ffb15359a7c58fcc6be2318193f 8011dc5c2ec4e2496f8761c0638a317ae01a43b9194eb0ebc0cea4fe57d34def 6a21829457936409e025e6bcfddc80f39f89fd00ae4a6387e3a1c063954cd09d 3a46f9c791a5a48c343e411caaccdc03aec37e05c8c9a59f83c8d9b1b979261b 0535190ca822c5b35e8f011040e17c581a8540120014a439b5c6e36aa774dbe2 d44dd341b231ed818044cf3c72c12d2798b873cb5568f00cff1b0adaaf731862 19df2975eea040870a12f564e181e6fbb3f3142ada7893ac78d382fc8ba93a73 49ce7be6e9aa47d19036be9062bc15cf2bced0eebf2fd40a7a9a873527501dbe 50de2b82bd26c06ab0d2b7c34b7ce9c5d8408e54ad98f11a063ac14429124355 f28140dbcdcd485ad0652ac46c367b5768f7a35349aa7916a064b3e64c66b855 1d6aa656bd513e5be907edb988fa281a6f5065f6498f97b02faffe2ba1ddcc98 43c84025675a7c67c17b12af3cee45979ba37136ca837adc6f26f1579f2dda53 90a3c6aa78e26622ad5ad8d0cda7257a9e542af625a84cc550a214a0604b57fd 481a135057a0d7811ecc10e8470a926a227d942d53db45828557e83b04e9f241 ff9f80f6884a7bceb7e2bd803d18e89604ad2bd3d24fe72f76a523b0411d53bc 4fdd03a068212facb573c4d458756b928afa5f5892602ca12104b5a5f0d69375 7c3a5b86e2a64c3eab2397e68e3439ac3f1d56d3ab69bac91088934959c8ac06 a62524c3113cef9a5103112ea4120f8f3a3193e5358442ba3d5444191fd9ecca a0468c5bd8ffb970a18bc62103812991073f2f634fd58dc7c27c19f8e67c8c70 5c9086ed8e8e569d3d3dc4e423bad045a29d57886da6a151c7a1c2eaf3309b15 1babfcf5d305945d5ffa185dad36f41274fb0bff9df2d36d44a92158905498b9 156db251f56540c8daa475bcb973c654bb3511e39ef715161e7c7dbccc179452 cf95ec4d59f98cb8d15a41c9f56b1236bd6c33369974c38a6bb16e4c5d7d9e36 e55f44252b5c8af5d625d0b24aa85adf6c0fef93d21ba17927c9333d72f90d9b 366555eb891710cdb2bd662749fdff89184bd4bb397ce3745fe4aef6cfeb0aef b2caa5d4d0a616747b4738635b04e6945c691171313e06be14e6d46958b6da24 4230557e472ea07fddc5325a572de292a1a58f3322aeded33a028700c8710e6f 42517d6ab674aeff330451ab071dbb05bce00db964dfa4a83b38577e80bafb5d 9e130ee02ded4fef2b67ce1f9235244b3a8a3fc6492795968610765b2a327f23 e4fd4e0d8f017813ee984aa43aff3862063c0bd738d6f450aa53b513a2c8ed47 25d13e03e3a78373390eb74759f435679cb6229f126d10b8d0b31c60e4edd914 4793aa810ff29bf4aab4421079581d760c23b9b3b19fcf274eae1db62380e8b0 dd097773cfd3b557b91999349a912fa1e4e300c06312cf347e1e4a14ba5730a4 fe0405e11575d19276b93b81bec00455b635a1c09c4c31ceb2abd660d2b550cb 5667e18c302420495b2cc2aee615d428a4babffdc941e514426d69f09f0f6a5d b36b34e0a6f25531307fa0cb54d17c7937c75ccc591e6f19f5d35d5dbf0edbfd d7ce198236054882d0d43c02144a602fae78b968d6a59038819c34a4aff9a54e bb9f10b41c38f62e86b9feb16b3c7f9df8a9f963b6dfa5d89183442aa199326b 02bcb0ccdd132c55952bf88088b9b3fade6dc067a5bdfb454a94e77faec1d944 2400959a62cacf5c0458fd5fa3b2abe4a82989ce9b2128b04a403e7e7cd03e8d 6e4c094ad6e230546b5c8e29f71c9ef548ba45d029af92c83441b426a3406e64 b139d5ec75e90d93be1ff2a2b9f2baa26bdc69966a789fa2f37d752513b76237 c43c172ffd93e4559f60f796b65464879cfe791cf2d142ddeabe6deed472b2c2 b85b60a32f57034f376bea19a233de40647b0947a60a5787c961fe2354a7d564 f578f5110239045b472eb3ac2dc595a4ce7c4ca5f2d8bc92d60d85b04382edc7 02d3d57b09ef8ff2697b987a16e2266e71af802ac9e3f1a9a662b91f949d2501 42beb6197cd91d9b4f4ee2faba5360b9827e6ec7d46a4305a8673b923c7a0a89 3bc413ae15ee6cdd9a9fa5b18ec88ab0d1403280c462a720b428369488872613 3b0393316a6eb6a7d011a7f2285d7d7431553e7c431afcf6226a5d6a6a72c8e3 ff10f72df4cab8d51adcc5a1050ccba8847d8ebf12f369a37afbcd81c77bf5c1 73b5d2e0fa6a1a20e7754d4713d50f3fa8594f6137148cca7ae72912b3932923 53533423ab0907f250ef64f55ef84348d61d2268e2ab9c0f48855d71309eed5f 55380631885db8ced90c23ed7363ac26fbc02e2ea89469aa899f616089503be2 7796e51be6945ecfca740211388c7164c2923d59479bcbeb1cd3c43df916fa58 c3441f617f4f3568097b60e1b563cc9598440505b03c100f7d03533b36077a29 6c85f64916d55ccdf8f1517f6f06d9ab7ed8f4780b722dad14c38a42f435d113 29ec83d68fa94928834bee3a0c700ed78e8c6ca5829087af79f1a56fd146174c f99ebe2efc019ff4f97fbd12d7276afdecb790793ca119b2f0be07b57b804e48 9e6bee084cde5cadfd82c6585e084b871bdd38e288c97e89ed30f60a5f3844a5 3c23a041489a8fe9856aa93affcfa638dfe9a595ed91541b400f6cd66869ced0 d79fe1fe13233f9fae365f503d92dbd82089da23baaf1696fee4bdc1b326be1c 9c281a188360af4eb0437b6e09f56f4bd51b540df8d76b121308ccde1c1f8645 da03eeb6c5f3968cb1bdc2ea12ec3f6f49b5c077448a2c15c0d273fe1188ca7f 55e18d5abcc6b79dcdcbc7c5bb06a75ac6a15c2deef7a50e0a5c483bb9239b58 93f29b343d8be7242d8a0f8278c6ecb538b1bf021b2a29e33a7d3348467464d6 62a7d1bef1ea36e50eb9c1c548c563f832f0388b0bd6d579327de8c6acfab8d4 0b9af08e997456ab4946fbf091435e77b49f96c4c723889974850d054dbd9ed9 1b812fc7242710fd68714b36c107c776f9c8395a832d490b23d1bb0a2a83a08c 95adb3064af2c0cf02dcdb2c86df8e3066292564f7c91c26db87dcbf1263bec0 3e3f7f11ae31569a1b32d93164a3f11c58148aa6951d13c0df0c072a3c8f557d 965aecd9d7f02db7db34ee37a0dc6ee944c5ee7644a170a89a2279bf7ef0a240 11564c8f30027e950abfa8b7ddf58d3a47fbff9429fd2d82740ad335199e8c89 f5c6baa1b6e60524dc967b19ed8dd3fa8a20eba26493d2c862496ae3456fb692 333e15418156eeec0038692d29411b4410bd96ac794b79c9b57c4e88e458f354 8d325ec2d8c0fd7d52ca2b448242d6361e955ec109b18bd1d5a72bd912ab3118 3610dbac45e78ba254e6b1145834bac13c4f841939609b3a3d29f08c9fc0d5ee 4d3417ffdaf36a14135e3ee4a3cee956d6482f66d24db97ff27348b1cde9043f bf4840d5ab338db31fe77b820d4ead2b08968ec4ca490ad70312495eefeec624 b79a81aeaa9a6705885b7b85fd56a44965485529fb1ba8357eac75058816aaef f5f63c12749009b0a3374474f1d4be7277a637b4ca300fda3dc7d08d86670eb6 85d84ac3db999e8c45169fe1b9d6b3db0f8017d950a70713ae1bd2e6367314da 577f2f14349160eea931f49feada5637fab98708a84fee1498f7bef607c948a9 26fbbba55e2910010a91e0e39e9fe09943483671b2ae0e2041c5f4189e8257a8 793f40268d547fa3b451c7f61559a38d6f4ac60b657decce905f188569c93c3b 66d3059e74ceada455e8f15e3608febd0a9b6a992c16d26fcaf9869188c131d6 8baac78817673a2efe78c504017f0efb00d18ff44d2b065a82c5fe8a188f2b8c a045a10d6ddf1979f4d70978b9e8e14fb3f69840c5a257d08497024ec67bbfd4 08277c1bfb461f2ee5a636320e828d62b6d8be423809f1f27b13382227a754c1 e32fd3b7a375cdefd3e573f27f3f5e950c7158c2ab94bde13b2f94fe71236ac1 c26b720435763aa895506bb9024e7ffc0598477b46825e828ea8b9449ce081e1 1e1775d9ec93656f402ebd8a07160deb415db6d3e2780ebbfb7d332db4b7d822 ae83234a02a9904e2492e7aabe744bc9fd75b73edc0e30f2e76dcb772219e8d0 0cfa8ad303e284abb2bad49eeb3ed0553ece8c816e0a9650eab8079908d909ad 63f58f8034eb40bee8b5c55d9f5882914819d830f4a611c158ba3c558b6192b8 f895da07b12ed98245826b1b2f83c663a774aebc4e1107fd50a10cb6448783d0 e0663dc967dde0219c84f16a907bbdb861661d96f2378ac8464f8523a85df10f a23ae8c9f9526132ce41ec5c2f4501e1401d23e2b28a43b6e1905ed5352aed40 34ee1be5baa9968c6312b115c00d2361123b8530374860133820ffde5bbcc5eb 37967bb806bc3648e421fcc9481f49574c2c1c1ffd17c0a126683efb7343ea55 cd36c440cc518007ab96283acded7ca1636b1bca3b3aa29b47982a66c064a019 3ca40df6be1ad73c7e31e979fe162935b14d7a77d1241dddbce9d16946482481 c337c2b2b772a11c43e3f1761ae17220ee54acc998ba96961425a18051a0c0c2 c0971174290aeeae2081ff1c50000fcc967d0622c27aabf8ea7ecbe0e33d1b15 51ad17404b75f017be0d051bb73dea2b8b4805e68e995280873e7d824528486f 878bfac43de3b735936881781b805f2d6dc2ae6e2e34c602d0f73a09ceaa50b8 3f38898a76ac6c0a91d0c699defe6b49b8bb4058d05999afde1450ba5a54aaba 34c52e1af58a09b085a9b4504d71561a746ef950f486c618a19edbde27ae6e05 b5d77bb722f2d661e82a8f70ee19487fc2b6844308370aec3a28d40d3e8a3cb3 1352845e46396b4d2419ee29afae5272a44e168cbad73cb98ba270c2adb01c92 6f1f6cd0aad9d6ebabf7becce4385fa933a3ee7d2cffaf04355858f3312b4495 b80298d7bad56002c296953f4fe18bf6b29ff4a47525cd0948d2e40e50b4f800 6d3b37a0eacb2b89207936c6997e133dc99e70ab32f46e0eb7e7f0ade22d9f16 42bcbdd59d0b67151099003cd6dbaf9807c23208ed4c057dee0ddfba037c927f dca7f009d90b08f4c5051fe7c43f9d1cf3e4c152d6e261876517087f5705a09e 605533360756ff45515c55b25d904967e28b74e534b51f1f18d106959ebbd9b8 73cf97fbce379ba50198afe82b93db1bb3a23f3afe2d546dc2dd482f8c14a76a c74c34163d2cad590d476987ea13507d53990f5d6fa12849a84d7ef6ac645778 2128432c5d39f018998f22addbb1608094717b2fd01149cbf8c567d95f02852f 5c29a69713f1c358d83420f4b02bd741b134a8f705afb31298ae6d61a679f074 695c2e6a262b20b6f4eed444fd1f37687b723eb420d1cb15ff02a498309b05e8 24304bf567585faf611e9284c1034e25512325c75bc97e70cfba0d4f199e079d 12a4fbe964c36ab2a6ae975f4fe68c7ff61272f810afe817f053f9c6a4f184ed d1f0c48dce07ade610c82ac25f8320976247f43ea18af7ac9f19903025a47047 23a9a49a54d9f092d4a453410cf492313678126a21a25e04e0822fb662566d51 428c5d5ca1f39bcd7452270c753bf87752ca08b622ddf38d6eca2f8ae3733d74 bdff39b6df9c4f87b0ef573affa92c40bcdc702ec1c2854d20c03b948e2be718 701388946b4c23d1c294bd1d1541e27eeb978c7dd9ad05567abe1bd66fca113c d657ea7a28baadbd447b77b23f863a512ebf5b256053cd8cefecac63dc5d4fc3 1b8b27120420fbfc6a0398156ad44e80768c70824d82e49ca20fd5720d4a78ed e55730c58ff778b14592d0127509679591a4253403e374e1767e17455260d34f 09a665db912e33344f4ac8d256948349baa549a87b47a8b31553fb68e29579be 99ebcc1b00e1b26a7917e4ca0251c48865d499be8910ca78af6b19bc7b39b33b 22732edcbc6539d3668d1bdf2d2514b0d6b96c076599beda71b3df88a55e3de7 fbe52957064c6366059ed4bf0a19f3d1eaf9bc01034391fbe9127a3a47280407 d4a8dd12bef7c2e9b3615953ac83253f8e73a769fdff3d8fed8c2881c5fffaca 395ee0e45f05b2612654089fb0809d10025ea8b1e1c2ae015d1886c575f4b5bf 13d9ab616717f7579db4d69a32259567da9a00125160635ff8c7296b53d0afc3 27873b8378d23ab487cfd0b2f3940b0c8ccbd696f1d6e18f1fcc7bac5f8730fe 26b0a9651ccf8bef00f426a3d16b634ff1edb34084e78ea788bea2af4b4d0eff 011dcf5a74f5d7e7ef33c12ea6254541a361ffc3c5646e377e6f0c9f377654d1 82bda0efe797dda4b30ba53a413b5e99445b5169d2ac2b3b5ea4a82ef8ecb71d 99b147fdd00196ae756558d3aeffd564ac13dce2b6f20266f113a3fe09b925e0 45d831020da5dbf3c3b7663dda400fb846fabb670fea83b72a9ebc06d1830262 fbd400cf2f807912cd559f6577c71b25de621b2e2171a209cdbc121d057b3ded 46e40e710f113f70a98faf17a0be4f1c0430221cc55f1d3c96d5800af92db0a3 9f715557b4bfd0f34342f1710dfe1973dc8f7b8c17aec77f74ee6eb7e2dc93aa 9184058a02c614022f3040564007da4a9a106e656267acb415fb59aed881c57b bdc40c5a2c1c62d77871f9069bd98aa30ec894ad85be686bc146f0d62bf20fa4 4f1974aba413d66696df61dd6bbf5b9e4e059c19ee4d07bdb322fb35157e7753 6ddfa988581fac036dd4c95b6b6ef41010e06bd692e7f48cb336b5c31bdf55d6 fec2e9d803a9fa6a29c1ddd75b81bfaa37e0d08ca796f9abd4ea004498a8fad2 d9ba1b570d16ff33706df51b3f92e0c45e5071ed11be711ea163b418d2758bfc 06f4b8b321fa423de5288092423383b6769839755d8d821cd7fc200c90738ba5 08f3155242723822970ea15a61ca0f136655f1acba7c5b29fbeef72d15b1735f 7d589a2762762b7f603115831bb763e2417f26098720cbb71dba177bf1dd9c29 0bfcd4466fc67fded3b4feab1d2edc400e44e57cbeb0ca28a1e598ec4ff04fd9 d3115bf34e5df668d92643c6000b2dbcb40296b701d1b39d419147ecef29d69b d30ba4dc70f4215478a5cc2690d0d8680f8a41ce3120889be93be7a27b656a84 b8f7fb3d13c02d26b27664670aee0715bc7a036ddf8606cb6d09f452781f913c aaa2cc92376f9fc121b14e0285e271e2e437dcada997d6eede8a781d7e3253cc 3df77277d765f35b061dce4142bcb0920899e16bdfb7b20cfdf34a5f717b8183 4a23f54e812bccfdcfb6ea6c706c5b89a3e7a3de499ab5982da214b157a57e26 94283f0af62704f47a9672c367ef5088892053b90c9c3e06f57f72990d2d386c bf4b8b144f75a989869eb84a014855dc8d32fcb816eca42216be542eadfc9f7b 10731bfbb5a0232c1aeba22912d6b2a265de09b0e5c3c54a56e7ba60b901bc2a 25db0d3ed80d538398b1957912b70bc12339a063c5d408c561d2815fb0c3ccd4 d9d8494bf21f16dbd8eae919d0d5a7e29486402abb863832373e5d5c3d529bd2 7e030f711acd73e5b189e67a745fd82ed2f30e6d923151272174c7f574af99df 6cf3677e288242b42eae2d540d749f30f734fab2505b2a4f434f520faf765b6a 640e9625bcb3cb6c9bae6be91f9bad4d89090c1ba3ecaaf038af30d60e362f53 90cf0e0a4118e5aa3d95bb4ca5d0042d2f941fe1313a1df1f54c23f6202da485 1f5c469a490a7748516262fea539a4556b9f492543fef4825a21d030f9425917 0c75deca266d36e5b5e4689c1ceca2c3d32301adbcfaa958feb04ef9b7df6566 396fce961249583a2872c32d87f5710864f41ed7e6071e1935aefce1d106a060 57a5950a8078677867da3e96bd4ebbb12e042b0054d46926d2d9d3f633246d4f b82ae28952a327d41bb91710b46d47a508e9dced351e04c8181a579d373539e8 ac515ad65154bcc6e807818cfc91cf49f511ff5bee76279538ecb71a866ed2bd 3054cdb2631fad0244e7a83ae34b97d3208899e1302febae8b78abbbf50e4441 7b6e21db768f02b185e85a09808ec5f36eaa14acdb6fc83d45b9d3e6d06171b5 3413fb84d6c48bdf1b9a5662cf9a799127bd3c7221c310d6b49f0446fa00bd2f 0a59d3c7e20321d64f797edc00c4917bdf2519d07122dc52c88a4c131005842d 6e10bd85898ae491e2aae74d8d6093bb70a793a2ad6375bf534ff450e4a02e42 38d9d05f1736803faf4be792f51e17583fd568201acf54de28fb8fa61f2cdf8f 301719437d7132af74ebcb5836db5f5a757a516695cc2f5da0fe3585dc735411 af8473ee7eb7c60423fc27fd630cb662bccbd95493e1bb594d3a339b1435e29f cf213fbd40b74a9fbfdb53f79ea913d0d344d4f0e9657be2f239b8de200b0c9e 5ac9dfa7381c5f099b39a70ad697c8545446a291abd3209e9834184d8283f0e0 59f44edea64a7c4d6b8f90cf4f43a855888cc30b8dac588b9cd05f8907419559 873efcddfad999a718d66bb6bc6e67b7655b5af313c62452fab1a5aacab225d7 be2bcdd9cdb691e02aaec2888d5409f3a862e92a436e8f7d6b6fa4121e1ed7ff d273b7377f454654e3314e83b7bf40956b5dcfb79cdeea054b096f9f68b3ee1f 1c7e458a884f750694b386b689fc29c5d73789f9c95c9035ca73bbf4d1d14c41 43e73790e24c7e777e76f039c4eae177e624526eeffe49387c9dada932485e7c 9797aa708d299457b161ef395f2ee27ebad1baab063d4b549c45e40ee6ee8d27 c140e7ab478b97669425ee97da6415aa7561e35be22de4a08156af9c33d3f197 e424f112648c6b9bbdf8425d5bcf8cbc4b00c8d0628c11833f70fe24a5ad3efb 0e11e043cb64cd121dc40b4fa1aa5f14020880b119c7a7f776b1eb5bc0290f7e 15cd96973f8d37c19e16ef79c028431a4d7a64a1fa90bbd4470ebc7ebd8205e9 0814734aed822909ebf42c531bc5e7a7454c6dd74b9aedfd56df18c5c8992ba4 164b91cb5df1e683aac3fbeee4d697ea94060d6c4195bb88f749bfd2b9c7258b cd0555cdf3f689c0e44e8010f5d4d89ad9368f2bd0e173da613b84361b89ff01 e4631cf56f439aa47487adedcc3b304edbce976a1cb8fb2a8663457abd4734ad d05ed3388721e47728dd8416d7729767893e7a05a90f3efda2956a2a355ed21a c7bfb4a60b958be58aa4fa41d3865c014f757fd2d921e986406f70d2c3c67e6d 0a2a388a3fea667b291a12fe53c28fd12031b3ee94eaf7cb34c7627a5cb22bb3 86849133edae65d10eea68de84c907602252743475563ab82caa4f621e4c257f 009acacfac7bba155e04da90bf9140f7bbfa9fbcdfd6f8ffd00709eace830515 f7daa7d44d031da7f19a9fc9e7f1c9a5bea669c647df1257a182e3a0924d265a b8a16663e87cd9b5516ed60b16c90f11cccfb86c39d1a97487729a5cf38a2952 096cc96b1a90b06e1b6665c98e302b3e09ee66a18a31161b290131a02b9e23f8 ba546a75ca5160eb51500a1272923629ac0832371c5b4bc18baf27a0ff257929 402427f3d866044c53941f650405c2b052f80733058ed16575b2dd37b49f2c69 a13ff0299fe2ace1f7381373c7aa90e92a1d0be8e778ce62df117170fb4e6101 de75cbed28ab762e3fe99c9b19d28b9d9237620ce25ae7d266ea2e3b04505237 085ee203294cc8748309d75ac0a6b1e339b4678c5ded1776f72469fc09cf1141 254f0e7c8060e33905da896047b8650550a980441eafc4fdc62c83fe3f687649 c969e7ba634f02b882916ba00bdf928a68567c67b45a23ed1d5669b77482db84 8885aac2e705c53951fdbe021debd247eaa53be12a2282b18418bbd82e77cf65 f7f669b887415443bb0de7c2c81a6ca7d4d6e6bdc44a8ace61ba10506edfd9dd 68f1e2a157dbf342492135b8f265c1555fbc8803326a872683c972fae527ffd3 c31967321d40007876ad044a200af29a7d3d2a396666255c0033821a73aa84cd a3c2cfbbafbc6ad96c122f0ed2c620b16fa064b4c6aca4982299456506762f5c cf7044c4f835420ce798b6fe57de13a1011d4877ccf263ca7249d772bc6d43aa 8e629adf9c9f0a7e22ec0a5c1b8d440c7b6ddeaa000fc9eecf03f5823ffa353d 9be462ecfa670c32b2441933f267794f6e6729b05b56bca54624398aa15fe9e9 6bf2f9662613e96aa16750b0841be0a61ae5524ff355ea169aa84b1eeeec328f d719b6911ea4e986f26f551c4dcfa860a67268264c32cddb5abf65166a7a8277 8b84f970fbad04244b66c176617041fed9c361bda35a8e92220d86c17453e8a1 01cce26443daffded60bd71bfd3ed390e503cfe21b5613e9ce6c0264cdaf0f97 14d50ac91b8028cd4eb8612fa0e2c33926ae819ccccc7e98ea09143fcd46c0f4 112b2c4971296dc687823fe16ba9b073aab209b35c251f95edfe61480c74d16c 0b41af4a3d8b2b9a368fe626344bd4f78e15bbe5023faf010a7b5c7329e2bff1 6d754fdafee49ba6094d004da8e6ecc7e40ea03c8571822337b9b4434a0e2385 00805ba45b92370503ae8d1f1202ad6982e6bd53646b606e02e4b7cd852f18e0 a4ba249f11318c276e21bdc7abc65beeb52097c390d074b2aec4036297867a0d d4d78d5f3339cb73261b62599c11aad84bff22e72fa5e94f49640967f3f8c784 311137d8a9fc565b04fe31899e00c5a17610852fef8bfca5946f1ab0f246fc49 cec0d0942a1377a2d2a86a70a06f56878260794789675d9785896380b0e21357 8912097b7942bccde588f8adb01b74adda432a6de2152aa27effad80cbc9e784 bf1fb45f87c16391af7e313f7a3a7d18e6632e2d1662d4fa267639fdc2dffcc0 97b58c21fa441c30e7e761e6948c8420feeef0517702da4ed5d18d8fca170167 de3b43204875d415005864cce9c9b1458317e25c2f08fac9fd77ef6f948d25e5 82b63eb901769f977e5b8e6566b0983ca82d8fe8c848bcfe5a72831645d55ee5 be73dacc9a53538f4cceafeea9660be479224aa2eebd5b02f5e02e96e983a53d 42476aebf2445483d9506551866fc4d2b8cd74ea91bb729e39d54ec5adf74791 d5421ed6b4daf3b1cbe75422e171ff9b36d4374795443e6a7e91b8a10b3497b3 8400d185d8aa49e3efad234ea7c8b7d89b9997ab65a0eff6509ae017c714458f a7dd613979ab9b0cb9a2c238dcfc0c1e712e8252fbf25b86d34b25d46d660f44 cca88301e1e36783b7769b1c066310fa53867af66c530d13c1be83dd7766cd94 bd44eeb31969c55840ccbdc092eeb565d5185e6443b6bea99dd348f69561363a ee236107bfa18dad03d97eb46c8daa8b5b3eb562ee8e4fb2c1e9c6fc2c8e02db 355bf2a64afdba149cbebcd5e90a7db74aa2d1e6508c6c0f94f36825677dc47c 211617b68bc7317d8201bad70c42a0d0873cb6455320ee9c6f8b02ac551ef710 eba95eeb75affcb2278d3975799c47c8255c29d4af316ee8ea33c989fd3735ce 799621f5719558dbdc88df5bd185830fc1f33648341f09bb60e01d4cdb5b4cd2 b0de1ff83f5bd52948778c9a4905a8e50a6bd5a253756458ed1f14ea811a3935 2c7de923add54c6b2cd6e66ed529def9398f35ed92a37ec65de1d6722f8d4678 b4454038b3afa2953d46519d849fd0a4688202602cc93f39b25da6fa30aa6f42 da8fc945a65dc61872a86d044d2217b320f2aa7565a21d9486f51c3965068a19 698a812e25389e19507a6b3d83f438e97fd69c9403a3a8984547b6bc596e4708 49ae707a9419f3ffc5d933ea28e0964936e750f85599fd5ffff67bbd964df4fa d76fae9113c9ab3e6a54efaa5bc634b65b079e38d9b85e843d44b5f0047abc62 bd0f73ee69479f2a2d7f6294461cb06bb451189ee1da1efe332b62c29d07616c 015a88223e54a6f500547441698578b7ed239f77fa31331d0826ee01f2d43b02 180d765fbd63e047eb9d0b8864c64a65ff34ea591dd51c72e9e826a479c46a20 2427762456a52c9967ed189956ebff36a3d6112fcc7361ce12606510f3085a44 69f4baa82a7920d7a9bdaae1eba737ab93b3018e853f3e57f8c4f309b0721a5a c29ea0ee72611bba18e72fbca63775d0934e8430c5e712a2e9ef9f6979f3d4f7 6739cc3bd517fdebbb6bb161fcf530e6df92fec6712260e44df9980403735d12 9172c854c1e6f566d8087bacbc53442bd7cce75e9bebd647daa4312ee0303adc 730153bc8fa8cdf7b9691fa92f9af0e761a8e3839f4aa6af841444c8a71e60d5 4e49873837761d4ce756344bac71e07ec4e6c902ae0652841174a84e162fd3cf ae4d2d9ef2410bd9d7b3864e5a1a3dacff44827c0dcb18f1f8191c0d2e9ccb43 968543a00a3a7778cd4a2983534f1c330311fc6effee6bf08098614b2c749c7d 5bd3bd5b4d9f0b80c8bbeea4bf6eea7acc5c53aca9f98d0a8bf8a4ea82519485 69bd9a05dbcaf09b6b797a22c5c091a2805d8a6ec682f13af4482ed44dde0755 93865a4219b0b9f91078ae9e7137e3b5ef88f2a5dc4a75e9e1f4334855f643a1 8ec3e07b7f21fc98940285a71ee0ce24f8928394b3f6a98c97807ede6276fbec 9b117f97150b4946a9b9cc12e07ce9b4591c05cf9f16a37f4ef20d84a891c06b 3b262980685e3de9fd1b95b76ba24296aa8da6efe017d0ece6970ff0fb36ead9 c42cae069f4ffdc0f86040f41060067cb1a54fc49444a0ea88b2cc0bd6cf7c12 30077d8c3808ac833b7ff94bcd1a688788cef57aa1a1725342d2cfbd4426d25c 6f78e4a0b573431035aa4b4889417eb9a8bd09b347314533bb93fae7aa8d7ab7 450022ebf19f523f64a7c2301890837dfcf76de273fa38c7abad9b61c0a51e41 0454e26dab4857b61a2a66294c5a310589e0c4f46113538e5aef5ac63cf3dc4e 5a39f8bd076140ce69da014c6effc6096a8103df4f8ab7c79f48445a714bda9f 951dd228d92733b8ed0679c0aae9fe311642ef05c85e36eed82b290ad42a744a 116aaf5fc061b925daa0dd9eace2dea166f9231230db0b6550bf2ae04ff56f0e 64592a5894a38293d1b6ab1d22dc17b197abf03958430220f65cb34eef181e1a 9176b873aa3bdc9b0ff1f559a6f0bf461a8352023ff96c23ff66936b8eabca1a e2ac83631850b0cacd5480ba4d829c078a2c585579cd829d87c7c75b84c2d038 6eb13aca0027818c16c6c37d76a23bafef22a0d482b2531b2c9c7ce01f81125a e432c75580602893e44379a3b10874eea65e3bf11cb65820d07b02e01957abc7 499fa68044f6e6253a26b5b0037e0476cd3d613bff97e0bb27f6afe61703af0f 364cba34e8d1458eb9795eb598caf17cc5b9750511fd8276d38af6ad2d3f79d4 0557d80b3fee0ab3b8acac48dea00bed9919fbbe70475305a78894ad8a0b49c4 d7c39085d3d23d45ea060830217bb8d696554c7ac653552270f5764b0de9e1a5 d6b8790dade3a1404d72ae2adf9ecf0d12afa16841fe85388a30d5e167dade3c 435e51a5845c1a3936d3ed5f8ac42de00dea7943bafb3e2e326f134eab25aadd 0ca58944764d5f18494da29551e1d5ccca942d97ee0945b202683a6840c94967 ced045a1531b118cf733ff61dbaa1907253ac34c10386a6696e910387cd2e1e5 e14a9cbe65ac08bdcd2b5c5a56edd1804e3d0c62b05d4c98958750674fbc90f3 e4d31b4f8fffd25057fb4b24bdf61fe3b21c62c6deae9325d17860aafa4eb7a4 80bb53aa12f6de24b74721fd75b760616270b8b506636fe7bb8fd1d9c1e380a1 618459752756068580ce40dc0cec504d177b07241b40190779f17ff00026edaa 77297c85b33db24404cad277319545a3eb6c4e3ad46fb2742f31dcedfe14e598 21d5eb0d46aa9f0aa1ef513d36b68e1bbb28cb5a09205f25c38eca57e30eb73e 944f672a4242867073c14b6a25aa4132d20f26a82b22c57d31a5c7e42e3ab111 7ea8b9812aed48d3502bf8e5fbb883399ec466b150bc4cfaa8435df44b65a8d4 332671bd4483ab137a576edc7291d04de335eac619820d218548eb712050c6ea 63935263b362a7c63d5917610788894b0b26be0fbf1d7ec8558f1f990315f782 b7f85af441b583885dc950872511f6960ebdd6ebefe49943532bd61b204cec0f d7e53a747f1737732effc499c8d22a53ece6adc8296ae0fa0696c132a9e6b5b2 802c322173df78a1cb04aad791b4c58e404a487db9ef8a602a0fb1a805b765b8 fdb7b2bb12e275cddd0ea01281f9f30063ecccc65650133f0fabdf18f86327b3 0fc7da830336ae217d1ffc93b49016f92c572caf28ac76b9d1671f4a04716707 26d52ec1f7ede3d6eb0de8e38aaecd2bfaaa14f2a43abbcd9013145c9957b28a 8910cfec259a312cb1341f08c07b7a00c04e541f51c7d50866503c36a8d64890 8c4552203fed5471ba6ba607f0f87c5a44d1c4d01b1a586cf09680a3c13f0a79 aa3221440e3560fa0f27557c8493a4d4016f997e54d69739756c015887829a77 24bcfa8a600f0893f60b4c3be2cce6b883fb79b2a41b0a68177acea880f9138e 2a85bc25b329b85d771c1abbe96725ddc4db7627b188eae3b4a0d80dcbcf630e b58adddf2b74b6aeb9167068c7dba39b7f3ed0b64cc819143288319fd5e64e8f 30971d488c1f5714dd404966c6ef768d4ce4d3818f55eea7edd6a7e2e5380964 088c3651dd8d0777a2a4f2115fc831c8467a8ea66ae3c011a16e884ad6dbbf57 093cff116da7ee488aa5f9d9fe9d556bed75f3b4dffc89c9f4d74250daf42dab ef7bb6210ee5717e7cf40da390777fc7a27c25cbfe38b828fa57e33c868579cf ecb5a7ec1de01632d31a4db9e05f618155439898db2cfc9333b5854b81d88ace a3b725c18419a7c39a557e7ecd35143bbee16f2d3a199140d3caf3304d70ae71 62f50095f92da84365df278f020561170e77090285966aa407aa3cbd2e721671 f7ded3ef5ab6256580354df02b2d4a10283bbf048d6c4d2605fc504b68040c28 0e2f01f7103aabfe0a23cb23657467740bdb0cc2fa057c05a753d4982b3b1c92 74af97d871557583f32072ff735d306214c2a8683153affc5e026955ac9af942 2e4e8b737aa16c90d7de773b7b7fc1b90715064d89cb8178a03730b4fa53abea 409a357ec9da2b97ac3e19254dd13f518cbcf2de1b89b7a7bcfc806adab01dc2 e0859e580c6fe93de9f6463b4279cfdb726acdeac1e45b85d6a95bd7521a9693 2918d859221107e9b08220534d8f1eaf449e763cc0a191830b21bb88a996efef b766f0c74b382378c5e3f19750f6abfecd05679f95a7255049469da6c15fac1f c0f5d65c12c0a1fe4eedeaac2fccc52eac03882ce862cf56bfb20ce22b8f2950 e4148fa74eeb948c26d2d95cca7e9808adc7cec98884ce8ec6e420d0f72cad58 44b1f6e855ae4b82830f53c1c57586295a29027c3f0b6240e0c54f30688f2f1f 4b02cf3f5b6b194dd361067f4fdf42b5d81c56de03e86cdbfa7ad2f1d4ed3cd4 72de437f00b8828436404b0fe40d94780a515e536931fe347c2779a4ccf2f150 996e0ec66b1680a18bb3adcfc2cc1d4a8b252d3b8b44b3a45b40907ad045da79 893863da8b616cf609d99da8d42015f4b2f2b7e12b43ca271110450ccca981d8 09e31613cff15f95ffbb0b51daf9c3c481057110c466349d4c66fbd2ac1cf2a7 3005633522481446f41a64555a51474bde32a18f4736a5929a73539db46c9bae 637dedf27873087a17006efcfd8dd8ebb421353fc4d36b2f8d25400ab3e6d26a 07e0a753159596b434c8843591079279b21ec273e9840895e6538a61fae48e91 917022acb0f763bb4cdfcbe64d7b2f90758aae0db6132eb05256b17689b8c6b4 d35c42166a8441156271b8ae76d9e48da3c37f5967073d04cbf466fc3be33b16 0fb62300e46e0fd83ef441f0e1ab5ae6805ae0d2999f6e8d5af8fd2bfa4f2eae 43967ec0a0ee483e0a24b5603af622a35fb7487759dca20b6d1fa484ecaa9814 5b2bd50028be987a1f041ebdd8f9221ba2af034d5062abf12fe2ba282d291cca 06998c7dac0a62eaa0dc60aa96e20015adac42ccdda81a35bbdd94155fdb1422 c05c1db27fb2803bf38a34d23fb57366ec68d2817c075a5cc1d8a520df26eef7 07c2f5167862a9479c6a354e5277da6975f48053fb33dc53cb1ee27f00b9e894 82c1a3997523a4bf8dc41a40d0672f46eec9204e9fd7ddea1655f1f4197d4aef a1243de6bad6099569063a416a06b4786e98f0955a90abd92fac2aa40d676e15 70b97f8e57cc4575aafa94e9e4a75720313d6ae28346da371bfd0f3f2be0eedc e54fbd36bc8912d0842c6f95e1cdd0ad3127ead290179d5b6e3e286615688650 d39ab2ea5f841599a43228245c2eaf708ed34fd8abe85051c5afd73a2ce49fc8 26ffddc2870fddd4ecd16b80b623459ffb7ff6bd4a54dc580ec5c72489fba538 bf4a16c7905db847156c2ae0d2bbad9a76952779cb93cb79f85f90d62a85b19a 8d6a129639b83593e69368c81809f2e202fed076ac28f8b08bf5a8eb844cdbd1 a3623e9ec972d57e250d967f36f87187f0dce7e4cd7a9dee87da7c8f34369e25 55b4c7739bbccc1f95b700b9b1cbc49305a7ce0fa51d5ced8e16a25ca6af7bfc 25c5779b123dc3df22bc09e1ef5dff33920ec244ff63763f347dda257e8279da e9a8c3262c398790bb9a98e8b6af899fd88aa6bcfa15a8c351abc3723e0fa14f ebde847234caf045cf09f6cf2448892afae0290588af1655f6b539ec34cb7f74 a2555a44c90da8b8d559b007f3656ab15d94f6b66030416698c229bafd197127 8b416829c524d8de9c61bb0f56b727e49fddf6ec501ba10a01760a0301201e8b 43adfe7b6af8f07f78b81f39cb107c282b3d46a3e13e987ac291a80f226d58a0 7ad73cd3387b60a2d18194717b2f13115f317ebeb9bae5de358b0148b91c3f79 6cb8a9263b8ad5f2d8cb731b51c2c1289bcdda103d10c14a80c28d67c36fd6f1 8ba0ba8bc8cf90d7faf54dbfb00a9caa26cbcbd5d920584e32d05707ed0c948e 3a034c49508ea2f573650564ce7829dfc8740abfa785e837631a0f1f3f3f0339 b1fbe18fa629d1e03bd87818197790d407ff82163863012f63539405cfe56d68 d4d56049b05e5dbb12fb644068ee836efa7018e84399dc2f59a425de0b8b50c0 33b74a80816a63a475fcfc5de321ef24c46f2f15f29fd39beb1ea26b47d91716 86090162a17cbe3f8c70ce55b36649485d383bfe13cdbdf3deebc4b2835e0894 7c03d909d5edba98029272eea5a06537bd1053ff212efdada586e199b7fadb54 2a52f1d323c353d3ccfc3ca74058585ce62bb4b6a259c0a1fe73ad7ae1bdd0d1 54f98fc48347aadc834b6b60e45fe917fd261b2542de6b1ead767ff8f93c1ceb 63b03e2c622e47fd83b572d28e7b0457b4313ff94c3a53a33d8d0a625b901792 6d3587066e16d8f96525862387df924b9829f268cf72877bc4d67411f714e620 80b702ff95174cb05fc547d21ab72ac15b2c9e2d44d161025444a2c95595a27c d6b3f2ac70409f24080771f86cab8ca530b50f56f45eaccd7124859b25ce6a5a 75b09299e6f5436de708f09b554f95d7fd1c1aa70f711557d745e1bd1de31f98 3c578d8130a7e308acb1d336ced62626c6387acd0772c1446ad16501b463a701 b74439b2be4904d09725c2a579d80d800d1a3501c34052d84298f32dab3af886 5d888fc487dc97e9a1e4efa8587c7a1cecf3790d5368af5c149ffec8cbf25e3c fe2b9aba68e5322d1fe93803bc36bb5643946fe3fb95d9408f348cd50a8efeff d8aa82d5f87c653c3d3aba4ac4346a2c87feb1f69a33e6fc46f13da2acb19fb0 220a1be53ab2d7aea2d7869ca086b4f567cf3a9c1db6d0ece28ef6a64c41dcce 25ced3dbc8e6c8c4e85cd4d504a83696e97bdf91dca64bb24dfe02cb8bccd7ed 146794dddf3689ad2e5d621ccd5e63b16cb17b07e0a2f12f928bf820fb1d58af ef10472416aaaf28627b244c629d2ec01bca5f57d7f1137e8e2cd27f962277c6 c3465b80f301498aa2d437aa0a4bc82ed3fbf7163cc1adc71ebd7c706c6894d3 ff3ef20e1934bc16d871e968abbedb8fdc9fc0058b3476652a54f5047798cd71 23817b4d05f33b508e11d97932e00553337cd811daf3393adcc7210a4049e35a e5be9cdf38c46a792248bd89ee16ca15eb54e811d53418d0688fbb42e71d17ca 3aa517521d25e30fcda4bcec8b9d902bfffaec452f8abf92e5559fda4250a316 125c4284da74350e2424bac49e731098e2d8e3e6c88386af04a79d6e249617c4 a051d017160006b473058ffb31dbcc3ea3a0640be57cb1deb1d1196f3dc8fc53 4c4cf65ec1bafcf2245f1b7fb16c9cf2a640b1cda3d8e4a2045e8717f4257c2c b4fcdc2d3c47df4181cf4d2fcd9b10dacf72513f9721807a5fc27e763d1f7d41 64b23bb0c3c3f85691b45b2943de5732908036b38797ff52e54f6aa6fc564fc5 0d2ed23b619387ba50c6ac354397948cc7d64b98f51d126d57c0cdfb7e969b34 edd281bce38647f716db19db3b2b5ab8032ff9f51ccac7481031e732d756d1d6 db86e98db4b9edd23fa55fce0e116cd04d9de723933f6bfc4032ad96b9fed037 e9bacc961d4c796aa124dc07429d79d315f6332991c846ab578904f810210372 fb4d65fee2ce91577d7b1476878ac029289d0cae57ea705fcd547ba402abd8aa 7c58abccf083b97800931d69bcd2175b520160c85351d4cb10f1ce33018efc79 0c579dd4af19a0276bc7e0a3c78d64411a904a23bb41e99403c53ac41ac307de f2dd1316d0ecea25d8d715a4998a7d5b3ac534f4a27b5882ae2bb4ba1d80096c e9fc3f7f38efc6a2f58cc8f2b9a8609210f9865eb95314732d1f50c80dfc554f a4086c45b58ebcfb2c9385fb063eb251839c6288fc8d56c95877fd9c91ce7374 a17eaeed5adf47fd2bf528844963f340e585aa1b93ae53b9854e58f6d29bd0b5 b03b50418b421bb9aeb616fe9b0f85cac4472b05f330a85811bb708070eda6d3 5236c7cad540ed69dfa505287171b5902966df134cf67f057a0a71c11311e621 492d9c123011a02b5ca334cc099ab2d1505f20672b49cc658ca3dc6e71c512ae 3c28d2fc3b74ca08033ad49b41defa2186787b65ec8fb65d38bf162ca5aafe83 05e621ad8824fff83b71b0d527828e83d9910a5f38381dd8c81ac40b3fb7c328 ef1a9b7e590fb4c08ea1785e20e912931ebf275b136404598a0942bd42179d07 e30a1f02e6c9b401e1f28d94a07eb96d6883efb9dac725414df68bada14bf6da 7f8cc19ef0d62d44d79909277d3bfa79e0f8c761d9346324bba7c28ec4e4c2f8 076f97cb237638a878944a643886305ce08525d1610101b878cc82f1d3b12150 c688c8bba1600308b9be2138c942c2956aa18a12a6dca015f484974d93221f19 25a10000dbb3b693a4d7f37e3b22a44ea5c017f623ef0c63c73be423ffaf8c04 2609e85e63d7ca48ae577bfbfb23294f7fbb14b8f27a0256a761100cfa05c23a c96940e58ac1c5146994e7495ee6ac18a5f45f669fe20dfc3e0bae39fd65b307 501d013c2b7598946df96f559bb735eedbd6360b58f689c6f6fe83ff8b4022bb ff9d11bd4f83f0fcf2511ba5450e0c4f21848d98937a684f011857204642bc09 58e09ef4358e2c044b885e2af256a3ab08621c063a18ae86ed3083ff2d40478f 2495cd69d81773ae9356efdcad814015a1199e58359109c503ef36a047547924 ae3625ee5cc50d758e22ae6244565c0210e0ba1d4c44722eca04358009c0cafe 60b3aa98909464b9b19b8a06a1be7b8ccf1b1ac73f7b590ab09ed6ff05850d45 dd57e8422b8351a399305b79ef433db94ad7095beede3c6ed9bbfd24f94a014e 748fa6fa8e5cbb0f5d7e21f12421c362fc11e3e88bda60345470494adce32791 aa1b3d6e75eb973e91dcb77dddd54b84816cf347ecb7b6a5d6a9fd68ee054385 7b655d81dc3983c096de6f4b11032375e01fc4dbb9ede396d2f16d4abc7855e5 316bbecae2a8dfd576f65094bf5ada5583d629b516e8a7b8fae6f4316a337737 08108a0e31a7e3a81e12944a21069ed4ade16f5eb48943508d0dcff83dd38586 8c01d6f50b59462d350ed9bddfefbc98b7f6a4dd2d93b6cbf910a75a490a071f 7b26de878f843d41310b7287aae45951244a9694ce0a34a71dcafdb1403a8ee7 a7547f3134a7224e0e7149d8bd8e71c789af20c532a1982b89d0a9c759b0e0ec bf0abd6f7cb0497c67c5e1d17550e0590df5577721adbdcfc98c293de0f1291e 5221fea0dd26b8a9f9b1d56172671d750f6c55749c72525696a3163bdd4802fc e3631d455a4b2faf7753a334cfcde15cee0ef73bed1ac1fcedf27a0add8f4e39 911d9599911ec00ca76d10bf03c6a66611421381627351fc0e264d5c887b6f00 6b89e660f297adef02b62ac44bd47a2da2405456770b1d0f5d1792a5b3bada33 db938941cc971b115ff6d99e3e7b36f60fcbf589a60c26e6cabb9be2859f5c03 17d53eebf9cef86abac569a05d2ae8067236ef0131f1386583db921507cadb20 7a79be1106b86a4ab60ae4d6d077d5ccab100b0b36d602a6a9221e355704d38d b2fe6e9eb6a935417de5ff9acde0546e7c6ee7c289b8c9c957186c23bfe3b16a de56256fda6b3825fb96f507dfc91138382504d82aee2b8c02f7e438a5796c6a 9847c64dfd4c8846300462122529cd680a8a6806d56664ad701f406a8fe89e32 39a91e63ef3566dd9aed53e73c2c7f2b76fff54244ba883628ab2f585927bb6a f5fb1d7aea714e46654d2ee5e0dc2647a524369100d1b282fb5c02b3303cce0c c357f8aa28ba3b8d38be055bae445a53d8aedc865920b946f577af73d48701ff 8d3b9410ce4b2186e9305622cc8c28a7f2a8ddb5f80d53cae258b9cd5402397f 5801390a17af769a0bae673ab3599f4f1cdc9535c2f8296d44f51c48b09e80e7 fdea899ca9b531911eaec01624c2712f9697e0a84b274eb096eb1976bc63fb02 d1eb0769f7b9656d441665f170775b7259aaec8639acba81a9090443cd932980 4bf32f5dafc6c975c19c8f91271c63d02cacd721f9f9deb41bc118133b6a5933 3a27d72d4b05ddaad0ebe1c8b77d57f915205bc8594e79b03f6a11d26f988317 32b8c3173788199507256b7dca8e97071ab0027d856902062946c6a61ba00267 d4aba1b5d94202eb87438f3117db8c5cac01beac648954b70961f3d97dfae29e 07a6e7f92529f5cd27bf6a52b8f95acbe63b17adcfe6a116d191a136417394d5 5b2ff629906e5341ce1ab08bc179daed42cc51c55a48384fcc7eb0f332cbd7c6 ebbf256e802f881c34a979547d358b7957538b989ab8d31612321724546637b8 c2352a4f829174b334a1083663efa23397367b5ef19cfd687396ab2597ec5568 42e080060806b2da0dce653665f6adda76cb50a35ded6d728eb30a4d715e66b0 398b585444f93dcda676ac9529b7a35b23626c350c5322b05af82a8795a14da5 5ff5d4f983c31daec0d239e43581ae769754092766872b0fc738fada648b1de3 397bbea58e33a57e9708e27e1867ec9c01d3ecadfe4000222785fbdc7127c135 9196e798d04ba21a7a9becddca4153228135c9d425a0f1948fe587c76ea67dda f981da2fa8ce9f9d1c5d4e84262ee369afcd01b2f914c25dc1666700c095cf1f 1fe1974887f2e83d782b9103afd324ef6551f6e371624f997399a87668b69f94 b3654a8f14380aad61c17ca015cd130e015b5f145c13fe8610861f3fc847db79 a19c47f13cd0d0247e8da3de7ed87ef6e1f398c3d3d064e304fd138e4cbf91f7 4435b63bf930e6c7b0cf8d88452f3b1ab83110b8491104e647056f8e285da2cf db32d97c68c20ea73cd7f320b92220a121cf9a1f449aacd71bb45ae852aa3b51 6862e2e4447480bc6dcd91a52fc3ddac7828945087c09deaab71de29f8cdbdd9 0642bb374a1b330ee346f7cdbf238268d3b4bf12dddf124f3fe888f8a8dc292c 6d515c9db4ab6c44fb27f8b556329e5dd1c06d08b4efa5597d66394fa1515dea 1b95e436c79bf66e4ae3b0cc143e9dfbd4b99e13e9501481c37f790e0684787b 5ee755e81387c7383bde3cca9af972cf2e2ef54abceb93fef59510caf921b75e e38fb41db1f43e0e13dee1645377dba647631bb6b1b7a76275dd998f703217cb 7b3b9ca18747186e5a686699c79bb19c6c9147d4ccd98e0002e58379a40335a3 faa087c88bee826f018ba10b24cb4770fd3d75119fc6a4727d24feebbe7713f3 d9dfeda28612f9507deab1a42ba139630ff92b3a789642d33c8bd16750677b02 5376e8e8be7afb0270995c8ed7264c68aab84275ef976c57189f36d15f4ea224 3e65552306aa3013303f55f4a95a08f956caed4f674b0578a9b13db367dc9528 df5b07eab3acb4e1bf4d76c0b8846ccc54c7fe76440e3481f7d2154bc6fd8e48 eb8dba7b0f2b1ccbff81fdf38c894389ea75c406490940aa33b15f052a40a1ee cd933da01cf7e352a1b23df78443896e13f9fff8c6f2dc9ebc5bfa70ecef508f d15b90dce2234a5bcd067c723868027029f1eee5f77e28b178edebfaaa64bec3 9d9f8d8a6ed7a3b17b053796dd1e24e02c3332fa53dd4ed3187e5b9a59b17a55 972e309c5c8cf3d3e0beb964b998cef016106a7e220072c578a96a5bbd8574e5 d6b7d121fe6326474ba3765f810dc24b84a8d9f5a183f0858626d178f4223979 1aa41de563a9a0dc7c744354e2c06d2e0eb9e6a0f926172bd97e5ca848c640d4 db5383ff00b89557173f6484a56de7bb41cecf5c243332f1e8cb3f4a8eb83f5a ebdf240b83fbd156cb99e7fd4b88469f2eef2acc0e502cf9afaaf38cdcc9fb11 913d686b5a140824a1b3b785d6f4bf01cf7a0d48289cad89825c9140403ecd78 da0e77930ef448b4e4e0e1acc6e15068a1191772a77f84463e529636a14d2674 33baef4e5802e4a7a94fde5d84c663e92fbdf6a22883f3b117197080abcc1d0b b83ab2d5efe76a68b4667df820b2ff888443a9a9b11cbabf38a89262a156e21b 0bf87a17c442e00f49c47f0c2331f93e149e6268376e0d439cf285e303fc711c 86b10c055da5b76702ee975c8d228f00a2def442967ff378716a4aca93128dea b86624c2cb578db5dc6a2a36e5081745aff0c58b02e28dd43984d024251e8bd2 81a5afaafbd44b3a1ce9d9d1d0321408f7a5e69a7e2857394fa3425d63ef1f2d 32a5faed206400dd0084cdda7b0398065449a6422752d14147acce0ef2558213 81eb33634e686fb8675e11c8a3ad17e4598e9a40af3e00d872b60902fa1636b1 1cf156b5befa857c859401f2adb7023cc85c61ef681c83ad1f9d79938cf9ca17 867c19dc3b2bbc075194d3eaa1012225d13fcc4e97d9d35ca35cfd1e32beb026 4863e179266da048433384adf9fb1532b02a9d2feb8a31020d79aea4c4de81b3 7b94ea2a183cbbd4fd8ce3141c148fcf95f3568e8562bd8e540fadc94cd2b0e9 fdb9a55f78ecd6d1d5ff76644c5830aa2fc81a6c3ee93c0fc6921ee258fe082e 70bd4a777ba9f3c2d34637b15ab48eab20481c2385c0a99d32a435cf67b6a8e7 277ecc3382c94b9ae9b4f98ad4b2a09e4e5c5c3715ff2e5f92d107f92b304061 733bacb1e3d6a11bbce09f22ce7f502053639925607c6bb0a30e4dd609685167 3f57970633cf8ecd61ab78b7cd4754ee5dff472420e9fab2fd6d32d75be0d8fd 804ca5dc7aa266f074c0a9f2a3f4ef6d63b586cbf89b1dd9015a03df898c9b67 01841ff851d2a80dde2c5d08f3ca382b5181bd9125f922d7b5610c0dde495731 ad691eaa5b94b2dc2d4c8f94eec56df201da4ed6f614f64670b9cc8fe4a20874 ef924586e7379c018f18ed7ff2ef9a21a46c7aae2ecae6e0cc0597930adcd284 48d58cd5850e29bcabaa45b09257997e6c2523f9864f8762f628e5d0907720dd 6abf59e955563fa3aa75ea833d05c63df978434b9393120410ceb491f1b28ebd a2b90c9051bd21f98f0a4926edc97322202ee67011847c39cf2ba36ead49bc32 e8d29f5964384c2ff7af9022c67c3cc34468d13f7c5a431f2ac8baf8b9b21499 9eb31b25531f11405e86ec6417dfc5ff50524721e0e8d6707047534c8d71b5e8 8baac897c00c2e876f4b100122d8239540d9d935769715008f16746d0c5e3abc 05890b2c6c685ddf9ea0f6f8db37ec323c7b929031d0bc2c626765e7d0ec25a2 3c8f09b3229c9b2c0daf01998e70298d526e53766dfdce2294832ac2b342144b f6cadf3fa76f38065e523e19dfc68c9880a6c377a5cd622f9bb3fc8f70716d02 72aed23d3200d9bf6baa7b59e14ece77e6fef2a9ba8a00f9b1055b0d2b930f6c 040336d7897b5785614121894068a009e3446bb420b3cf559a1179c234cc7def 0e2e8d343bae2c54944258f3d4c5d60030bf5f32b734aad83826c1ebaac4f31c 3a94e10bc636cc129f1ae9e3601be09ebfaa026cea55cc794f0789eed7a44fc6 7a8484d1a711877dab3c39e95f6ee340ca2274edc0a98d887eb43e46df78c77f 8d211308089019aca40737febb733ee0c930f1524b451ca19e70a13fd93d0832 1e76b716b66d6263eada70c66d961efbe6d16f23c3e9bff9e4ea4d5055d14bc6 f9852c50a6213c164829e7be909ca53bc9e74a08564668427ee856b5215697a2 44ee99b6e6b589d53116ca579b78545912d82c946c0d19be4e43012956a383c6 b228f6c06db63f381fca429fd64d6559be72c468279cca028a201c8938e17816 5da563d721efbc7c405b4d43cda13edc5ad6bcfe101d1ae6402949d5db39b0b1 e7b2b990fdde603cc27e1a3fddb43a14bfe15edca96f846d5637cd138ec5f2c6 0ab26cda4e672c027a51925d653c8eea8ce831821c61a5d86d0fb40c0ca781b6 d3886059c0d229061f646141852309262a87371c312ce6ea074ac8680a378128 151c103e2efe29edc2e56bde223b218ad8921248ef248f5d3002619747b8293a b0577ac3a7af257161b3ce213545a212ef624b797234253ba9595871fbdff751 bb94875086feda06e108f4b75bcd33c9ce25aec2fd0feb25616cc06c0f009ad6 0db28f042827802936195f265b0a5ff856f2a6bfb0111f7156bf6c644ea62306 ad18c747b14897aff1bbbf03b185cca423de9b40e6d9a659f890e884920fe610 99af84183263c425a35c08bb89342a06acb9077bb60182cce402dcf0e9f9b741 fca75ed18da859797cf4ca22d3084959863bdfdf1050d71927fc7eb90f5d1b5b bd789ace4532a1cc6f5339d89ca1e1d723565a5b77afdb8920d86f06fec36bc4 d9f2bcc93378ca534855700b730da8d2d7c3e5102f764f7ccf8d5bd1e4f1fe83 e20f8737a302454badb7b443881ded5289f233b1cc3aa278b7c04cf3fa105317 346fe2fd6a28fd7a9c2a3f9b2184fac167444619e0c8103c51462227f9723031 b1d7144b7eb6eb030425187843304528356954e1c9866c1bef4335fc03831bf0 24fcb6d74dfb720634a511a14094b104c42c440d2097a25331ded75d1f53b8b3 2277c76ad5a46e49beea6cbac48da6f93ffdc9a9d258dcb09f69286f8c2909dc 831d5ceb5f601606954ccced0dec3f8b83175aff529cb6a9b746af49562d41ce 90938e504e89fdae6527b76f5ace47abd46e7c6c9f7f4e1a6697bb1733eb91f4 a4d81e560f40e9b3f64fc447d2f9323e180b3cd19a252a038ecadd7e13b36106 2c3fc7e2e73791d8bdb2ffc4a682384c24938e5c5b4da3badafe15d55dfce92a e4b63ec38c6bfeaa2685db1fb961d30e0c29754b68a173cad5379f5c6fd36bbb 67ad46c1c2a41a2c924b4bc1e8e106ff142fa1190f5c791aafb4bf5b57fc5964 dd7b5b358cb26526d242fc09a0aef5495896614f012f51922359e3adccbace1c c4301ef251bfc70aa99b321b2e1ba5cd8a6b03dc6640c7f431666f79520aa237 cc3e3852313b70cd011136714df55226ece520933b33a4da5a0545341b37291d ab601ce16a334c7100a90a78f5e5252507a777f2535c79caf9dfacced4e432b0 b8fe99b11f20a201164081fdd3bd9fd7a3a51cec0878bb9ef14dc1accbbb667b 998b4262227cbfe26f3470028c53d1a7a2b9509f01904ee9252e7989d76acb57 6d28f745a2531fd139c77bbe528c1d15e9c69c10ca1868ee113e7e8a0dfda3d1 6b0c18995959de3609f17ea52e508c3343e97e9d28f0e48225330a880038ce21 b28e58d6f0704102377570a5947dc88a4b471ce4e7b429494deca60230efc1a5 71776f9f94fa8368a2431c8941c413ed05a24b67d160a8339cc320978c5bbcad 95e66d9aed2097f4531408368dd8fa095b64f946181d3a3e31b357327703094d 76f459813f6fa638e0e3313b32be0ef5331d33b9a0cd51b3fbfce41245a25051 abb02b47b86f851c7e275f02b7928be9185ebe0c175a88da801ca603eecefb6b ece52dd3a852d42daf409667a1e860293d7507c43988ca805af268cee35dfe50 6fb6f11b2f82c7a0b65b08c89f88a6af23c30e54fa77282a6d04074199167190 832ba371039cab123624096f71448d135b32248d51e19a1c45d198968f321ef3 9e31d3d6db300e95fa046f7b342210529b9b5248abda8498af92dae7cd857a74 d891e6c0d8e3da47cbdead6362a0948a44f3f4f8fae3d4b5251589952e523403 24a7aae1e9fc400bef4996ebd5367f928c5108979f9ee7358015d8968f3b7d29 06340458f0ce45df8cb0f75ebb22ce42489faa940b25322568740abf60e6c1a5 154fe199e6caf85ff28e0f224b879f7e7c79810411bc416d433f0491945385b9 c66317cff74e4dc86850ec8fe4df648c8c966ab019c6eee14da2f1d3f6e17c3c fe29877d6b78b94b9193b16db571f1391154b0e9c4924d270097d2b1469aea0e a1ee24b86d537f30125e8fbbb7dd12d900d3dd0a782404b78e7fabbeb8e3e0f4 68bbd761edf1b535ab6eea5a4ccec0cdd435f240cb73ba8c573fc43e4c2eea05 0de743c81d8827e4c7679c2d226c6dd1a54783935bbb9e09e160c995658c2355 d47f81441d09a56347595f3561da1363ca6ce17fae41ece4ba44a404e9bd4c15 a6433b85ae3cc3f8d5f882522a13fc8946ef39593f86a23aa7cc4d2e602bd91c aca09d0c77137faad9cc4c979b2ef9317e5b15d36bcd7bd796619f703c1c3eae 10c33cad719426f8010b71cafa2014d0d3edbae2521fe5c9f3ff8a1a116f0fa1 b22db7a96362eed739d9a85b7137dc436a7896ac96ebd5367f81538304cc6125 dfd34841edd732a37629727b30ad642024b38985a3f24516313f6578ee3f94b2 0ebbea90a9283ac7a86235578619f5c2c389b313e505530ebe99f4421e16a743 319e89cf90c515f1b58febee3cbc35359b3233b70d18e3efd5c71487f13556dc 11bb56524bfea67c5beae297cb371f5733000c3336ea4d7572a4ae68af19a698 2882af80e0765809bb0e7d157711459e8d7bd46b7e60ea5c47c751504445e201 f55d8cafc81b17213f472c594aa27204d99ade879b4f70b760a62b9f60042e28 9162a89491faf0f020eb737b1a35b9e567216e3d9d6f0a8713bfd310fd42f139 9a2884d4066b5481268cb6973d9a1e9e224054723d57a61a61a232b413de23f1 b8d593b53291a7d22e39d6c02669cdd826fc54e303c078b700c1b57884a422df 55c5349109a2e08453d4e32e3d4389521e225b5d52e414064a17133a2d24e4fe 79d725b27462b75d8b791b5f4bf1ee16bf4060cf43ff3fb25170635877ac6d70 2e99c3dc2cabd20d9b7d83b55f1bdc29916c033fda2980eb401294e7eafeedfa 1956d4bcd56da72e34d08d33110f36c70d6d926ec6516854fdc0a69b14e92b1a f8d15979f05dfb0df9696edf794672dc1f88605b92726c5a7d0976ed3c7d2feb 9e5e945e6adb53398132d6ad9884d37b3c953830e6e34d38e0c8cf7e31107c76 fca4dd6964aa16797fe5c9103e47f6ce96372f7f07a2351f2467a104e22837a8 a61e4f00705db3bf7c02c57ad4d134e3e98adb90b9dcae5a3b550be702c76e7e c038dc81bcbeba4bd15f6210f4477f8d48c9b8c514db37ae5a49719921455f7a 6852e005eb38d12c25393905a3245382226d35d0f516e71e9024e10062b9397e 74b8d308e03f524c88f6a02ebb12174b99394e2d4c5cd60e481a8827eed8ad03 07e2c86b8b9c1c611aad156f3d450bfa4aa7f3aad490314e00a31d3d7343289b df9ccbc247639fb65966dae091013f98ecee14aca603a845ee421f121e888b62 604964b50312d9e9d000be3a80ffc762fab0c1384eb645bd3306eb7d23a1860d a2ae2bcc3790e0657d7fac24be39b0e80ed61fc575258663dd329821731f8072 90e48ba6018fb404029c62334a0dd6ac7584a3e615a28ff6da044fe1034d7075 34b4dd87db425df6368f9422bcf1218f590335aba12177120b23ca6c5020d195 7127dffa1c43d09832f8271e7d1edb9b509a53ecbaddf7b53aa60d493734f454 e780899ed1fd947abc7b248da0b1d605a05f87ec65f81856055c76afcc4a0493 a7bfc00864957faa4c85f7fc37f9171d5ee991a21aaf8a770de305f2b8f1c0f3 5d67d753d813cf6c8aaa259c7c47af138a7c8c128ed2f8e05cab01184cf572af 766802297d52ba01a129caa00fbbc78ff203748b53b984aedff3d235e475068c 65b3112f4da78255ac54403acfae6e3f957487bf73bd92ff0bff34e676ac0739 766a2065696f916ff08cadf7143267a964b58c5ddd7fb94126d93e32519ef6a2 5c1f1e428b865ecc839ae48229fe45dceb20e69b0802cc86f6fab7a44850acf9 cf8f4a30c2c05a4ed12c8e20de190195409684946eb9e7153851b0bac8efaa97 e1c00f6af7edb41d75afd001fdc242cef66fbfaf33bbd9567b1b1f32a1cb8b94 fcc45d6aa4f9ba7e4052f960e608eba306d920b374665455a09148aa88ae4fed 025c11b29d7080a4f313c854f9c74081d26b9f2871c055e519b35ca343421f53 c441a093436457f11129f086329ceda5baf54270f8c2a959c6bd6106484db762 e54748f2b46c3b1697f6e8d10dd2c0c7afb84c5c900aa9f5e8da195e0057152e d0e324d000851b611c3bb5e997171fe794a99c75cd1182e8e71832b9c63d1c76 9a0280983401eaee0c56574422ea42c96420f14dfd06c5a4fdb6fba65e5db069 484b60b74fb575685d64dc710f3e72e198b34dc8f085b3d8168a6b97c39535c9 d54d88369a749288e210723a2135348238ed3bac3b57bd84d0c1bdfdf60f0e3a 6501ffe02a8ba9d977cf5126e2b2bfd97ee0aa5b2a9a3585be5efa02b146637b 589ba3ad0f00cdf0e38a51ffe5072236b61f328c13748243f71002fdbf45ff75 9288c24549a06378da377e305d24732d307b3887da5712d183704f5815419db6 d4c816afc5471b7b2d1fd0bae85ba2bbfabd2b183a73ed51f8d15902ed5931ff 8f114e160a00b4d4a7a98fa7181d2a378d544355b8f2c50012a3cb392cf91932 46452a2989ac1c6d7b92ffac25a1d588dde929386575163884ad6aa297814ac4 124113e2de246a527268cb037c117418d03fa5a590b5392000aca533e210457b 5fb7a77546096f2054151c06a529cff0f522660298d2c35836c83ff948d4da1c a60d9e35307f21821a10d7d5b9b14f6c7c361b7a9c55df6eb9455bf50dd7e2b6 23eec65ba0addf036e9b05d1fafb1948e6ba56545f054367288b38622c73d325 2e77a0b299648f1c8daf20cc132cd8c18a1fd145fb2e85734f4277b17c863824 0918cbfd09f59a7ec8f31dc686d7284dc26abe7973e3f8d313a44955462f94fc 36edbb27c654f937cbbbb472b9c7a52aae6c603de81f6471f9fa539a87bae5f8 da795556374dddb7943c29e550a6eba583978d2fdedbd964883f58390205e2ac 9cf0a0144d0bfb7cc599a40ed87cacfc9668a7c396a6042dd7bd929facbcecd6 1720839d5bc4fa1f767df4313fd8daa6858c4d6afa52c9a3d15c3e2a4ad75f53 5381078894e229679cb143ae88693511bad7c96e9bb36585d9b30fc6d1d6c919 92f8ccc5c9225f7d2f5f700d118d4e5636068f82166337cf3dbdd332d9c11b3a 8108f9557e47b3f1d5c314f0352c8de817c7f3616841c6b5d7658bd620f8af56 b97d2ef07f914ae94ebbd7b346898ba97bc5360b7ec4c6482c9d1b3d108a908f 9a1f33888fefa5f1dca307a0f9b98b674b649ac62ee353b453b20c227d848e90 f7fb4fc81b7a57da40a7d237c124ac2d70e599ba77d8a7942c3688c3c224304c 85500cf826d8ab9dcdc616ba9fe189000d69b51de4e9635fb38d20941839cca6 040370baac271223394eb9e8a660af026fed08ca604b33920455c91de5fb37b8 6c09ac71891e7bf5eaf674043e9c8bfd3049a854f83530b1bcc20c56cd3c1bd4 b5a545824f243f8b07029055a845b108b17a2c94a879c1a84001e811f95cad43 a2acb5aa60fdba1cf4b2e27582a097f5cf5800525551621ca35b88f0a9837dc6 e230fe9885e669d19809912ed7fe90c089bd527715e094301f4a56cdb05abaef b016bf7f4ff537d3465c51a7cc2ca45becad72361f0863dccacea3c19b2902ff d5338014993618bd3239f77f8c36e58de4ab318c628f1c08403e8c9b49723bc1 697da2a348fd3cc60c2064dbaa81060b361526e461621da4e3ef0e462b34502b 5789d3f268fd0e5ea48b46601b68905374ff1427aea175154e359829095ce4c0 f56ee3cd5112d34533937d3c900a969d600671163a1e664020f4274d586a9820 155f378d4372dd1ddc35733e1e62249d1e2dabeb851eedbfa4097ea1041f977f 3ec3b7331ffb0df977486262efa6c4ad394f833ca5fa1d45d304065bc6b0059f 6ef08d9c6ee28c9b7d64988f48974d8e06026014c66de1b87ef750ec81dacfc3 d199334cbb15401a031f7098c9a46798971edb155c582b7179743e99987807c7 7cdf82f30b379c2730fb2709647f49c5d53e5f94520413bff4fade93c95ca83d 6c9d4664ad48e7852b41fffa5219ff5d9d3c6d008615029056c6df2aae34ec1c 0e84f3ccd8cf2cdc3bea3ad6bcd8a9900e9f7b3e611f7aebbd5a8d82af421533 12bd2ce27c2d4682ad2a8b24f9daa5554c6dadb9201c14be079c75de5de12d59 d1d6c8810ecaa77b86920386ffc33146b60ce81c891c364f43f3bcaa1a6c37d3 b10a19732163d2affd021a92d8547d4027b98c78ceec9e061103d0359dac08de cded1811a65a96c0bcfc37490a3389746186af73bcdb78eab06cada8935b41e2 803156731d58b88aa6ff98e050b1077120d98c2bab68a427b53216d1a5dae18b 9ab16371877da92313b9093853fc0210b2d6a2016889a2c0e8c63bb6b84a6831 1f420ac4f2ddac8b5add156f770c545d02b160155a4ca3eb5866e5237503e7cf 15fa37fb8de25bc54b4ec3fe925e6b0e47e4d0a68efc1c8ae8b8fb33f8721203 58e0d56037b329b426aa446c9dd2ac4e2ffda1c675faa108530db7af740f1622 794e3e0f660d70b6cb19c331719b43e8a016ba6e273f124f68808b31241ca15c 7168dc5b84894dc19dbd5f3d13799b8d656fcee75820cf35e8fd5af13d43a97e 283d83ce25c2e26d11bdeb52114f70ea433ae98b230e670f9f4a275cb9f935a1 d7027bf49025d52b39c4b65ac231ff1f404089c8c0c0293091d09699a858a5ec 8d576d670465df3e3b251db8bd90310e4e2ac669697a3881f14106edfab96c1c 6ddf5fdaca335433d2f38af3035cefef7ad7709db72604575c1ca12d9d9f01b0 6c55f569b789da8f7c9efea428be00cd88f44b9e83efdc2518beee404530724a 1842b29444f8256f2aaace6c835198fd325bf1d9debd8083dbd7f499928836f0 03877e47ab2431a7192ad8254780094893e53f86aa5e972aae21ecef13929915 6c7a8df0a054dbd0d6a4fe0e531d95f94eba5cccfbc7ad1cb93ec427e270f20c bb7736f065baa1526a22809e3c7ffca94799a6a50782b29ed79e002d1e156b78 e29886e1507f3b931fea35a39b1297829a308665638cc397d7273522f0795136 a26a3a411fc7b9f2ca91da0e9b358ea44c645d11f594b5434980c8816066bb29 a80608816e99435ab354b7f43b1649079dffd13588d68dbb7dd8fcfae02bc8d7 b282dda4d2e8330816f6b90a7285ae31da63f198aeac6d511d9ec40308dd0486 cf71263f0f24dc2e6b02af9989d2b7f7d9ab4e0b684d2ce93c56139a5d87171f 81eaf3f3231116dcbcd96d124d1f8f0eb3a2cb3141d09ca910323c263199d5d5 8cd93f99e70bfabf1f2825f1b6ad88e6c9cd9d7a549dc87cf537a4a31b3c3cf2 fc89e4b24753f6045258dbd923fe8ea43a6f1b88bdeffafbcc4a25bc565e549e 72800f84cc08be806553c98d4484f15a6d53b1096aac16d40ace382b112a0c92 542ce314dacaf77e5a4a99006d51a9a632843460b47c40fcffbcc34a5de5a9ec b391402e546a3cb1eeef5f2655fc4e90a3d781ff083b9433a02aa3aa0f5338a2 c31fd6e88745cc5c51ade9ab68656001a0f419513e66174da10c646e57843798 d342f2fcc5c854e1eeee0462602ce94ed06d3fb0ba1e0654d8b81288bed947d4 c03fd2e46f7c05a6bfea39c05e58cb5b5088e0cc60ff4b7ee949ec6d73761d14 20204fd63603adc72fc050b730d6e41406d39c65fa14a4d2e110fa229133a2d5 97436ba7d5efde246068c4b35c6adbe1ef92b746ecef16b4b9467cfaff1352d8 880df0d3aa5c33f2acd838253c08de4513b474fc0b57b53cacdfc78f9fe50239 46e17f2d0268f9170402e299e43831f86913bc35689441f77e37f8d0f99bfd9f 8de209dc202f773a0f4f3f1b018234f2c2d09207ec319cd1ac6f1ba1d12a7576 eae64932de419586ffa40dc8e214d21077d740666d9e32fb92c9d90b17746336 67f10db9631a65d6a6ea22a99626b6e6346f2ce46a8c8ba4b6a0da3056a2330e 1895d9b0f673030983c2fbbcd38f6188b31d1b734ef71f65859287ff3c618d46 671fa429d884e1548ac10003e8c33731dddb442279e3f3e013c7f6cae71670ce 9befc73b3481239c8b32704dc2dc9b8303afb64517f4e1f4293a3bc64c0018ce 60e47b9002fe25a5d09f1822738a72876bcbeea8c12c0091bc6a5d5dde7c07ab c16452bbf4997252f96fd86d45f7277d36c4e71ea96209270170055d786dfd68 14d53c65cb781ae34862ebd0e4211a988dec6e34da1dcfa1d5838c37e1bf1809 d0c5d31827970adff8344d2393153bb227bf8ca11840c0003cd578cf49943e72 790dd3f37b850b08352a3fb795e6350e60984455bfa2c27bf08d8e48f39ebedd 8c356f73cfda897634f07040194c9f761e50ee1a02deb8eba12a58b2cc67c154 28005181319ed63d1ba44d5ea0f40d9803855b45ea1b8a92d2a3d6ba521b949d 58a71a1210eb71536cfa11d10ef98016e30dc7fec6782aee867f926c56803213 a61c9cdea26b55167f1fe2e386418ceef667321b6f79641f7a5b6e80dd7d771d 44d6a17480d13cfcde8a7e646dafb52f94030e1924541203ab6d3dd1e10b3eb8 3b419ec5f39598e5a4bf2db5a27319627a03cf256c6620f7649dcaa0cf57d3a8 11a6828d3876e27e9442ffa76bf3a40104f294885b52cf3a3af93f2cfa7e90f5 d714a9993a58bb81caa371f8e750339eb08c3547a7f3791d1117a4735f900b42 625c1097e7520f225001718dd7c71da65aa4733d9f982ea346f6fbcff6c9e476 fb8c81145d7b924e07ceef65a7b74ad6d8ee636f7a8c538b2f19daa5a059156d 5cea0f00d0017182e29b77c388a07fee10ab99046cce8fa31ecb59893ca3e034 6fdd36b370e192f389ac2226a6bb25fef17076f73a492072b97af107a03ee6d0 b38ddb7ece2a30f991908914b16a4d5b4b97562e1024e177b8e81e05ee27f83e 3f482ff96501c70f47fa275f19084f75e376d0bb559a710b08e1179a36173eec c3cb787fbf4f471308f4d04690fb33fac3f3f80d6cd713d0cc5c1858036a0bf8 947537d63ffeea80f3ba680d4c5f3534476bc9dd7219abb9da6a2c671860669a 599e7cc50d8aff363c187b620b97ce60c358955afcd2c695149cd1b147c06f6e 00347a1b6e099f6916eea2115619e06511c3f619ab4c65f169a3f55c25092eb7 1dbcdc7d18cec3d8362139b4e3a325a55aa8e41a790bb81115d9e7af67fec4e8 67a9e3eed3a0654214f49066712b5faf457a19ffad7921559bc0e6062e2583f8 0de68512cb8050da45938b7d8c2ca738dc2b465e82e4d28f6d7502c0e91c8233 3d43067fd2c4c66fa85cfd0dc3f6583e241eb9974184db90c156d11db7ce14c1 74c0de186df1990bd385b83b763223eb0478e70a1c3ef0c74e8b2319fe58fd7f 618c907f6b144a81a6efcbe3f3574e58ef284c78fdb25c8c862898b3281b759d a3239907d00fd6fad3887d4b32b525e8d44ef091bc9a6cbd06300b017f7eff45 f08c818bf8d4c1738bfa3c227d8f5d11cc78ebe21660088cb03233f5ec12e014 653c6f44e4ebf94f523a2c85340086fc7b54676898b49c06ed86e7b474c3b03d cdb53b1a99a23b8ad73283b7d019d95445f13cbf4da801d6fc4fb5a4028190e7 5fe5c092d6939bfc67b6d2e2075af28febc0d606d24e227ef303b59a09aa4df9 1f216ff8e5602ce8868c46d116ed94576aa672a0a31f686a7377dc63b9e2567a df7cdf45adac31f2a87613f20f58781485f4f18bd61257a7af0c4ba37e609de1 08e37eecf3e1d7343d559704aa81237e7347b32f77eae63631bb7c155a51ab14 e762e04fea6d0cf4e2b57d1595a798e8e3afd46588c7a539f764c60d8b80b6da 54bcb786f4749ab9f5c3c2f9c2c212d7e29e4e9cbb708db834dbfed2c20c0ecf 8ee743c6160e59371697fe387ddcd00dc7bc2e1cab4b86cf43170e5414171b62 be9a20815820a6d694d98423d5a77d057cb11a483ccf59a64adc8cb31bf38fe7 3d3f1b3034dbb4a2ba1ea3e6557ebd08e2c6b5c8f6eb2ec12f1cfc740dc46ca6 5262fb27081d4a0da5992e6bf996e3c52e96ddc019deecd3ec32a9c6ad1ae3a7 d7629b7d513773d3cb6f9099b86f9e1a7c0b98558a402eb236d57d06c41cae47 d53484020742d5b9e853a714cff0b4e2f80c38c7b58a779a174076dfda9adb2e 143c46ee7c5cbcea1ed90d088c76e5cac0e12b9c51cb89a325c1342a1bc1f1b5 2384d3cb9e916d52efc84dc5d5bf3750f3cbcbf29346b3af74f2cd22220f259e 14d0d39aaaabbe37bab12141538d12d75d3c10318fc8452fca864a7534716bf8 10273f14379620493ac74f1504e36da63a9e7fbc523671094e918000d314c44e b60f7735fbbf6ccf3fd62497ebf9e43a3c0e44fee3c2a9657961375f4aaec3e2 89053fb353f75f901a6273133744d276f0909ea488043b97e4a012964e1c8aa9 bcedab674bfe81d40d03837e16605d28225b06f6a2317c08041c789d8c77959b 6fec85e7784531dbec03e3c61bd448c3dc9fa9053eaa10147c6f6e38fbf15e51 642b9145179aea701c19d06c1c169ba5a4fbf54f90c42324486969f875077ba1 47651e46b82d351855b2caddebaed062034c2ed47318e21ae2978b6f329ea82a 2790fc39db04215c04411f45b2a63ef6a4c9ecb6932b8244d2a8a93693c8949f 7bae9469c3467cba2e83dd650674d7c8715a0d7ef10394cab75b77fb7c9a0f4e 6f49442008206148192c5159aaefc536560893a3939828171e19e1474b8be0b4 422783d6bc8d757b6b04d0e1e9da629d82e157547a08467d8a25a0791257d0ff d54fd011d70bbbc083681c39b658793c77238bc0aec8cd1ca787a5ed859118ac fcb01b86ae07f6754888f70f9f3c64dd88e0d9c7eadd275f2bc127483d979d4c 6440d7043a3981b810f9d000ceebfaae9e82475948e3ecdef6937077c9d32f12 d58a3b7027115167f8ecb33c2386592c7d71f964007129496ae9980034828563 3b9d10e4da7a513de08483a08fe031ee5bd47c219976805936fa9c5ccc2ad310 19700f4a59dec3bb11109e16e94048311feb51078505eb6a29530f45479cf80f c12f019f60f7828821805d986e647991e20fd9e3a18016aced3788709ae3dc89 cedf6f505aaa4c65a229d56d29b6bf96fa2f989eebcb16589eda210c4cd52655 2b1e8073ce43981ded6ac274bf6c2736eb3c2dc4674d467ae855752ed5cdabd3 eb7dde563dc430ec9b2f52bcd789662e2f2396c19f39750eb4933a90e81901a6 dda1754ac1751a7d635cb5110827df051b47ffad25f0f43d62fd9021166dff11 343f2b5a46796dc4c4f498f1f03d47ba3021b3f0e50b92dcd6308643ea73968b b1783c5520c5970f18ba82dd4e7854c6e054742b31ef342506b8ae50dc68555b c461962e6388e8889b86eb8e4da31de98474d37119d3342f5e0cf3c972289258 104c4abc237d749292a6118afc3406d5fdecef553e480dabfce9087f62af1309 27c90b821af5277e13e87518a1d27a29366ca1b717033b81b9e21aaab4fe2eba 3a3b2cd4bd5045201075cb3b39cc79c8852a3b0f77596c310f9ed336d4dbd102 9dd8a550efd99f3a79a4f34971618f3914bacf910994e8fafdfd803b7da2be1d b2c83b135ec733bafb79fb27782ac2924900c38132c7fde5f861b7571f579af2 0a6eddc1ee6d81f3b6f4bd1da5c24988456e44403bb477f9a44b56391282243b 62f2ceb7abc036eb8b884f1bf98cb73a06446e656b9b1e4b22ffed6b2a364d62 75f85d1ccd5ae84aa910d24484e5be19f9cc93c7944f048836134783cf938ae0 ab6f2dcf260b3112ee617c7ec4200e6c7a7f0484f60de50ad7a0f59cecf129a2 3137c601643831112ce61431552a63f709a3003a6f4e998815f05f1c9c3ceede c866dd8a19588e3db7d1d1c7b745d1d9bc31e694b57cd9f272f8f1bedba36ca8 4c6c9f8fa5a6cd50c79940701772d7115dd6b881e6f9aee6486529a6804293e7 6fc88cbfa3a3cba1a0ecc621d41822913e09e8e06f3a1c1c8a047f68c6676021 4aee3a3a974f8ce09bad8edfdceec3f69bdcb6435254d54348811e4911c2a7fe aec969be588742d514d552c66e60f5a7255d8cd023d30a3181ad3a1d31e04e5b 551bb1825c214767058ad648fb3efd72be87b366798468bc730489cac9fe7084 b9dc307a73061c0dad6d72efe7455eba27c057440ab7977c762ed9504c3e6d12 980b8251ecf4799511d2b510633e4d793a679644728ac2f9a3f4bd673a7a3885 d4e2bb19cc0d0ee38b7736f9f037043f06e23fb0428318e0e8961637e7138101 049b9530a15a13fd9ee1aa34b29a56f17e50a8743b373198ee19708c2d1ca442 6754bda56a2f7f3c4cb34387825de4bf226e6ffbbca348571a77d7d779eda627 76b9605dfb86f64fdef0f13a8f61288632ed6aa0181c7f0947dc84cf02b7d7e6 f582c3db41c857a10594a1138f046a6db9d89a91624d68bec27dbf24ca38086d 8c5a879035524522696d4a3d2e4bc01242bf4f335a8a0292635bb9253013902d ee5d4b6eaacf697e644ae21881435d181e2452536f2bfc93eb82cb2d1cad33db 17a821ba9debda697f42f18e0a0967e2cbb4fba4025d9130b65ffa87d3b93907 180368cb76da8936f862474e651b436337ccb76081798e97e571467e94855561 17b36c337498b8b4ecfffd29a90863ab2073a9f96f84d8c8faf4283428b3929e 26b9883a8db3edafe1ca4f3f16f4578efc263dcbeabc6be462504b621a42081d bcf4095246c8e86484443ea6098a981552150420f70f81dd17cc8b852bd289cf 706949c9a1d7b447b0c0624abed4109130656e7d9842bccbd67f0eb5c32cac4a af413971f55d6d87364307309915ecedb27339851f8ad8e0819e5d1b21a2db0e 2c8019fc9532f8c4e01e5b4601591ccdf0dcb1b9ac11d76121b2de6d96ee5a08 145c5adb9b044140d238c9b1cc02f3d74fde0b07028c81603a3079b61021c60c df4ee3a852bc966fc034e23a076bcd5115ffd9685f5384107ec27095ad4922d6 d052591f952c1c733363092ffe41278745c96ae64b5beb6b4467cd45b8067c2b 6378d83c440bd79b1a1649406cd01340a4bed3060db9126a6691cc0d0e15331d 7bb989dbf10c8b02e1af65dd4c963785aa30c16a3bb57f35d27f4887b9f0a04f 9f65ae1457db643d8e9b3de0bc1a1d3586bcd5ef79f5afedfcde39e9bbc1916f ea59e74e317889ab64f9d92567534e59d1f434910ac3bc797365ffbd3295a534 c1a56443c43287773ce624f0903f4806329543f068131ad6d9140bf8efed987b f554f6aa54300b586519d1ca9fec345bc538bfdaf4e546f411b81f078844c32f f9bcc55bab3e9ed1ec44c01d8ae1213868144ce7785c50a37635ebf0d0158f1b 006328bf63b28cc20277fc32081fbdc46ce42f2f5445bf4d04d428cfcfab9f44 569a696539025e2c7e8524e1114d35ba4e2a1f56c545e4741fc2a8e0997261ca 54e99e3abd35234a08a70fd04943769d4303d502cd2634fa777cfc3a78ed9629 7be0b559c8c8ff6f40c40c11b5a402dac99541fa21d1d1b8ad117413864cd9ae e4bd2fbf05066ede920bf117635288cbc3863953ce8894995c2697d9fdc6e911 106244247b3ccf48671ea3915a9d594b743553d519da7c11641f2f5883dba391 924cf2a2259222d694a5c04e8438ef56d99d60e131d726724f39a4badc4809a3 bafb6cbdd8bb26b907bff73f034c71444dd3062fb7f035b13f189c10f7d72b93 7fc16ad929cf3b9e1fb95b104430701fbdb128d2c41f9df68e4a96cfa2880823 0d5ee35c9ceecc48ab041037f923ae9d1b661ca8fb67fbe37cb80afe95db4833 08415b09832d96f7c15248ab4110dab4f214c91aaad251d5c42b10b3e121613a e817bf52b5daec74be2666497d749d33948f5f297f7fed470f0c34c5257a0232 8bff4393e4a9248f5e0d2f33f554003888fcfa0f92fb5eb7dd40034e981a18d3 8fa1d747072b699c2a6bab4462222d44dcd382f7d1c0522e4924d6ce6775e290 59586f6c9188f018e5f55ec2cdffed2e368601282cb9a840f8480960cf907b22 e2b6c57d15a55012035ffc6d3eec2644a3a9dfc5a167a79aafde6f26a414dd3f 2f97ce591d5dfcbe7082881fb24c4abfb2ef24e48a5d815b4687b78df035159c 0907373bed6e4700bac9a3df333ea6f34c13d8ceb31b225d65a9d9357b2f1fa3 6541c632168344bc3472b932b34c4c2d20216864215217df3fe405cf5dae3ea7 a5c5b61d05e370079c2c42a8d2211777d6d640098e3e029778ae905fc5da00e7 0aa432d959fdc71c08f98c8b1e172ec038a171f21737c8aacd8a7c9fbab87810 87f68a0dc4d3764cc619304825a005231d915cf9c3f8648cb7308290742a7518 d4256907a58754c95cb11ff48224a52c974c2290254b894bbd8050251061e668 deb504d349810fc3327d43d34c76390144ee6f10a304ba1fb8ef95633ad38433 4410dbdc2a2ca9514b1b18f948a990b4c7a5ad911c599e99e8a6bfa3a7cc3922 9b1dea5359ad3549e5796de48f50fcd890f6bed37f6da6c9de49db4fc6654736 04983389caf27cf1f668e7b97ab8f02a83a16a272ebd9c9afbfe92b50a42e34d 37dc1503eedfba6a3c4124c96a268c7302a3e659672ce5e22ba02e0706b13a91 344496fa56ea1f3db111908bc8d64cf0f9d998082445693275429f345895c44a 0bc9539bc825136f6589330917b46dab10b9726040d260e54a3490895d49f4d9 a0a1200020f3278a55538e8c3724282b3ccbabdb130e85c23a7c5231d1a9ba04 495411b6c44dc841a9f886de4c1bf9feaaf5ed6939b17521190024247a3518fe b9177978ec859860905da812cb481564095ad51062986d1a5213478d2fda4c1d b568d66112c216a100bf816d31e5152169bcb2568f81ade6773ff7147307ca87 65aef5ef94439587398744ee51d1f6f340e7f23ee1e3538f1435ea1ef172a46f 409c1abff3af0baab17352561518cc7d6af8f2c546ea44feb1ef7373f9cbeb2e 6d079e8490d45a85631f6e33e483c02f138625ae5da5134d479d48a897446302 26e4f9973d7a37894627f50a8582a21bdc8fd2dbbf9b42851a1857bac74a7272 0943ce1d3de017783b2de73749b5ddbce2993ebfeff7a11e12a78159f2ef9f63 4bdc8afdfb0231bb6e26f11acafd45582d6afafe745f0cf4971a71fe5cf3ff48 9a44d7d9583030126807fe5c2954a75af8b7c82dc18d5a3506c3af874701d632 33bb88973eb68609ba7adc1ad565873a92ffd4fc4c1475309eebfa1c4a5e9c40 7a871f63ec425f92ffdf083b5410f1f0b54c8eea1e2b69b3086e5c79e526576a ec5ff38cc28229b8f5a71a5f10a603bab449a52455046dc6afa6b8a084fe7223 a39e51afea6932b4faca45f5eeaac6b1d9fd8d5995e0d98fcd483cdcfc609a54 546f2f3fcdfa60dd551b150ef13ef5180c7e507b7cdf1264ae9ed4a51d865ea3 e3cecdc2119951808f47d9e1ec3d063be96210e331f33c835689743f7cf07b2e 96b4366e4b447d13fa34d6cf3e60ca8752f0256662b9112e7f13771f01da7cdc 959fc86c564b161bcf65282ebeac376db2200a3347788006d105776a23d6f3c8 8ef8e85e3e359db8e400b2b8be789a35bb709ddea6a17d8692a35f7fcd73b0d3 dc697967f4bc95841af8f5513c78ca328d106b55aacd73f2fa370523f59fb845 65497d00216b846df039fe831bda92dc44a3e334824d401d909b6f56edd9903b 61f5fe29e175d49e7b20ff4c38c2c2cf3fd52c51012be931e4b62f00f38bac41 8c5b9ca6ab4d20743d93c9a8cf6e716269e89e3b869a0dc73e0d531af38af2c7 b4f074f15ef8cef1b865a10e7c5b1a0eda7af01fad3e4d0e80e3cd1a43e1de54 ef30e365510a906d8d4468b316e905cc92bb231a6a9c6bca003aca011b5f0276 90664370304a0e34c8be534f7c4f2b126e697fe394b2fba6a9a25f598d185058 563c0d093b054e2bbac5f22909f9a1725c84caeff067087b879b73fb612396a2 3a5b7c32f5de0ae7872056ce41d593d746ac832d7e8805dc70f1d856d6c1bb2a 744ffaebf98c2b0dd8beee08bb2432bd08e043d012894436d30a1b2eb69727f1 2d44cfe56cb6bb419738c88d02fee1c9173538adaba709657e14fa1e9827528d ca9a8688dcfbe6e4719f0e7532177481ae2bc279dae24ba04279481db27adfe3 7e7325523d813961542eb422eb147597665833021ecf958bd9d109023ab0c7d7 712d7462f6f15cc6dc2cb1a19a0b7329fe23e8d6dd3cff5f1795148cb434e6d4 a3cfeb5bcba0e44d9cec017f11ee4ded65a545001fdc8a324ef8b1899546702d 25c44174f626568148ee3b0bd3de7dd0f80623cef610ca07ee9a3beabf8d4f3f f6ef09e609c290e9a3c1584ae723301e83de15e043e9b8d1deb04e2cb3b784ff 4b0274f1c3b8bf71c34b5078afb918fb23ab2a0d59409da99fdb05f8d7f23a66 eb1959af307e1bc690e5fa4113d87bdce4c1c7927a818644d2f46fd746c9d5fc 9b8679bf4366869db97f0c6aa87ba61457448f7e13f5006562190ec8c9ab1b01 979a9a7c37551fef8598cf925f5d9bd277b56b6c8e3a1a4619255acfc415943c ed67255b89d7657dc860f88c6f478dea0a0c91ae2c88d3d45903fc7d0c2f3b61 694464885824eabe5b917adf13b3fbd28707623b6f7b5784810c7941eca13c7a 8e7ee11d26240fa270d5a8ae7409197ca52a8075e40c95184ba30697013832d8 68090a5c303e3c8c836c1784000e47d84c24d7ef520293cce024d91071f964fe fb5d319b577297682ad3823298d26302cf09e66beb6180824e6263e9eada7cb0 6219409e9d325ea93d61356dd9c03293806b86a312dd4c6abb031e979d7e6a56 3c0db1fe9ba19e35552da6ce188bcbb8afabdd6814060ed1cf36e85c88bf9b75 263598879b7acc4e5baa672246f61a3b809e41e5a8895b1badee482321b9597f 8251cece519c7decf1f0203658c7fd66999236685fea9f7a87ea29082bdf96ca fd23f1aab2ec8e5f54cd59fe2d7abb4df63efc3298570b99d7d208d6cb1f5d66 914b08f4654a27bcc6c926c020b5033d744c9d0bade606bcd7772fbbb1654312 1671b77d887ec49d5df768482b01ad49f5db59aa76dbfcc725319ea4181d6e0e 340a2ba21bcf59514771a7c1b8bee3f780c7b711579dcb1ba9cdd20ecac92b05 903d0eadfae2715b0ada1c974433eb082e6ee46041e37d4876f7abe42b51038b deaff7ac1a17029cf956b69f9c1710db0dde1ee10c3588de2cf8e7c363441ee9 48c9b57f19aa1a5efb5a5b6064f351a180c9f0bce457cba7294ec0fedb281d06 61f92ca09e0da19d4a9c79f37a088371cd5c459417dfb04335c1c450defd1a66 7e76c408492cd139a9cecd352d47e09a5e6d7cca1601267f27c32498a5107d6e c53866caeca644ca2dd972ef5913dd50cb3e6835aa1894cd42db599c5b61eda3 3e6adb54cf4bfa210b00bd01731af4ccc39a9c7b21cc6d647ff45b0315f4aeeb 17a9649c2da84c2f9efa5ca467bc9fc9c0bcf11933189ea324fd048aa7b31b21 6a835a4f680e5053595d8f3a1ccde16d51b3bbb2891560e2f74cf2fbc0fc9272 fca8f7491afee1c52e3b1a3809bdbf6b8c51ac919a83901e69c1a0d6fdba76bd bd1b96eac30eaacd1ce42da4cb13eb7c12c799c3e5781cf9d9290e2f64ae2cf1 d8f1c3b200d15f6d2f9852b0c391f8f394d595964cd6c4a005e76cd60e91b2ee d9c6e421e2cc751a7d63e2cd9ed5f3a178ff31f1301c53a4cd7e221917e877a5 03752faa79c19299c4fe48c3bb01397242a8e4cf1ab9f1276d64dc392632be0f f7843f41f4bbc50c0c733207dac8c9397aa5900d73811c78953d515f56652fa0 c84f09172f40c57c8c60bc026fe41c79e51a0663d3598e3138a760be1d33202e 9a03c9d5db3ade8870c14fabada3ca275829d93482f7bc5dbfeb2f665ce2c815 2c61a42f6a1f94d7a599cc489b9197e8ce53ac73d16d266beee3015b023d367b 911d16ae7c18bddd5453fe0394fb6a082de0be651785f97cecc91885cfedd75b c7ea78b000a1cd75d277e01249a05f2dd1a29636fb9413f606dd17c58300f25c 68825888bfdc6ceb83f5588834bc7540f649d8ceed32e198566d34c5a591afae 6a448d9128caeecfe8ed2f6ad4f7067aea1ee977cce70bc5489bd95c18be16de b4f4f07bd2f1177a47cbd8bcfa1c1873ddb14507512957f8bf15a164d24b2a87 d4d1b1f59ffbf420f06757f7519d9363b6427fe70984de9484d31b2d4cd9a072 0d261663d836eda4abd567f70a1d4ac14652cbae527a2a817e84713c5988408d b33ac0b8b67a569d4a2947cfcc99da380ff6917c9809253452535def5004ff03 2ce75022d75a40022d720a8e9330a8fbffdbbaef07324358e050ceb1679c3a6f d635c1ee555930a220147083cfcdc4db19b9583023e90a923a1db609c690f8d6 d6b08567223f387dfa1b269c8c9e898d645d183767e18e327e55afbd2b1e5e0a a8e46ef294adfbd9224a02c2387fa7b266fbcf985991781581ff782977e7df9a 6e8a91aa02c9919f99f5a0f9ba5b057d168e96f53ac2326e37f9b093a33993f9 4fcb2b32e7e867cbc963ccbf94854f9943109732109ea5ea12719822fd46ef83 fedce6adcdc61502d389442daca3b55ba9ae7bae3ea099062a78c5a0e8164504 7547c7dc57d9d8326ffa409f4bb51451c99246969441cd0ebdb9bba53544c6b0 3cbaca2d733c622072f3d786a82c7dc1c985c936476c2d4f8a2ee59bb816ea6b 5f81cbe638cb5ee155bbe6d8257b61b2688c5f3e925983ebbe4c2728474d4163 18971391a73f54b50052a9990f11aa65e8c1f5d39464cc5e5c47c79abff141b9 962dbcf19c15409ef0571e7efb8eefce38e6eb164cfa923969cb16db5eb71476 d086271c835c4794bfb9f6fc1218c4f61d6edf442add3f15295ecb1f21cd993a b2184e65b4f906c0d41fcc474c3e4374d4a847b00d3c670a20285865179b79a6 cb9521b56bf46181e172584a6ff205bb503c8fe35e64c802e0f1f8b3c344f39d 5f604e40fc0c6a6150dbc09b44ff32f7e501f9ee67b5d4f42d8d79fa3a8efea9 a4a15806a175dcf96b86fd5d921173c5cd2f7cb9dc11d8b95b9f31104081426f f4b37756583fb78125ff07c6c9e8eedcbe9dfd2743b28f3326c2f643488b2ebb 9c008990b7c0ffece6bd9b801668b1bd508aac2d7afd10811c4646b68b1a405c 7226640c827fa0d1acedea81f128261a793c5083824759532e1c4e1717a93821 a9611fcb9fa16004b5e5f2226db4ea54167ac22bae530b0e7f6cfa7ea83de3d1 37838cdb341cb7dbb946641f524f91929641cb42bc3f099f79ee4050613dc638 c2df4229018db4e4785e9ccb647a08b126a1dfb906d501e09e37e8c02d7955e3 57c94c31bf4133e2dd79c5400d82a4c03b733a462464d24f2570b864405f56e7 db3b0a004f20c5479bc63d105a766f20d28ed89bf21505933da1d11f583f4f1c 17defd8ddb516181d8316e1a1d8160e5ef2549b3eebf6dff5a518018552d401f eeb998c2606a555103b59650f837ea66e4febd975b414ec32ead2ebb37bf1620 de12a68a5c491cf6bc413a32f432b0bc7d1d9fff071e3c4a7fe58114aa401a4a ad55c47150c4a09523ffe0966e3040f43d3f72e3fcc453f4b5da88ba899304d6 87161c005633dd964fda24add629d5e0bdead02b272fb364b7c23e2950bbed90 4c25f600d3bde258d5e27b0f61ef7af3ec2b29050d2ad0b4bd44b79263fa20d4 98471b717b337c0f4802985a7d43df1ddb987058d9efb5cef37901b7a326195a 5e879c5a3b08fd0e9eed68602a6933295e8f5dbcad59f73e9d4dafa866cbf0bb 057c0922d59cd711f55ecd41b9a8ad327f399c99306dec228e3ac2f960f5c022 0460569342451b6ca0f4b65c1df9a7a3697bfd8ef586b3708156ecbc57b5d89b f28a01027561f5e6d524e65f3a32eed44283c35b13b0e7f057d61e75bf70c8b2 911e9518b78bb830fbb66d361b332d79d2f78f43c3d9d90d5c5d0a05af4230cc 4f32eeb9a7bfe9e6bc76b247643b9c34f3f6fa9cfea6ea9426cfab208129cf3b 61b5cd9a5a6f0cc21cf1576f5fd7f2ddbbac3d45ae6166514f31e7e45704efd5 6adf14bb05202cefbe389b68a29cd56a875bfa8b859e15538c3e4f75707b0514 4c064b49e08c1873f4127aead80ea47b8c853d38d76e798c34a0f092e512b276 42bccfc9abcbda6b659d8b6caed6056c993c1bb730cc0305b273f22fdb9aac71 9ec5f546b2e7d28672f2fa387e6f0be19046a116d1a47faccdac6fc6cf6ffa82 8dcbc422e97500c38fb775e2492bdfd85ee24cacf5bd5eca1b17d69867c44ba4 9608337d50f6d26436ef41737febcf20172b394dfd8a2d7abc5a8352cf00e9c3 43ada60b0fae732f3da60a2ca5d1d8143f2e609f119bc449cea79a75fcbec8ae ffc49bbbc371e6e6fb317ef7ddd357b191d874d20497c3830e04c1eeeebcc574 83df0b73c180eee7001cd9ffca8abb533717d7a641faa6bda4884cbed2b24cff 03f90e14698c602153e7af84b1bfd76e42a703d6b228064e4a9c274dace1d5d8 6915ccde4426b9b5a908c153bbc7c789c315b909b7e7cce59c6911fb2955407f 131ca5a986105d009dfd78e30d8b12c9cb0e22bbd0c38e684039e62bfb255625 796df03cf56ea7b04555b5f44ac17c0e99d56b230c11818d3e2a4c3e8d209330 a2fdaa8dda25a6e587f31d459580d81dd6da434bf036194a989f5fc8954c52fb d9f4717282cbda2cd9d2518d937a8487c49eeed61b92abe4d17563afbfe9d38a 585fa275376345d1df0198b26929171d11d22b4a6a1fad9fb109dbe8a46a1561 e60c4180ecbc46a4685b69ee86be2516225f2dd56d54f16359ff79e679052567 281dee47f23efce23ba70b7efa2834efcbf0a6e1a033d320df90fa44694cfa38 2bbba156f34b2d1a85594151e4b705d1a285f0c6385e169088018cc3f8be28eb 69f028f3436b748f239550c3b2c3f40191dff3e9bc0135844598ea3825f16be2 a2f3deec27675a2640e2716fdf40c289f81561d2d0d1cdd7ef1d5f8870749534 8c2062387d05dcf380e9bd4e7c6ae04f638c53aacc8185e1c49941389618017f 3898df5112cdc9bdb7627532a5be3724127db81fbe09095cff5b4cca82787c92 f6f7c020c9f4ebc0f65c536f780fe781494bf4fee82dec335ae229de666277a5 491b00955e27aac9345c320c38ad2c85387fcb833039f93d3a695a5a5ddc0c71 c7a2123054abb0215f4a50f0312c17d4319f74b84b9dea5e809180a31ec750c1 73c2592f03719d667e0907949d0de5a6bf60452922e14f2c3730b09aea035038 ffa57d77def41ad448d69a8ec599084cccc513e31a107bd6645041dc6d489510 68eec9d5b0e3645780a9fc81e522fb2f1734600d929d732ee5912b5a90c9d27d 6a5260bb6b590fd8d5d47019a3ab0fb1ec4185b4cf2e371b43c66f1c337efd8b e382e9b1b7e12d92b95c179707faf378965b271a7d945d32a55bb0f2e6b5e054 548b1827fd141a26b1162803f36940faf4acb3330dc5cda5e5972716de0f8db0 fb5a1098d21d9b45a1d5f5caa0cea736b2032baa82a16ceee48d04897f2d90d9 fc012a0f046c9a50025b7ba9874196a55c409b1114579061ae67f3b500443d72 010bada88d2d9a32b75c7831fadce5ffc786809e92085d613ad457c7d2cce4ae 434e14c2bb00984f9d24b0146fdc5e26fa95f9037d275ca451b9cc3a65e2116c 59cbd535af352cb9b1408314597da7b806a566c7a505435212136522a3ceaff4 eca9d9bf96cbd472371893dd8f2a806c1f1c77f0ce799bd2117305a24db44d76 a153b666f6763805d7cd32ed17e97b1fa7bbe25dedfe6aedbd066158bbd1b305 32ca2dcef68b2afac4fe3ed7a19207c48b110a35d1e6a1ed95f234e10f432b54 4014fa0f6d3b4c63d9e36ea84c69b1f6dba8791a9e2f65cde72f13217b0f48bb d760ccec97956bd6a9badd1b11e5e334c5b6aabc798ea34d247e6c5f98d22aed be9e8d02f202aefd4262ed122598a1bf9d3e5e08bd5d86a06589414e0a866a56 0538488623062df79bb34ef60a6479ad56886dab5a7b8e99686014d0bd3ec2d5 c17f0d63322931537ce007aee19f7c25a6ac7b5a0eabe47a630def345088c483 e4d3454458a2b9c66e9e7a3400712e1514a6d2d99164faaa4b8a24228da5096c 221698f9fdbe73f8af4d2fb6faecc990c2e89142c1e4b1dc8f7f2c9f57d9224e 44f0e66e0b3d56be4f818a77aa0cad6e4cc5bd97d325db418fbc914c020e2b9d c771cb79a2e227b8a9350fda5a198f99b54ab394af4923b7f62cdf6285b34c54 f02600ae0c6026a570310a22ebb16035824480ca531d5afaf1baac80766d33ac f7103679f2458e8535e6cb8f60970740ccaee1e31cfb6f2ab3147f4edb443392 0db0e67e1e924bab10839b47a0cd1af91e8156f59ee70f53cd7950ac7d75a977 dba203eb7ea8d5c32908a832f2a208ad59cd1c52d9208a996e3a831309fe53fa 24d7a9009e36c6cb5512a0756da54194459af7aaba9cfae07d465973c163e513 6fed0cd3e103ba2667ebdcccfc2000aba28c72ba791f1e0ef63f59753f0290f2 1bb710583704b2caaaa21b878e64a5d21c6cd6769b6190de769e119094e909d6 edbf05bf0013d9ee8bcb9d13d5d91b6135f68941d313dde2e997feea48f099e5 f1fcb01ceb225d287e2425a0904b51aae8d3780caec17003559d9feb07ff9574 b2559a24fa86cff0c2d905bf09bf4390b639711edc9532e5eaa215ac8288909d 68266b7ba1c3387340a128e37e30c8ca8579d7fe9229436e5a15436942350b9c ffffaf31b77e252c7a85a2690beda72a689cae9bdfdb31aa5f1f975be25b5f0b b7483aa39d258c69951493500c2472063279f672dae2da80667efc070c4419ed f33da2e8127a07fef0e5b10ca3c4333bb6a8301bac6c4dd48b7a7bdfc2557491 6372db22757fa9d3247dfc61f9c05400e1fd99afb097546459b067ec3c6f09bc 3bd54579044df1a01af8d9f3836f0382f17accd3791d120da4a846e20fff14a3 51d52baf3b896725841f809ad7e511f8dbbb9063b4ecda02f451689179a7a999 a43c6ba28a136925b49ea45982640a27b2551ca7b61824b892b572211d0ae0ef ca21d46d8f7c72a13785a74e91fa6af641e403fddc1abb8c03377bca47ec66e8 32daa7c826e9e005d460589083985c987efbe0c1fe5b6693451569a3aa02de63 ec8551436cd224c91546ed316112a0a5d27e88235b2822fc712f3d67cfc03d7a 4146df19107b7c9c1507e5ae200d3fc465d6bd4d5aa7be3041089b0acda87f69 bc4be64eef3f5d38ee0b24c885b37bd4256a5f5d9ca241c2ac3b9ad3959cc2f5 cae32f1e9d97d0336ae03f1ce970d2814c0b8e49f2e184af9b54150eb77b6b15 d8fd645d896a31aa18cbb95e71e8ec09f846d968b44f95b654160e060d3100cd e695caa2203b60c3c798a0076063311b29aff78b9672f21ab397285058f20eaf c542fd539aae1087a15091c787d1085a1fcb3fe0716f17d54b6d4b76e5b79208 dceac2a7382b8788f4afa9b3058beefc7e53c897e57a9b729c5109fbb73c06f9 616f7756f6367783e9e3169bd4ad492b4e0ebdb3dd4282533ca3c74fb9cfc7d7 7ab9dfc0e15b43cc21a35e2c2e4b871997fd8fcac6d503c338a28c4f5a8f2cc5 c2e572cbd7f84daf859dd0f5cbf842ee5f64e683a132a99612250cb2e56a577b 58c9fe03593af2c8805130d08953206bcab1c6377106971297d840ef3bd11eba 5b4c1ad84b693e088f834546a5dedd4a39d03259411cc9a4ad0c6a1bbb105e25 51653fcfd9ba6fd8b37ba8bfea32f9b09bea059baf0c3697ef2367098dc16e4e ca1c9bfff791319a5fa0e78918785f6fd180d5c502f1512ea162aab5ba19efc8 73008a43edc9e8808757fe459e76c4db8631489b46bce30bfe384e62cf7b2339 c2e346278916ee4d58bd730e2a0532181c6380b2acd1905ba2434f44fad3cc3c 2e733f53f0c941ff9cf2e2ffa036c1189e617d3dcf0badc8c4242a6e84c1ca82 c9cf210851c4b38af1a8b9fe9b308b6cfd04ca9d73dcf8b98590b74d2dc393fb 2e783852ea8b26cf8c9beb079452e185235baf6734e592e08be8c0e8749e5fa9 ff17b02aeb9ef7a46aa6836c1c60e55a9280aa2e1145c2444d133973cfbeac9a 2665e47d700721f38427d72d511b67e88caaca8923c93e48dcd8e61de788d4fc 942698f9b9e8c18d6eea7ad9a29cefa45f77ec7f684f2821ebb527206d741ee5 34252ff3693c1464c3a9366172c29128fcd68907286f5e4a4b868f9e487bc85c 51175f698b18862533427001b0a435c8c3f7fab8ed12b59f69112b544d8872ab 71adc347c39b2d07e4bff0a31a08459e74091e7e54b046fb88064eaaf3944adf 5758fff95dd6bdec68a98eb5357b58a5897ab774a595035e0a0c4ab1aa156ff5 17fc68c2ad275361b4b9038c240f4fa58e460e74084f4efb0155c442fd98c166 975ed6e726fab4ff04c47b28df3bfbc5de3fdaf4b1785b5add653734b308a6b2 586bed24f52db8d5e84fbfb3b7674eae0551635e72131d0e5902592f2151ba3f 6afc0afb753d172538b6f97cb4ae6360bced7b049b2e7793443ee1afaef4cb06 46e8c4aecd7938a98a0e8f63c8acc72f409f6fc5d62436d0788d032b8d234950 1a80d19bb9e3f3b4e1b466a5186feba305b086b62412dcdebbf302f437c4548e c7d7c4f67a473095c4df1a21c50b98284085c0c0e386c07ec962f75a20d26a50 e2570073601eda0324d4a0c9944ed0f50d4e91445bb71cb7e20ec12f872fb097 1b5ac70039ce1dc5cc28104234bf3f8824f35eeafdca8f149dc4a477280033e1 c5873ac7e6cce535dc80369bda7bacb145b864eaca2a26440c44738d1300db6f 1475582e53c93b8bdf94b8596a59a3b0799e5bbc82b90b53fb559272d409e6e3 99cbddba322d1080f90a14f4106394dc6ac23e3f469ce06cf4a5341622d6e6c4 077270b4ef45bff99a3ec56cb2dd57283538cc628efcbf8dafef42e1da5fab39 6f8e6fdd99e06388d1e9f01e082268e82654252a8a9be160f28d694099de6fcd 72c76dca496444f6d82f4ea26bb2ce3c271d8be3d7b50e94d244dba81dcfeb45 47790265c09482baad627280c372ed440e904ca8509147a1486c8f723ab67326 6738d72db48fb9c909e2398c40212d7630e26f5528533041437be81739b74c20 33d23a3dfca807a3af26b1f3bd228b4fb70b7895f5fd871f8fc1c4e08bd68133 75dab488c11535022985c4e47759d4ac66e36f196425a1a5d9854925b55ada7d 37c5ab5677992bac4e58998cab105f0822b3663c1209e96b24998a05006bf908 4bcaae5758fd3a0d58e24087aa9e3e7061515815beb6daf06267f1a1939c6904 35dee6d24c7686f285ddd6a2cabdf99be0d971163fadfdd76173bfacd102b7a4 5838afad1eafe8c4c8555ee5ee340a111c6a9315327d2d0eac0d819d189898cc b51d3dfef8054af52ab55493fd744c9a9969afab840e57b90548d22128ef0487 a92fab73ea56b5d44c3b4d0e359e6f34d31618edcf39c300078a27a161412fe8 deb48a726b3763cc6b013d22aed1277cb64be07b3c1a1d20ab9bbd969e724330 a0282f62350c558c6c73436d8bdda0a1ae4a5884c5703e44fb9d7a02e0655e0b b65c2bef2496a1eb74200f84725f31037555f14a7c361f6810c9f67e838aa523 cf6691ed7a2d2a6b657291ff99a665dfb0ccc40f433bc1685b0154a78344d95f c8e3d7c1ea993cd154a48ecd7e40078b30d13c77f4e938ee5e62891997245a31 5a614ef033fc587bfad0f16cd72711c117d9a86de95bff98d2f906d47c37d19c 3a6ec8752471d01f74d0c69fdd9e4b3adcb75b05a455512518d256d3444a55d3 e24bf1cc8408ca27212d61730e7ed52067bd3825e73d0ffe213789d5b1437d18 1f12bf73332ad2c03806eb60953d265b2d8891184d73b199b4094c9caee7e2ae 6c8b45d6c7e3a585c50aeb4a4633195c1e955a920d9b471a21b7288d30c66c22 3e2c83fec9fbdb6e7adc457dba12b02e73974797b7dfeec8937bf43e206cae23 bfd73aebbc812c8f8a0068e8f6db6a90e5a33ff6915631782f883361790c10d5 42aa33ae7b65858f9096d51355bb5edd86911c611e92f37e26d1a87d8cd96a00 535b26188a7b2d93a0d018cea5e55fda3d3ce4758e15823fc4e4fa83e0c47fc9 35b0d79ab78fc4cba8cb6ad47b1692b54ff2a574bb5ab30da452c0421ff65e11 a66e516ebcb0e84aa53fa46269dc2643603319874ff9e3a77470153ad96ee5d1 f03ffeede50ab09dd5e8e85753a95dffa5860b26d8c25cda944ed8f5405ea3ad aff5149d19c66960de1aa9f542670d306ec383fff48c7916a2e82ea61cdb29d1 c54a7b76e093648ca07d21c8c07f73b876ca702fed45751dc5e535410bdf9160 542ab7ce39c8ca76bb00c3400e67003c89fbb8ae231ae909336fe41b414a8b33 5f5733ee085d0c11a7190f451900961b4c7881ca9a1742cc0103460bd930d250 eeda187495af6aed1bb4d8cac54a929c1bd5207311fa31d050dcc113b489b0ab b1ad89ad2c0eb9ee33e745d319d307e0dfb4a27468b5e3da1494275e382753bc 4d739c142cb0a920f3b24660b9c9a15d7867fd2cd624f4299f96bf6cdd712259 ca98ce427a407e38f640d9fef0a057aa4d7e0722f8fe1b1af691c0177302f767 9bbaaa01c177cfb55d56c9e39d793e6945b934c814bde0a3fd5583d36c04a2eb 4f139f9733897715256cb14184c89e0e6360a5456d7f704fbd48b5bbca23f44a d61eb5eb0e4e4cda58596be48655b71a157cf63fbdf1b605fae92e33cad8d7e6 9dba4d65226d680e918ca199bd722de43b1636a4bf9dd61ef3c58f8685f01346 0e63705b4bfe93b57098a16a6aa7512a1a14315c9e38e4dccec9287f87712481 4719a915453ba84c76fd9349584f2188a077adaf06aefd2734f4640366f46193 97b2b1ad317e7b8c1e555ff894de39047015bef16540e8a300378ab4ac21ea36 ddd749fa53937323f5bf117071585c428c112824a880806bb467560bcab1da2d 6fbb71ec234d33d0bcbc6683a4b35c8cedb548cdcd890e79b347376ac7eb0f95 0304bffb05eb8264f177b659311c968edb67085a01b3b59b47472389cecfc1df ba1e91f577232d8cc8c89f0e64afa9f87730ed3aeae8e5e1b5d41d55167fe604 cccdf0039cf8e51ce03577a07a12308619db29a7e21ba711f0d46f1eacb62586 99adcf4eafc911459d53524ef3595ea9d75cd5051526f428ab6da4da40ea78fe b65378aa3090c1c208c407b7c4dedbd1e9c3f8024a8b75afd551bb4d39652f5c c38e0385e192ae7137dec6bbef5891ef89ef130e68adcc2853bda269e1fdeee9 27da1c3ca5c855ce3eba99a12005909aa5c5c8679f73a8e33e14e5fe2d903f7c 6bf2fe3e133f8b1a355e7643ec589e80d2909dcdf7760dc8391da8cfc48c3519 604b52a7d725cb0074361afba3a48a49ed4f54d711bd0938b6444cf626e4860c 36e297831643ce90a1c849b24ae424336e609a534a26222d22f3df73db85d33a ba0dc5c66ee5c44f7074e6d151da17a04c2e742f97a5eae47fbc41f534c30500 8c864a75b529406ae3f20994973e480eadefb7dbf543c2d1467c638817c97052 caa7079e86c43c4ed0c6e6a3c9569fe1e23c04473feba9cf24d0ef754b1e0752 c815625795a3947edb8d43aab921ac875c6312fb51f53c0ea24f52ff96196e8e 9c81d72d09784033f2d40708aed825317a9f06061f302aef3bf5a58e8066bc95 fd28ee03a1b0514b467fb52f3849f73b3eb2c16593ff5c02771b6d82f1b40c3f 4e6811a045c1affc30f10f9537a3caa7b483d1022f160b953465ee768693be12 97371d0adc14d1b50cf4f456d7d286b925729fc7328dc0d9da80aca4014bb7c8 96ce4c08bf301232be6a53b060305eb78846029e05dbe7b65648dd918e9e3ab6 b7987ef9d704a6cff43a925d9c353a924a08ad6976fab084503b4e85c669046d fbf0fac1e2d97bcb7858fdff2f4fcb339c16e6a665d8e227fc918a90cc8387ef 0062e88850584d04ac8ae952ed917955392d26bcac6bfee01fea48864c230bc8 ff09629c0c970c0172e0192f464fe65372e5f81c865c2b548eabaa054c20bd7d 028ffd607d2a3f0b7e84c69e44025cf2887b7d9e99e0dfab466241fd26bc9857 5675a78ee007f4dc3badc86ee13e06472c1bc8a483be23932db1bc9c977ddb4e ec1f3edcb80b701fa7f145956c7f1cdd22b0695141fd47594a04fadb8b0d20ea 2cf9b04c5397b6fe15955c6fed8396c6c5e845ed5ba55060b523a5db691cc5d7 74428de878412531e704f6041ab4a25749e20232809f9e189e9b9fabc9f8cdc9 a0ac0493104581983324819a6f5ca6aaa97d6423f57d7a000f29f4e5741603d1 e0db413b347c1cfae3622a99e077651f261634651e7ce0a40405168eb9fc100a 1fb39a208e191243a8b1acd58b85cf41eb81acf41ba5cdccf7389b1f9cc82827 856841b16871b4771ed56646192126ec2b7633ba79e6239020783f2923e87605 33ccc94040cabdb3f7e1462decec8ba7209886fcbaa1efc3a67d0bd20de64f41 05a5aca06002cd44bc48ace03d3f64781a4138bf2cae4a3ceddcd0231e0628ca d46af544c7f800ca1ce8462236a092ab068be6aebf5fb3322450561151ca0b5d 01ff8c894c709b3a05131287fa578ff48203d0adea05f0e57fbc0664e773e8b5 ac0a77be3ecd51d9cb0d1ffcbe22890cc581481f2cf664e50b09496fb2849fb1 25563cd340caca3a5d0a7b01bca0eacd31815e3f25226cca37d2348081089538 ad4ead93f1f377ec1cb86b933d171aec561149709c8169aeeee0e5bf88fe532f 10a10a0c127ffa80dba1324574ab2c7d40991ec68032a6d690c86f12715ba179 a8a8ada7a09645e36a9fba1a9e54df47394f8a882040f4ed9ded29e1aa68dc60 19d4349dbdceef3c167f8ce296e2326810c25cfb7db6492ec68d853b20b6700e 91fc876c181a5714fffb6952347278dd7d28c0daa74681d0a12da9630126ed69 3a32e5a6cdf3e4843763f877137377d27ebd4294ccc88fffea20365789f280af c1f9d717c9b553153f1e7b89b56f92ae20295e03291d212f103dc6fef75cf058 fca6e1f65a8977a8663c8b8a5daddb8a44ac24dcd6e25af43a816eb75437438a a9b875b4919f8d48d47e843750c82a68d18af901da5ed4ae9c6b55cb9a55b5eb 434017bd3ea8d7a3a7b4c110473c4afb23f869da201bf765c2a35e886bd6f67e 7f3461d7dee5b11c292730909be238646878d6f4466ef73bb9206f5bb8e8c2dd c34c2cdacc9379e70586687ac76ac407abf9556f21823d58a57f04439c0485e2 8c4afaba4170c80af8a585ba2ab581fd45f0836daffac5b7a9899b55a52840f6 8c19923018473c434cd1b9b3c5288947f5e493e679941813125372caf846de16 0892b57f1c3e0278f1c686214612219d6212bbcd2d4b9eb3ba6c8aeaf1dc2291 0e5fe577ff1e65e587d7b56823dd196b0eb388270eee92b424ca722a75be5f64 7b931ef2fcbe710ca997e46c83ae0b70d94669d28276e99c85fd4130f2340b40 d9eda776f00373be7fa1da8fc953f33f07fc7f1f47abacef41266c23c4e7e914 e2b802f8b23025bccc0a208394cb26783905e5ca4c446fb76c70cbd426cf24cc 76a642a9ccc1439c25a5222bca358fcf59bf51fef0e7186c035fba5018227d9f 3f2cf0b3d78e1f5952afbf6bb0d49f11a0bf2ac1cb15983958f21a48b375f66b 1c355024884b6269186d80888c978a754bfc6e355215851cafba6f69d051d10a 8581e7e468290c2d6360e03a50e8041484b22095b6eef06268dff712e270008d cee17baeec3cb43090156af521a5d8b3b2edc4718893f67d20e21cd205f51ee0 c12a9669562be380a1facaeaffe7ee06e1fec9b1577ba8282768f744e5f4c553 8071467944688fd15b7bce2034450c4029ace8bb139030d276b161b013643251 9d736368b51638ad76a3c86a475adf7d360a9240946674e7b5e72c93691c60e8 711428e904e08763a78751066fa4219f40d5dda1e2c9f0eb9484c1c4bb0243b1 41197d6a27cb43507676e9394a9d0ae0dc03814da514a61b7d7e6e25f62a5d43 11a778f4fa8ff8bb398b46446fcd1354e0159e7778b0abe58461773dc9fc97ba adf3be8dc03e33ca38889855859a27c2bfb522aa8e51fea5176e33d8c6ffbfca eb5998fadaf89d7b30340884475c1641d4aeecf91012004c9547c90c6dcad026 bb59f8e830d9f0e134b7719bee12f6ddafde26e7bd6c38e5dac7961c629a859a 80459c922fc6fbc253f2f6cd80b47b2422a4b905021ef91bab547ec21c7750c3 39592e6cf0f82c8727c112259e243a653d04d9db52e521e12a889d4eb4415d14 501cafe61ed1729e4f90ea3f10767a6719ac5b0cfd5c80f86c2ea8176dadb058 0f623e4d025d668fbb3a957d1163e5ec4668553593b4f77fe6d64cff742be3a0 fde99e03375a5e3769ec8198d53dfdb3e826094fb922c576839da0a90208048b 71c2098df2e95d3287d77b0c95231357dc152c4010799c4380a2d7b0023d4928 4f3435c590fea856e64b1273523a3c5d47dd09284a336a386dc62c8a927be8a3 8e0ca5a2e648f7ddcadb81749fdb05dce2813122fe91bcc3c1faec663d5632ea d1c6bb27e90e89136af882abf4bc19dd7c3d405e15f68cac85ba4b1e74a25c18 b5a02aefefce4e0b70f96a6e90c1c915597cc394bc0bfbd8c8672f85c23df95f baa2f4cdd1289b9c55ab226c10a043ffb64c52ea6f5ef8f89846dc064382886f db1e1ed5a0dea6c2c88a92f01209d8e96a3212e9fd93b18305bbe186905383b3 c97a3e86d726b957823f0d70b1d5db0bdd8f848da043826f249e81655b519451 2b68fa38cc48bc43772fe122a4d82cf6e4180ee021eeac97d291de96c9c43f80 4771c36fe963d88d1474b597f89e05c0be0418d07c2f095e6e5e136b4bf9e5ba 9e5b26bd24fb3846ce6d06f262ad4783cd62c0a55509f40baf23d72971e2bd70 00ef82fe27adfcf79b0683bdd7c0001aad2896a95b2f9be5408fc8274ad00f1d 11cb898e8a3bdd5787328a1aa4e523d545fe2c666fa0533841873c0428738ed3 3179a5cbc5e5ea009c7852a4103f41b1266ecc46e0a410473df7c92345ffdb28 5e433749db699cedad4d5b69d4cd8ecfface7da665d4534bf9c344ac0d6017e3 eeec522887146b9d772c3652e11337f8fccecec7933477845fe2b1fdc1f2f8c0 d54f9efb955ca45446625623320571094ded6402cb182ccdcc4f33ec1bf8d683 300011e27027aa0ce017accd8badae52789495628e3f8a6fd8e3d2d9df12d40f e77983291f2287016d104d2b201b5705575d3baa41751a3ba344efbbb2b95ff9 b1fddf9314ef3e984ea14903a940808c4b6a60f259cdcb219e0d4ae8fda60e76 538274cb3f385c08720ffd4ed376f17c084c6a7321c4f906fba50bf2f0bd3dd8 9e6e46d675bcfcf0edf2347df6b367278ede97d59f719249f5230a88041feb09 c27d84ac8eae7699631530485be75d314d866566653cec6b7c85b27ac02432e4 82b618d0bb7abe66e56a9748be8a2aeb5b4bccfb6fcc993d1d262cccbdb17b9a a154ab79085cde6be8e3b51117dc8c7153a8d464d64aa23f758408a5e16a5bf1 4b3aab31b10e674dba824aac4498693f1d4b0d4e8d613359a0b90022329af453 2bbe61e17974325ee2b9e24bd606fdaa563cb996c0c49c3080e92b38d67f34fd ffa534fc4f965179988d513b6d0d500a31e88a324bec42b9f6a2da5220b0a3f5 531db445d4a3950a1a2b7cba528a9d973a6408293c8de29087e2ed161199d698 64d090f303bad30776a555696426d9d7da03e5fea0e28919a7f754116e56f171 0b9c1cc1543070ed73d3aed05ca4c76dfc80667fd17b513b5de18bfc89c340f8 05e96313c19fa0a305ae21faf0d217d300d67aa982b00bba2c5cb5fca5005418 187cbc26662cd5904ccf42c9cfdbea54cba09d4d80c10af4a3c0996e3cb4551c d73ac913183b1ab95e6e68ba82c00be36f3ee3b539ddf52344c54e883575aa42 279edc0d07b7ae524a90401a95175f41d6a1dba6dba4f29b77cd9c1046ab9b85 78165063c253e41abac5d9b631c54a4cb4742410039a2c3df4be0557e52cb91d 1912ae1644c462d46c520f80da6bd8b984410ef38a4044931e64ca1d552e1857 d6c37f9c9caa75b2b76babe4f6b1b8dcd1ca26b1177f31f62204fa31015773e2 36bb8bb1384692fb42c3c826486eed92ce4eb1878738086a21a811c02d2b91a5 196c45b64da68f62f1515ba775f088bab80c0d9d77da945c61d2b6a9463ba9dd 54ccca3b4dd9a4615c5f6e00ee40741b1078e7975c468a56508697326667ca5d 2b2ac9d74327e1a25bba7d87343911fd037306b343541986adf371633e8c9a6c c5da03b838c5a91a5303ee106ae68e5e2194cc64c90dfad58452ef71e06d97a1 7570da46c9f2576ad90d77ca6698da9436a7a8dd2aec3c1036142492e9c6306f 311c1b468270bef22d6794130cad4ccce658cc04b3d76a024acb16f88d002912 8d4fba7b4453e82273e1f43c9e3bd2ed989758ab581232fa299ae4c15acb333e 5d81321cc6edf4586587f805345bb9a392d0ece97f945551564b6adf68fbe1d8 ec4b0b8e9127e7c10d9cca4c11604ab098778a5073437a8d21ebc61108f235d2 3f42e8321ed4ffee68bee2ce54ad21feca2af00b38f9d475d529ed43dea5dc65 1e694a94652cc1cc30e833b2a1a376dc40bbc6b31f4f1edbaa1f816b7a8ad91a 1a05ba3847afb784a001f341b8aeec594a0d2bc1f5b4986f994bb656dd9d1fae 32854e55cb1b26c31f5f63de8ce8bb05e85d384d0030f35904a8517f942de115 c1842c63cf157106e9259debb8dc8c8197ce58096845e4298f227158d15d4e27 2397f53fb83179ccd4f9a9774a164957e5f3d98d3bcc71c410e9fab5313fd3c4 5d1458d58777439077f926efa726b839efe49c460b5c927cece22258f56e4107 84e48cca2375d25fd923b1d56009d5dc7526727ffe1939bde9e1664815915b78 e86e93fc20e79ea13f147580b18d297cc954d0fc53b5c950db333e5b15e3b47c 899f187534bda8ab009c709cd9ca3083e80ae586d59e51890a2d727b6276904b 8b53c4458056c9e72bf87b703027e249fa64b655d4c1058b4d1b8a978c4ae951 72d5d4efd9b2b7d6155b6806f475e4f1aac604545c1489088a69e39269d2b49a 1aef091e942ac0eb385fbfcf854548c9836fb77cdb703dc0cf8e77e19bab47db cf3b4384ba6e1c681ff62e7ecf1e329b4b2a37cb19f59f215dcb91e4362059b4 0d57bef3c4979ea8ac4ebfa748843c828730cbbbbe4ee236697a1dbf2afda6d0 c92229b036f6e97cb247c1fb98793a507ac766096ab481254a5ae10da2c685a4 7913f049f6fba15331a42ae89b4f54b8939932ad0b53f31946af5da0cda49c19 8f516baa32a37d2086cb8cedbde3da8080928f614c4d344dc37234bd2dc3d039 f7555c3ec1166f073583f23f2d0774660bcde2963f056d1ad16f17faf77c2914 6734bcd4c9672882c4bf8b2d883c21400876ff544c37fc6422c683c9c3331dae d0359e8053ddeaee8e79f06b579ead6d50d756fb4ff96b6b341f548d0168b762 f0805ad886f38148eaa6d04919f9af04e0ce53f6ea4a432e33f4dd96cc8438c8 31062c012508105eba51477bc655e213834e18eae9921a7dbf1a750c8968caf1 d849b54bc53b1e9aa3c40eacdc629ffcc0e5423376d3e3b0738571ccc4bc83f2 6dc5b0c662d9adfda24ad8961d14af46142c5e13491a6acd16659fa13a4af99d 4b36f217bbc9e5daa23ab596e1787e92f3aeab8dee1841a6d0ca9938ff7afb3b a4f3bb9b2e1cd9355bcfdeede18c8f5f4295afc446530895c4bdc64c19c94b2a 864ce233bf7e59474e726386549732d8e8708ac37905574dc2ff77113df0ae92 24d95a1536b4794982bf863fff7691d8a1ca43d8a10118894a34e674d862ffb3 78a340f5e3e8b9d844476a7f8580979f28388e4862a1c3375ffb070ddccad0b0 e25320048e5bbc054a8eb8fbd29dde120bd70d273e1e73e1ee53fbfdfe2c8324 fcfc209f752fc11ab86c7773508548a17162c7c0c714b61fb22f6bad810ec5b4 114b3abbb2502298a7d893e8d7cf2a5ac7f4464603b4df05850d1cd58d05c5ba 72561c1e91e1ea4d6549097c8b3358eacfc85a6543e96237bcdd351f62743772 86616c785d48b5a3a19cc08ef5c1c7e4ec591626c337badfe54e5e6e477a1f6a d5fb29d9486065a3b916a6ae65855d7ff1b38562335ba86a1b28c53d51653bd2 7654a000dd5fa7baf01a49d88ce42a0cd87a561f266f144c66c2eb8e696b77b6 ecc4b391078e11508891d6df32b868db84b310c61ce48f1661186b4cf72aa5f0 8d727f45e854d19736b83677eeb779f81315f36f66529c52096f9196985d9b72 bc722d289b3ad7390ae965d538fdb2d6c4e271aa7c5233fbead4c0df395b79ef 43e5b1031a3529f9dacfc5fc821f099017451a2dc2d1474ad8565b3acbcafce1 08b9cefe7336e103fa9991557f0da42713c34cd4e2cb551ca8cdee80eef7f747 6c6321a3919de675734bd11e9be75fe04af0140ae94267828e185da2d2fd3e09 45857ba8ac367023b5a4879f17b39b3ac527376beb4eabed614c124c788c1db7 c021078292aa2325d31d07be53622cfda585ab0fc7a6cb2efa69f21d23b6a36b 47a0b1449c3618db77c2f175999ade574103d140f606c6b0466801e16badae44 197bda595f6c360876dc8a537a274999fc0ba72d72c6e15a95f9655f0280fb01 f0d7df00cfce3aaeb3258bc08bd39f32700c0d666151b0ff22b0d54adb2c4bc4 f83a9c285d8dee289e91ef39071b8fdc386b97b49b01c6565c81e5a3ba0d740d d7f30b6620bf8c644dcbac64bd4e6b15707955be3adab7ecb7a965b49faf8623 f99b8d688c5d8689299732bd89da03706df5e754cf6254f51c0b1ae9e3e5ff14 b31f0ab5f2c14d7debf10b099c353ae313eb0f8777a6841f9579a73a0cc480d6 58a2852ac8f3dd0b786799a05c3ae0d5fce3aa39e5ab2936686bc01fdebe1e6c 75e8cd139b1106fd5671a47659ece5d4418b2e21faaa037d0d80efc7aae4521f d250333dcaf9ffb0b640e1c92b650cceadf1303805e63bfe8a6f212e180a7533 be6b3a72e94a83f0a1efadd790fc0844a90b772104968fd5325e74abae33ffb3 5ad75787138f84bd655e145bf0ca655fe362bfc34e2640d1dc6800e180fbf64a 809f34daa266c0933b3927ece4943074b809f9470b9eea60e550747cb7a84718 ec5996da3ad7497854862275e02aa73f84c899de57108ad5c28b1df678235835 b0ee43932958293a48a6f84c20c3429c5a7b8fd2ad30dfb6be099fbf885d42d9 2fb586b8186c5137f4a093c5d73cb97f19e128e208f8f06b5ff915f489855629 eed3ed46d1b5e3a1679985ab0b28046f043f60ded4eb06cd8d46f2378e3ebe87 5309e5b4ff8be39bcea08934f3e911ba76612460deb36fbce942356528ddeb4c 3071de94fe48004b936e71642d58927b79b7187ec7fa481f037a43ca3daa2c6e 2b78aed85c4fa16a6f7bb8b7324102c3e4e94b44ac53439640b9dc9230fbc13d ad82f10a9e2e65eda02dd4209ab66cd9b0b01bf713574be5c6f162e5fa4ef1b6 63b4e2f15c4c65415e4951d95f8278465a67c9ee30057ed359569b5304df3454 b240a9c9f210aba8e1c9edaadef4610f69f97daccc27b69bbdfec720562d2989 297cd4a0a223e0a48bba38eb1d6137af050f0f0f617a9b64b7540f6f64b589d3 f4cd34d79e404e0d997f08058def591727bb34cdcd83c569355351e04ed49e5c f6563eabd2592a6c577405152f9d9745a4315ca01fa73dddb3ee76561ca066f8 764ef57944d7fdd3fd48e8d7ca39ccdfc8e097c38047395d4316fb0a151d18b2 fa7c716450e727edc5af2a0046833c9e7e7c4b55ca93691f59b90cab92c6cbf2 3ed62594905329be805be07ac202d9f8c2067ed29aeae5192c51d8ed6681c491 bf7a13209bab8be4b1c422d737d0ca01f09e21e4b209754c7f90156f5cd3eb73 3c317c164dbda7a16c1a2c1daf5245f860e5cb6664b38aaf9d54e7f34d7dddbe b19e7082c6a4be0aa1ab4da573f05aedec9af17cc523b9f27ef756c6fa766048 e55062be56ff8667ea0f9629b904d87c5761a0f48b05576fc763d9ffabc0a2bb 59d079397766fd1d9caf017958745fb6bf3d2635eb63a9f39335edafcfe86f08 777899471b103bab99a70dc8146f521691d0ab9a33918c6dc307786b8bd7595d cf070d42a4ebe597b6c12116c49834b786fe86f5e88b43a600fb7141303c0785 e876f9907030a6ba943a83dac84b8644cc87e51e1eb2984862709bac0fc25dbd a876200c976d8605239cbe41a8dc5d726f53c1a75db92a9a9116eddd7e9e437d 2d4c72f0fe5d2450ca2774c161ad5b9c58547216b3ce7e3945f2551ae795ae68 b66885159aee13cf0b89b95996422d00802206be31f50ccea1d7d4576e73a8ad 87350757498cefbb918029f1f8fbf67a5cf1081473c04dbeda7a9d11cb6dd2c7 a54c505717a0d8c30db184d4e127f40cefbfa34ca1b5fc8f82a8aed7179476e9 c3a731ef4f20d122473f688ac5969575fe35acabdb1dafb0e505b39591f4d010 db11d1fe8dd67a51fe71ba52ae1967c2ae3f26405ed271363187ec0b30f19db1 c2395bdb3c902c226fd9471f69422fa689f543753553a4e7e6f1bd71b3a28777 f8a9bd973a7dd997b243d9a33ae08970524c3bc6e9801c9a92a8ab04813994f5 69eba8fb10b1faddc575da570db6d2409099d72503f184f06a1f940e2efe1ca5 394a0b47885012a21e7b0f1a79b079b8653534cf6db260f261c3809947824f70 6ea028fd5dab0d1764a3398c1d5f03175524871a09d63d4858a2fcfef159b1fc a91d1465abf805eaa87a93ff16242d0bcfb3c80da162d9d91759801072e01e6c a7d64651aaaab1e206717cad9e10afbeba205fc8c77459f2552a0edc0c5ef137 eb5b5620662c0351547e950338720503692a8ba8f6cbd876005c039f8ee8c1cf 186cdd680a3c76f4bca92b4affa691ef4f65ee0759f8549bdaba580b2a41b4a2 5ae0739f3580c2992461f3d1a82a9577b45d8b4cbf91f7661997a52c8abd5dfd 05fcfb20ac10254e026532909ce822d06bcca7c56fdd044618365ba5f403d082 978fbd9ed12aa5a50530bd89f627d04c3f4004232c22eaa68d2b1be2b86440c6 d97a2895d3f56461111f936232b97962c47e22cb90b8945251b13b8407b7e61e 0f0beae00af2d814eec0adbb9905a189a99c405c39ffe58d3a3631a9a725d589 10ebfaaf2db35f87bf45537cf45a29a9c327f03a9b31f008a24cea9a7c67f17b d8aaac3a9f753a9d227b42ada628f9d9c270ea4344f09274531700236c9ee077 3fad1756c74830f27f3bfc9d022948639d456f3389eabb8ed2a4d131f381ba85 7405b0efdf0e411574a7f949460004d58ed3006210ce14e3115b2780a3ea22e1 86d0ce0e7cd7321541dd438959957a4691883c5d0290e64a329332334cb948d7 1c7159782a7a5b936b78d6b43c2e7dc5550408a14f3cfacdfdbf91bf35a160d7 11aad9965b0dd6d5cc0207c7507786a02a34ac91215832e4bd80665f65e808f0 58b7380f806b899f2c48db64534d3667bcf1d2d33131b0448df1955757e514a7 125933650bd91f8b5ece9a8b6841cf18aca90c5a3f4b2234c79c6022bdafcc54 e1ad3407e70b9453441f6b736c3eeee8821c657c4239351f08c5ad261c8f43dc dc32d21d4d2c2f92e2c9b6fc6649246debbfe835bf598d8ef506c6b54954f0f3 5578782650564eb29c36e93649ac6defba6de11d04d7583c14a04646dfa4a896 588de4861e7890d11c201008db2d563cd5c87f2df272ccd8559263301f22a032 2fac3acc62fcd23e548e6800302c6a4e3acd967e0fe4c80b3e21299cde25ba46 90e3a0a400cd506f6c950e64f2251c9c0184d052242b413d5d3319d0e393160c 21e294e044a00c827e93e19d2d591e45f9819161583e29426b8b6d47b1b1a580 97d03736b52358ed074df87adbe6a8d5945b395fe60cf79223d78baf43f095c8 9f59cf53aff26846bc258c39b6d1558e8dbc8bacac4edec879e7081d98de2317 a1e01a9f56d56acf2d75c7e2af2a3c410495ad2ef7cf27efb17671d321135d9c 2ac6ae9e5535416a8472b3f79f8a59a7916c90d7f305e2558a7188b5357bb7f5 46b87f03b3a4459d5a7eb64eb493764739c0566746bd19a9040fe4934fd4df58 976cff7e24147d927991763f34f4b78b727af5f267e886eb8ea790c14cf8552e 9724359f58bdbf6d1a04756b172cb64a831c95e4a0d8c287339cd76e30a837a1 7305f2def984899e638db04260b905431c1de92af1c0c1dd2e71051daa6b9a71 93d3985cc7109f24ffe6aced99805273c306a55592191a1fb42979268a9e2e20 c769e2aea174e39916cc5f7490c592ae845b653903332538541b002c31650b68 bdb6ccf52d529405d14c4c65c44a58a43be81f92fe1231939eff309122c5246c 594529efc5194dc5ca1c9a20acec335c29f9d437e7790791c3c8077c39e86e17 85e1bb0dd3f47fdc3ac88a2e091bf6080ec75e2310fbc250c1c9c67ac36bd357 146a490c50c4c3f5f90d4dc7beaf3c14bc5718adfeb1f4152f104333507cf6bf d59c6a80e0617cfe8d76ae1c9ccfe506bff2df3b2be19f87bcb04588a4008d74 d490216f2224a877fb504708188084229b57d92283ad3ce9c7335ee6672cf512 c9e6fc62d2713a6c22ef3f7cca5af300b2577c5ccde291121515ea1fce9bed8b a3d60a6f0d78b16b6dcc9d711c72497f5c201e1b561dd70976c8d8d5a39c4af7 bca4a57466e61a8f7f3dedb41284f9358873894676fa15970daa4e30ebe2ec8f cc48e731b61c5edf80f91a54b05687fb8d881626657324cfab050e58ffaab2d6 1a52260a7cd3a525a9ae1ff8e474606d146a8b77f3f77f9dea65d509fe14e1d7 feb37f4784e8fe6d2e2e6d381c0e37b89c00f968d2658121b631eb3f882ca50f fed5e034729df08fb60ec595ea40d8d17ce21db68e96c71d4f24c62ddbc4f654 fd256818012abf90eedd163015a18c46e0269468632d2b49eff77860bcb6c7f1 3cbd9d92dec4389fe20f4052dc6a1aa6a1387470a693554c1986001b3e1ac571 3a06927d85d1d1f06184a657417cde22cacf0e4291c38e1b7768f94185122f07 435ca725bc7011d5a5bca1d0930be5e94f63742a9c8a91c08c50b328a6ee51b8 f1e4cd6513b92a5c4501da1cc1455b0835b81d2bd92992316fb28cb452f652ed d41d3402cae6699dfd4028144b996cd14c21fae4b18409444872f55d8dc4d054 32d0e6e9fb1944f63ce4ea4fb246fdc0873cf8b7cc5eaee31107061a8838bcfd 498529e9617ee113bc614517c50ccc45d071337aa10f79a642bcaaa7bc4642ba fec184560b6b01357f68fc0256dbdac3bfdd9ce7bfac143289d1122ded1110c4 7c0a5e7a6d17ed143adf01c848f2d7987f46062de72801255f9afbad32078a35 800981e0a6491d1d58d5a5c02d80a3001e3da7aec6474ccae83f28942794261d 89bf3817e19c0121d931fcef4dc7646e6e2458845734eee53ce089916f074557 e991dcd523b38b5dbb2a3e951bc956592d565c3fc5196681b8edce7b6260b709 58694c35f2d083fb4d0740b97b2669b9633234109a4c85608b17a3c669ade9b3 f63622cc7e79ff7384850350e78635a6dd0d7adcdb9b44e7e89caa4d1fe18e2d 93866031934491227fb18bbfd6d8c6d444d61048f17c5a4a0ea884f4288529d8 d0c91d9e8e4c5299e0fcbfc990b8b3bebedc47171fabf29f9775b943e99d08b3 239de3b41a06bee97e14cd8f45215fc04d83eda1a744d7471c5d44519faf6a37 e4e4cacf2c2595fe78020fd040f8ec5c39523a8792ea5b1d7aabd293b66160a0 860a3b781290162ed094802aab2b365833b4faeb24fb236992660e962f3453fc 5a11a3c7818f2d1af95c561fa5497c32937f83caeecfba333e5bd6b6587a4e93 853fcce73b4a7188d36a0c0f5cb412c1977b131160d49dd18fe9ae48bd52c27d b52b5a3b2994a8612e7ff9c638ecafd5b4bc63b4c4c90def403e813539ea0c16 3801e82eb54d4ca09d969f641a001a50590f19caae7dc8ee7588c8ee1a8fb09c d8af127409fe137f0920961f23e56db730929911a2c97a88c47733a48593a0fe 016ddfaf7d7987f2faea6981c2aaf45b9f169b5dd4fe38f4c74aa49205198087 8ae37077561fe909cb393b3ac133f39edb89156d81c30a338ec80deb9a730488 e421dd313785ceb122b851e361e8a7504afb2f2ab4287e1252c615695559ea5e 961cbd14d6d10e31ce08c83bfcd2a4f8fcdc2734b99518a67b56261cdd41f129 1845f424c7d91154d48453e82d8582aa64080c5849a29c45eae1e3ffa60516dc ee5804f023227b408d5c9852facc1b40a38f8f06c066f6e4541a1d5930f31b0c 7bc368007a5b0e2077614fa060d5e3895ef54923f098832ca3d511f66f9b12ed a6428feba35e108b6e7789afb198b8ed09242655149c62635aa332238a8e2f9a a42d1bd50e3ec260a5873ecf0834a4f4f1c77dcbe1a47b4346187f2267f658e1 836d1b01642a2fe994298d0409ce7568816fbeac820d1a01d01e5a48b7afde40 45a0e382379665d08cb0e38777e8dfbdbf5229fb97f5ae118803748c445110ab 3cf4fdb9b0969ecc6336ee896085eb73b7a7bd8f75b96a12a5051b7f37e8e5a7 18d0d6255a2244b061e1a49499a6d0c2e5acc1b6b80431e542b8a0b5cf1f08ee 37df330e5ad3059ad8a8fce4504ea77c6b05032285befce0f48829baa3ad61d5 3603d858de9e0f3adf85b9ce1494fef068497e4f87cf70199accbcb6112e9649 4892171334423b7fd703490edba9021e06b82f38b6e95ec646f9f90e6db38e0d f49a95ca69fdebd57b181b360d34260e390230b0299e692a066465356462869b 46b04e519d7a1058b8f026b0bd0012d1d077999e6e8bce0194cb1327b9a4bdb3 8410a81b2377e5bc0217e6fc4a8345ea43910e674ef1de173a8ce69a156e98b4 eb4482b21a137b5b728a40121384b674c05865fb3f27a864d5800b7360371587 9f46096061d16f67f3ed9cbe0feaddf7ce256168599591b7575a2895c2b0470b 5186db078cf9f4a379048ad9b98ac8e6241228882ca3a2330234fb2a850543bc 732c086e1c66f827d842d9a4bfa938432365358cb9b2ab6d58ac05a68c2b61c4 9093b782ed02fe95e7e8ba00c8a37dc40fe1ce61cef424eff3630b396139d419 cc010bd5d06d8687891149dbdd9a53887596d14aa338b344394ca23375ff43b1 1d90b6d32ba71b739780ff3a37f37f74303ba203243a3abe7cec21acf6aeb3c5 d657440bbaf82aad9f56e74841550a772ccd6c6478e76dedf77ef59d2e5746ed 7bd18d68dd98c84f8fb1f42992ad718e7210d8dd81796849fc0d042f9f908422 c4e2e2bd5f419c15f90f0ff57cd13bf0a211fdb563523769ed7a8488c47fa7cd 460c50dbf3cbca9cd702bd59afb0f5b11340349a4f2f8d7be5eb38c7236d2e9a ce317299927a62bc4180c5e41e5252abca7e155a82d252fdfafbbd651de45d52 758040412435c029c9c94246b1b5532cb1795b6e1ec31110a7a32e171f5995d9 dc2f055b1f1b490bebf155809eb49eac6cbee99bc5bd1d934541838fbaa33b2b eab3163c7ff55bee04ece9880749a368fc9233c01c3171f138f256d874b56cfe f00cc062dc1a3b4311d77013591661fab99488991c7b4f11fafbf6ed9589fe8b 768cfd48a954eb3383ae2281f75ad2ca37118cac8e376bb1271749eee95c5fa7 16170ec054984ec69f91f8567496008f75ba99765b58dc73419d6c0bb8af3f6d 77febc6d211e3971b9a0ce9ac4858853fd7715d5575e38ece54cf6c770e337f6 3cd8b7cb530ee0b6dd5f223ffcbe572726c3598c16f6ffce16f3753f1cd5923c f92bfe50add52ed6f432237ce677194d04d6bb1169b2063bbc102638d894770a aca7388474786624d880a484ba876f2ae9b292e543051cb0c44dada4e2e2e7f7 3d0aad3b4985d57134c050edae56307f4ebc4007db4d2225787eb63292d95f6a 40cb20d45ff64d4e1946d1318a52bfaac029242c7d33c1907ee05b4fa3b03f24 0472d0ea6c15dd655ac51ca7f319122b58c0c6373edec801564967eb3eb588de 5ecafbac0f38f4d528c0c017938faf45cc1e5f38f7f5ef45bfc28ad81ebf70bc 1e21b275de690d5cf009f59e48b8f8711676aa2b9449615e729bd42af2f65d1d bb86fe86f5e88b43a600fb71413520b7c547e984c85718fe4f23d7352282cb7e adf80dde3621780e174898529ef59f2070d573a37a714e306db3901ba14fb07b 8fa1f916e330cb2f5351bca28edfc22d9397fb56afed05ae5a6cf31607e5274e 88eba2e58fdf28d19ee2980c0c59e1ac0272b7dd961a1dd81b982639f31ebd61 541bc6bd3b3c4bf5e0e1fd9c1c68f4ee797bb2f7c22433c6532829037b87d88e 2f7f90bbba4b50556c9dbbfacaa9a1ce99a60ebdc6fdd2ec4ccad91c76f5ebf9 58e635c2c6be7b34b6991a4122e6f2a88c62579bed6379cfc8109784d346af60 69669456f2825d173e314118cbd3e0363693d84813f4116fd1ac7a8926599e7f 0a32ed21eaae01470798ec117fa8061d28bfab648c9ef8566e4e678fac19dcf6 3ce3142c394ea58097d03736b52358ed037c7472cdae0bc6728163a179eff3cd b0d4ff879f749e6dd3eaf1cebac7b1e21c827270da40b1d4ed5657461162dbff 85cd983516a3e8635e35451f925e2641070c7905c42806f755bf53ed11f37fff 39d190c3b0dc346773438112f51403d3d7ec1221f6fbfe20fa8a10ebc38d2ff1 d0f88bc21932f1416063b6f8c3899a22bb1e27c3d2a0ab004df2d30acfb5ec34 4449210117572644859a4fdadeec25fec4430f1b20d16f3fab3f5cc74a96f6cd f3d4c8bf5c76058f4e91060a6048178853972fe47ce0a06d99a8ff0b66e88fd5 ae35d16cfa2faf60445752461138b33d70f051303473b5856aa60e4de0772561 279f52d0a0c3bc0a1012132e7784e8553dbd516e4b6f5e9adb8a0154ecaf4d41 42089eb1d589b4c2b2319b09d38184a4f18173ec5e40d24b8822fb56a27864b2 349539e20a7769a6aa033f51751cd8ebd60b8cd47e6494104836b6cbee567521 a9ff42915869f62cd443ed1931ddcb42b52ce74b1330935069932f3d7f85e532 907f95bbd139543bdb96ec23d16374a96bb3c17ad997e9d382b5ea590c93ba9b bbbbad9a8ef1b8aa1151a44210fc0b94195229b525f3290c2081cccbb042912f 4bd731eccd9e913c09844b516f38ca103b0f2c3260e51f4e6fd509a7b1e2e08f 5529487a87fb30228dc83f056b23d49fe15ab7e6234b2c510c7de3243c9f28f1 2a532e678ec55a1aa2ffa31bfb8c3e980ac7b811f04f36f2df896fc2333d1613 1d4d554e5839bd67d39504f28ec8669b55803cf41d410289f69c311c49d7a958 a59ea89bc16c0afdf948af963b62cab5e5e3253e741c057148266ecb707f7fd4 a16f0aa39dea6f01503ea0db7f80c85b5368fb2016b56b7578e2ba9774211607 1d1cf0e0e009aeb14e85aa99cb802e41b372a3b875529d8274f6847f304ba130 477b87c2a9f1c35f17cd9830c6f8850da7a1e7869859c14ceb844deba3bea401 d9b4b0d04a3df4b4c2442ce676a3bc30e4dbe212d607111b56fcc33b0b6d7961 2e8c13fcb79b19eb667d35da5f7d073fb462758c0233231ea6e8e5f387bfa3a0 98fe314fe05895f1b5340523b2c2a5facc78c4ebb88a6e2aa1299cdbcb7d74ca 97e6bbcbfa1d787a438107d744189df2253dcd349c3adc82b8cf06bdb14c51c0 40d9fa128466341c9e642ebb6943c97a5ca2023fd3a9dfb40b323173b4d39b83 468ee46174e047c229a5e200eb71e21a45807356a5c0a7b3e61bddc2471b693c fdd07dae4ee64df08c942484ae8ba218f1cf06520789dd64e26550a644a1ff5c e57a5fee2a6f88bfb18a20d1e27843dcd91ab096194b618337f1cd82e8261e6b ab65fb0c42ec32feda855f26f57c593140b1cee27a4758b5c87ed6d3c87a5e39 3e6be744bc8bd28d3ce5b019d5d299a093eb18e0def41361b75a040692ff9039 70542d0804065317cd503b7e347b21aff375f004dbe7c02bb6607d1beafe5739 ef329dc47a4b87e7d882ac558614f69953b3edba56d14c6773f14736815fb63f 99d3c6a417c46ea6a550b209f351f3cc7849272470d948bed6e83c12d4015cc3 3fad35fa4c22fb6513e814fcbdfc96ddfa0f1606ac8be9d85bc52cf0c29b8ed6 4cd8c81ca40632f5135f3098feb2b7bebfd66ec789c5c1481bc4e7b1a05a2c14 e1e6b7feb9a4ea0b08ec8dfdb84ac675c5d1a2372cb2f5a729078dee052d17c6 824ef3ca68a552aa2f9fc909d44350eb6e881c1307903ccb614e088e411a31e2 6349becbd891bbb7794d9b1c67f2218e1d9d1dfe75a53b46d52accc860485470 e1b1113a2eb8643021282f1ab0e7ab96abc54b7f2524f863387101e9fad3d14a 0d9d8c235441cd9c2c2ec9bbac9fb3e1b14df20a3c2e744848a1cfed04dd9f29 b4f3fc4506203d30cda59883caec8be04351c6b45359a76ec5d2bc56b6684bd2 9a51f5273dded2093d30f0e49c598ab6efc7cdd2b0589912ce1c111d70d2f3c7 6595d39ace4a77b7f63489bc44da49a929fffcacb8579ba53f8e525d841efce4 35286daa1484360410dbeb7386d128e25086e3c4e207a12dd3f74dff23a95eb4 936ac18237bbe201bc5d78677ba7c1ec658a4b34ca419db7e95e464aeb830f32 b18e3fd9e670839081560baa674fda6455aaa68b0c630b3cb276f41328922462 ea7e119cccd623c955ac7085baf26c20052b2d206c8647baae14a1525379bcab 1b2713533210c7bdd9fb8f3990f1e9f5dda1aaba0258d204a9fa33ca4e910923 2bdbc71a1030664946bfedaa73f2027d05ffd75fa332259cc7713a6a9acbda60 f226d8648a2a3f7b8b3820f9d1671058333df8e7bfdceabfff9cb91686cae21d f79262f9c7267fa9022b3b44f7c4747ee0d02e56d98c75b8e4d66bea0b9961ee 80cb6694f5a6fbf802048484bcc0095a32a6bd44f06b1990b06bb0616291e572 69089c54971a01a9cc9a61860599e1235a0b4f8b66292f92e7704cd1de412cc2 1ddcc52aedbf4fc63c590956348ca25049cd847cfec2df96c79afa3f287728e5 89df1dc912b50e974d05bd1e280fafc2c24cfb9939063fb40a67c4c563c35fc2 aca8c1efec5aca6872e9acbed46a541b922fb8ea41ffa0129267152ba1a29cf6 437a796fa60a8fc6a208377eb88a5258081e320faabaf074556324ab4859c5b7 3344cd1fa5213c8e3e3c3d4f1eede79a06331e184d41df24dced68a4b7fb476a b580841cdac0041bb6144bb4511a4c8a4e0f40130a38b9aa96354d4b0db2df3b ea7b5dd4f6fb479e9293aac7ce5e7bdf5a6f83b1ab84a3ae648539e2341b2eff 15ad2aa2950bdbc901b4e45d35ff0bccf28abba330e81f2296b1655e53684824 58a30bf6a93029a3921226691cf78a8266042413abde30df00d2addfd7248a1c 701fc44bd0ab4efecbcfe5bb371884971f40a6509a764dda9c30ed44c969d64e a84e799039f13991919b6f05e36c7d342320002abf75065847900fe208c761d3 6ef55af155d40c9f43fd717abc1d75e6f34a84a07f774e25bde1dd5c58480d10 5d05c596d424e14a9ac7a0c7012a15bf53bf99da38fe98adb7eb722a1c01e028 37971e78ffc474d0b170f29630c0c28331c93ca2978f09a03c2131505afd56a9 29933335743a4d79c2078d9f267ea0a9988936721e29dd68d9a4d107c7c7d8cc de1f4a92ebef9482181b7fdf5f06aa1e2d05e388b9cbbb61093af548296fab72 07c413db50567cf63d1f9ad78ce264c572c64bd38467531eb073990dc2611f25 2da2b42d40243f376815e372c8dd9e87e03aac39a7e0e366d6f78de685ed0cce ab2fbe3541dbf4dbf905435ea7d4a1f287c64c8b9335c922fb8507fea85ca20d bea2da7e5ca0620548c27f7abc467205510d8496181da0ce71acc1867023e7dd cbab872750d4bd49787c509e56107d8b33dd1900d3eb3c3e84e8b95d804a20df 27298c3618d174bfd90f3b2da2a7ddad3dd1881d14911413bc11c503857130e6 02576d4337190c3aa3ce0c7b12d12956ecb7f3b7dc552ba9beb4526c9e4cc6a0 d869d58a9d1314afd545fc29e32833ff7645fe5683277cee8ba4953bf1ee90af 1df70e3e7b1569a039d7162976c668cda410f7614f782eb9677bffd315eab683 05074779d064e853b12d7ba8e55947805108ecba95b9169db47dcfada896012a b56edb8baf3346738b5b30ab8779a6db499cafca0f6ffaa480e9a24c6ac1bcd9 91c796157e52c007b65e8a60602b9a75f23391a11c26bfd7eea9e805a8fd02f1 7bdcc50cd808fac8e26519d0dc7e44ca32e72c3f3a5e09a7c572c1d13450d90c 2d087a4cc0ec88a96d7f270df71f2b93252acd89aa38f8bec7b54114970073cc 4eeadfb1290eb1a5d3a04c483d1588b17699f12cad06895cd91013c41b08204b a6612ca31108533b749c88e876d08a241c6a944368ae884f7538b0f44ccded6f 59710a18f01dbca0743f0bac66c4a100088c19192abee78d5bab8f6e4e513f68 40b817e8156d9c84283a67a423d1d27db7c863ae64edf4d3377e00e0ce2efd60 f7679d980e81995eeb143bdc7a72b6fc0fe79874fcb71945110a8e39b27ef93d eefee0c5f55b8f1beecb2f37beb5aa7625de6204e4acbaa6942f5a707a137663 eb110898dcb47a07ef99ca784395164d5abb8f7a9f1b32bf0a6ca234de14819a f903811b87966af03c884ba7c3ba00be7510829672cf8f29742866a4f423d6dc 2e402140d77826fefbea8348ec1335bb326aa8656547096028f4429f2e9d0b62 28195ecfb00b7dba5042548e6ac023a40d1876c31eb0c0d6d2515d5e6fa591c1 4c0e29ab419f956ca5a43efb8205479ed267047f25000663c892625f2f7e60cb 48514cd12ecb23b51ac1f29751862fd626bb4ad14c9efccd9c41bcacf269da8b 332dfc78b86578111483726d74c13ddee4930b96aa8d1c60debb788fbc247a95 249e10bb35648b5a9e692945f28f54e6538cae96938d4ba7b9493534a4e77686 1e4133eb48659a6e572285bb70a71a394d8ee2f18aa9a276b04a33809dc2160f bd588494d5e9ffe4abe586f7c2623e9be09b4726eb7f8ca90aee7da2bb3c8eae 37fbbadb5d822b3ddd8073948b8894119151f403cc42fd301d83b753584553b0 66401c670204042620f2d487afe7d89bcff93713e7a5d370c932cb84d9a0ee59 12cfca3d8265c13049c72770cce3c322f2d2917a9d0f150600e0c25352db4785 b5c90c47daeea24ea1121563d51a1371bd5dd0e3d85884efefa14e5985459ee0 742e3a569df0b18d1515d64a4face2389d359f6cf186feef41365781ade25b7e 14187b1fa60fa6c2c597e4f5fa705983c829bfe230b45d13cff987e73a0b683b 7d1d14305b133ac14be61f24b396df198563574f04c7ac66cfc518cc131c7669 8b66753a9bd8ba17fc6aebcbf58c86bbcd7f380a589d13434804a5db4d5cb3f0 ea217c0de9f0840ac9335e4a3175120abdaecd2a4da3e6b1da6fb3a9805a35c1 29caba93c505f12787ba8aac3490c65747aaa2edd46f83220ad6a8f928830aaf 438e9acf914a90c71a26f91305c025d94ceb5287d3ca0a3a59f3007c97e48129 34bba7129a4e016f29eb25f9f8edc6fd36e4157eca83b0469c0b67c943315203 7f007b84948297a90ca564967e2f6ffe0248f96bd429a0a9577790539ad2a926 104b2d6f1112f84d129aeb8abbb376e2f4157192c8ecbc09f8360b7d6e6a9222 918beb752a5e3c64a52ee2a53721311b14fd5f0065ab125f7304901baf30cad8 1d4fb42102551b463e649d3435f1b227ce1eb0eda4c91f69c582ab5290c737fd b7ec320428647b45e83291b48f91b1130143025441f47549450db99b1e721986 796e543b4d120d83f252ec5f0c38fd0dddfd5842332e0f3cf6f2c90be3020ae5 ea491c44a68b6defd2933ee5e57798689a82d4e667726c032a92b0cf9af4cd2f 48ca27250e056b9c1f408d90eaf38c6b888a3608b7ef7b3b31272c19a1f586a9 43ef817fe67788a8791441bb24a83d46508a00e980d11b1d516571d95391ee6a 953b805f8b52ef72e19c6590f33a70c0a7f636c88f1c988fe60af008803aa8c9 a5e1f7c06bd94f538ced3446efdf94680d04edaae67f0ec3b29831af077f533d 9c320cc23490599ee2baa947bb25e77a750dc4745888f19518e49ef3159b2ab4 5d80a9330cc50b7f24c8a90f16a8f0ec067816ada21c6c7febfebb325716d79b be012d87fbec5e58f97035666c5231963de0252c23fb70b4de736e12df13d43a d454b209d43aefc4e202a5035f71d111221e2f6b2796c127dd3fdfd9d32a152e 79f29fd1b9e82b0c7cb4864bb724282f7cd34352c146b98f470c06c007d2a6d8 181161c7cb1e002f511efd6e096b873020dc52a62720a0586d89d3723397a0a8 48d09e8be4a6e973feff9fa60ba378f0f459c8208dc855702217000ff9d0458b 5967ed3241ffd72dce1fbc6f262ab207528fd8a00e1c81aab4b2c3721a0ee278 4923024ba4f0826066b2d6f9b1fb62412a188a04c81650b317a55565c55894e0 97bedcb0f6f4b03a0364a83296962a14943534a0e4da3e0e24f1bccff82c05a6 fe7c11602c7f5e4639eb15d5bb9631163a29640cc079fbb64012ca18944e944d 1e851e910ff0d857ab560c38eac38c52413a45d0df37e48fb8f3145774955f58 f281ba245368384dc2ca15d8d7ad2a081671e2c05684f0c21c68cdd0e5826645 ae60c7c81b68e9ec8ea8b6c3692a622f60c4cbac3f7c61c51357323ffa63fe50 d78b15687067c0e9898b7d9f2da2e97e467cb730a2aa6cf5d9a360edb5aa200b 8867224ad229c04af80e05ed3b8506621242756398c8555dc11033a8a3cf005d 746c6788a843f9ac41f3f7045a26fd17fd7f5e8c6a5afa332ec7a329cc24bca5 4a08e9923431f1f194c19df5378c247a992fa8ae5df70ccf1d40ee3be0e25eec 2c7b3ae5ef921ee2bbee77c1bbdb158393c8d0b96cd407c5f5786764afbb9e0f dd29f1b93bf806b0bce597f51f67708f89383303fb5e86a4aeafdb743d760406 d0d82abaf25ecd7a6591b23d8e1f91ae9781043dbb1c2db1a3e3cf9dba2022ef b6aa1b78c6687237ef1073bc6bdc065d154633401ed2227705f489f6dccedb94 a16c9ee7127f4e13827c05eb7731fd6c9b4ab6af9447808622ed9a31ba39f2ba 65501a6b39ad87b169c59b5e867d093078e5a1d5f5f4d7250cec2706640a05a6 4ad17e630ee0906fa1c3ec41387393d1c51dbf2fea5721f43f1cd62cff4b1e8a a0217bdd4a522d4614c4343b39c62d9da80cbc691e8eb3e8884c70dfc799df69 fdd4fd71d130808edd06c3cdec3307c0571de702c287c0af724d933ba3ef6e17 a52098d83c9d2afa6ebc72cd8c88396a41f709082c0357021cf06b56fafa4c82 afd4735191d0336d689e5aa748cc5003b0d27fb37ed173ea9b57bf008d57f28e 51e6b996b2443aee2e433084d78d11f8cb812fb7bafebe36ef84e4400905a208 a2621240b94d6f6d1e78ea2f790ac1a601a50caac42ed6fff4a6359eff0af249 681e481c6cc72431b96d0eeec452fecbb8f12de03c8359a140ab8b448644857d 77c698e773380cdbf0b20097e5baef9736b34ee9e5c931550d1e5ae2cc1a0fda fea53b2b7f0145c88ff12aa9fc3cac0ad4ff7eba4c023b638650ac2ff63f8c15 247a5634f7c5f52d40d50f0f838a481d871c90a6177699d56cb71728f7675694 143bbaf92cb9c8187f0673f4027afdc3a82e1f1d132c0adad1f1b5a5d88d3e55 fbcd131decb8aacfb987ecd2b08616d2872a57c092e846850af87ae9e5138e8e e393a897adf6a604e419e82567554eb42c0eb4d986f0b15d5241fe394dec0b14 5ba32711a7551846088d1cbc6df53751bab7bf41f1bac7fd103cf9ef5a013f6a 554aa367dbc2f60d3b18683f494dbefd7fa6e69f64509f2fd17dc2dc56675b22 562f67799eb23aee0601d58bc633f7efd496eb23156d2508cbbc5e0233468031 abb25e7ef934ba1c1f11194647a41a5b8564a2f7074e51ead191e07eedfdce06 4531206d9f45a4d3a6c973da7d4179970bb23ff7ac306a9d8a86f3f193b02456 99d5e09371aaf0abe0eae637bbd5c2e322d05792b8b0eddb55f8ae1a29c8f893 a44b46f09fb51f6cca07bed247cfb79d6e165428dbd6762fb852a0b50fd71bad c8a67916b4e860c8ac455b8700c4bb3d9210117188431ac09ef81ed59532eb7e 46b995e44dee662453fda4a035f114795ec2b24a9aed1fc24c2740a66182d44f 4c887bd5466bd0423f187cdae256bb2ded376e6d01ac915f69b46a72cb7701fb e39779c789a6d73e09fdad1fb96cdc15b5f12edfe4cc6d4683307c20f4c29a04 ef891fb6c9a653b3e590954c37882242dc0263e5e78243a01ff1b7f617b51541 026ca87b8e1767ec650db9a980a43046139977af540b557d833a5f91dce1b4b9 cc2987180e36cfd06ac898f9fda09e7923379b544cde3f19e53087ac5520d6be f27ec7fdda688fa2e2521d9aeca7640cd0c67fc4de3b78a58f2f2713c596790a 5397b4193dbb7836e5ae4e8f730fef0072d80d5d08db0ea77aed7b777a043169 1f7e4a6c19569395c8b62491d4773558389b3632f15f279ce76c4ea36d80ca87 50739d01cf9ac150cc71bef46cf5fe7f5b269494b2e45c36bc8f6e3bac6d4ed5 ddecf72da7a129261ee1668f0dca9709912700c218d6c75995d9211d027997bd 4d8a5e783fd01a609ce491b0a3c5b9f1849d3c7af1ec29b552422f4cdc7ffb68 a187231313e5bde29a14434455cec6e59e1fd22891b71b8b27ab577b1080f7d4 dae4b70fd59a2618275639cce0ebe8c089e5199743447b147d2862a4f86c0d94 acb7912cc3e7ee94e13ebed8740040502cf880d8a936054e2e3f49307a96a482 e52c0adb899a70b2da3de0d344bfe5f26dc38576fe0e49725abcf892cb67cdf6 3e4ff64ced264130113abee27fb917e8b01061a731558a7f0602ddee53f846a6 3ead1b70ca3106459f894dc8adfa42c0664734ffee165435c3416e33d7985f15 3342fb711620de891e05dc47a75fc2159eca7aaf99ce9babdeca2086d29b2698 b4bf0e59ed9a4bb1d8abe07d0eef91635904a2db297f52373b013098913984ac b4b691a953923276693a46072ae22bf31ec035039a2ec831da15b787d45a0bae eb19a6d51fc981371be12af2991fff9c34fc1a3fb33025e9cb4ad28edb6b2f24 6d80492f5b16400452e82aebb9d5356a1b83166efd47e8b3c2e85d967978c69b c97c80f2f86f308b8ef57f831455e565a90f883952feeeaa79524e82c46fde1b 5637e423903fc5ebc87fbb87a9296f4934a8dc25d469f23e0dbcf737c3f91b60 578fd778c9a7cee3f170182a43a625a8267a6d31481d30c1ce760469e099c081 4efaaf654d13d115ca56bf18112332ea987d95699d42513e118f0fa10b7f667c f5248a7a9820ad4c4b3004afc941b56d979196caa59808d6835ab662e02c5638 3fde509873c0eab21a23bbf820754fa3e4cc97319c1a22c137880cec1be2283c 67a5a60c693211f6e3df953db459a95b2916e07cf78f0a9f4aa7accfabde245f 8fa91de3b93139a1bfead75d282e1480fa815bf032e8bb7c0be437aaaf40e415 114037973d8d7f090ff00aa453915bbfe30d5be907fa62dbf142c7d06de34a02 9e60b0f5a9d818a41dfb6984e145ae53e27a10ef3e801a6fab32b732b6e59670 1a916c25b8d6dde4ee6f533bf86a29228c32be0a7391168e0de2d33a19bef7a1 21575558146e78ea4a1e918906f47211b23bff484d9e8f5c74686e071395bfaf 1cffbaee544040ca5a773c778333f4de2500fe7e754f013626c48446c88e7b7b 09b8166c4a35e70b8a23e54acce058aa38bc03082996988734c6331b5fc936c5 3144ab1081b9f0d2640d3a5c56c6ec5e841da4cbf1addfbdb69d78ca9a030a38 bbf1f50fe6e8c17791b5086e164a844cc12ca8ae77ce726eb0426cc567d4a1ec b2cbbb56491fb2c61490c00950f57d3e7f445f6893dd943bd214f5efb64d8b13 e59cbaa2fff2f469390f7eb67d2b698cbc1de60240bfa546d062da830580a097 af74715d9e9faed2796bf01380857b3e0b25b73ee233825d74057355f7bc0a86 96f8caff67191b8854b73baf714f33ae8db3c5a088994764493bcfe3b1e53a29 c851ee125e57cc7acccd16e2ddce3205e41fb1acdb80c8309f0710d10b47f85f fb0d99166c9e8d5c7c2a03fde970761a9c449fd6b64b0294e9f1515920ab8c73 6e30ca4805e04e6e8d59e2b64fa4850758d4a396fcfa1bf861dee3854b1209ca c27d7ab21dc56e64b1e8a8c122d4cfe4d86488e1191bd7717f2ed275467a1513 5409eb9be53e64c7022959fc4062b0a8ae2d3e8c3d82acb80f7ed0de5bb46de3 052a570aa8510a3c61e84dd46de67b609e5af4e21744a7adab3c84f40b20e12a 4232d71abd50099fe1aa314b77cc8e9fae8c20667b51ee9e2c014d88b5b3370b 5cbf975dec8e0518bdbb0facd231c9fc79f75ed2df84aa12910da5b5821a1d0e 941b471f6b441431f90681f34901d6fbcfba80728f99e483e5f6d261da010062 d7f0e7a7f28d51293d99d55263f50055f08390a5b2d19d4e7d21e758442eb2f0 7b36a574f3704b796c89b64533fe4b56d7466ce1169f35519b7498563c328600 659020840f2029195cb60165303aca82bff39b85543e8405ce605fc0e4c509b3 ee03da4ebc2117b8c53e9094404309ef40772eb68fbc53a9404f8856a6f1eed2 c19ff3d37611157b0c074c521ceaa283fd789693e67935807be66c2b8c9cb463 5635345c8fcaa617c720abc2031cca53c8c9f184a313bc642aae3277025c1fd9 2b8a3c8c6525ce816e79bcad7cc3a0575ca1c15e790da9278b7f0d52161d60d4 accd6524a200f8f0e28c58492a73aa353ba3cc5d08b50d4069e0d4d3ed5b89d5 26bd2b0d30c6ccb0c81628b297dd0a11bd78a4197dc71b8e526a07f94119c41c 31068f5b8577412b249827a0cf9faa09bfd2c58d1895e9152789de834f17e0a4 b289661753560e126a86c36ffd623a16905b25a113281e4a5e4f23ba18d76dc2 c4eb55b4b2adb37dc26f8501b415beabf99a50724676f752508cca4f2d377d8f 3fbc4ee548e32b2126a1130aa7ae985b67b0f22f23cc5d6a6ad91663579c230a eac6971a114cfe4722dad2985ca3d8f177be6d25e3b46791d076e7b41472119f 2630a9b127a41d23c4856a62c6d4ddbde6f60854645fa885385edc4099501890 444e1f8bc5ee304d61e90ef516789d1332503392b58a1f4edb6e3e51fe46f600 255e6e67ed8a1bac4a1f664d2f6ff7eec03662aeac04898137b77caa4a06b39e 1d02a2400ff8edc0203ff69b459b34ce1893fc9f0906b9bcffd6cbc16031e287 81ab8fcddac8d46766b2fee7f1cc1680c761571630ffe3d98d003b9c54ac600f f19497f857a87a8f094de272956c34236a0f7735a10f4d9046fb5be505bf9155 c8ea406dc75397ac3a39a1dd9e3d826ba947f3c2d9bf86e562f784f0ac9c0015 6ac966d82db89288f9fd528f326a98aa4ae52c01f37ec35edec50bff538b1c27 c12b2a40d381ced5dac57265b09e2b10e8a8be18512d86c6caf0bc4ed62d85e5 7c705fbae9a29ac2cdd04a39350f56340d21f2a574338c4812fad0178fbe6bcc 9addc2ca56ad4d2414189e510af6ca91f972337b2089ccfa039539e501ca93c0 077cb632db432e8f763f89008b54409e9a246adde329b1959c0195a65a19dff6 6f2ce2b29598c64a4f9f403f4875068189c37dbd26d898b4d2036ee9d66c0a47 bac97eba09e6cd35efbdcd8580e3327607c4695b955603d65d49b27020b4df47 b5856b82fea411f32316a88e1588deda3b4cd8c4d380f6d7e4985b1e5c56adf6 8c3f823377629c6b7803bca146f598d9a81ac8fd24285e04848a7cd04cf76423 a75527e925a56a94d34aac1c4f4a6d6fe5d7abbdc5f8e13b01e629b07227c482 a95306475af166614162b3c959943128659d78615af9436c19a024cb9ad0695d 00eec8af046c1b46903b258b4a96ae75fa79f11fb500f112dd7e898f529e83cf b2b0f0d2c362bc6a3131588f45f716d03ac26d43ed6e59160fffc7d004438066 9e37ecc78d570959d9b5966dba2d05d0c1f9da72f62d4590262a4c20d44e489a e5e71e5b24e87a5f322f07d2e3351b9e7aa51c722950905bcbdc781d9f5739f4 dfc08700668b45eedd07fa3eedb79d1b874c75a4ab1eb586763a9ccb902312ff 0d077bbc7cbd50c3468bf6d3a8b443516ac729b0ef1c1d47eaf28f1d2d990978 d442c274e7244cb2f3581618a8e03528f6aeb6693a95674d084490e05ad99c33 1d29ddf587dac92964f28b0dc417f469e5c167373307a25b18e1f79b954a2f87 530c3ac4f3f386baed26b8eaa493881806a3ef2ea94a0a3e255a9049f1d2e880 b22d7b80c68baef9cb138cd0e1489b4edee24aef9bc1f575211718b7ead30ee8 a40080ca72908a94f4988a736b98579cd9eb7fdf2c5846fc366b789b96623db8 99ea3d6d645e2c69ec2d80c19aae44a5ad075ee4263fcc1eca9ec49b1f238cdc 8b34cd0ad8506f46af612a5244c59a24650c82c78f0b55c5f725882a03f6ab2a 318cb837d391332d2972bf91059641750fb6de030f3a9d2586864ca66cde6889 1fdd091c1e1c6ac10c368c6a1c88cfdce80c2f7784f5e0f3fb5eb7a5652ddbac de6f9411dcdca24085106a903aabd550d6a60cc254a4ac9799102af0058e69cc 967336ae463af4f5ad60d7d7a0dd1ea90d0a61c54df53de73dae1a47280ca097 6d72df4fbf3e279099e6c15080c717436be36d6de66620449dbf739cc20105fc e5d156569c93998022adb75efc94e759cf218b1dfc2df6623a05ab83e6b33c8a d68f50199dc187e32e25e69a3078401cee02ac970c9d6ef83043dfe89eb9bcc4 000a67cf53b56ebf0eaec01dd44f33009962ed178a9df6152d72d03dc2c20008 f6e8b0811b3d6165c094dc9273027eee868929c91b23481325e52317194aca3b afc9331178c5bc7908e554ee14a5f26d1d156883c610ce4d7b73a03d9f5ee6af 8a6a3595cd5b8d21f69311e1f6e427f74181ca00359e849f2b5641d5fc44b02a 605b2e5ac0d84db3f9f8ce484f75d4c9e48b4003f076dbf8c5e516d2891e04b7 0a7d05927b25e22008049536b853eb1fe560cd7f5975e6a60c59ba852e62b800 1801deb36ee091af53f421dfeeeeea56ce5ffca7fd3864e2ac597b18d733d3a8 1fe6b47a9a1163c67db6e5b354e4e7a6f32c5d52585070bbd44344ab9a3502de 80bbdd0f1acfa5c8f291a531b6a0da0501973ee5c124c957c18a7d912d6278a7 25fd86742c7e8908c1480176d0c9dd6987abacae3502b1397e3214fbc5f1c52c 69923ae1c0be2e24d5dacd3363f5dddb08f14f1c52f21ede3aa44e96a62f67f5 dcdd0e4dd4e4e097796e39a10be6e53b11eb52738702dd87195239da0b2772b0 5ec1ff879098de5fab79cd30762efa1f684cad1d48da21897c7c0640e66eebcf 92cb578985a3ef8a67f4e4f95157213edb573522ebf1fb28c8f8bd668ab9b065 896409d8c80ec49766e2f919cd9be26a7281aa249ba1ce523539b544048d2737 c1a5c48345b8662e1591a5c55ca5165bcd15d4a6bf883e53e15b362c1b13d59c 8da24eba5e3111a84b6aa885c503c0476194093e3f614724971332522e70bdbb 5ced7f3095be6e771b22c1e276ec900417d23ef3782b597e7689afaa8eb6799e 0ca951fa47420d8d51695db70e405bf5efc0781bd390df0f3819e76a789f7c00 f31ca62fb375065d6f1fa8bc9a0b181641e190450a7f11477c460543ea27df8b 880236a38e3ddc221d20cbf82414ad8de9dba8467c81a34d85ff496f9ae08dd2 3ad0c8579891f3b2a5dfbc5853e42ecf6230c9984e1f204f315b08690151a40b 17d8ef3b070413ea6a8a0326ae595f5fe4c6e3a22b9d4febf4aa7a5a380dc7a3 6ff60d641f350edeb361583074dd444f8054edd567e0d6e85d0bfc54db7d5a3e 426452446f90e3a3247a6d107e5a50d7b3c7c3e7aa95eeb00457f19f6f036c3d fb64e29dc053bcdd0c193cd3d64db4e5deff4c0ae25748eee15dbb3e497fa7f2 30cba7632ca40dea5af07170c067b57c0246a94eb5890e475bd48d5f3fa78c92 524e56a86c81a05a996d78f2b396773431039ce486f061aef34ff2036b374cfe 7360472f81032826ad1b683614dc8bf1b1c839336a5b2b4bc34dbcb931f9fcbd 0898b5cec6c5b22ff48dcca5297b8c31ad901074a668efecd2f9fb2a48e99426 38694ac1f53d5f66acc5ed80ee74789607fb5ea4e876d0b666ff64dac1473e32 9526650ce6d86e64ce7bf4ecfefe2da01e7c99eeb701ed83d03f5070172d2beb 1c434c5896006ed7a54da5995f92857c6a6da196b54cae99bf858e1fa03cc06f 7e2433b5f657bea37d794e6fba7f01ca901054f3dd7d000ef3ada9ac7e81c787 36cbce2f36b1c19f1316a066bc0dcd0459056c0e1301d0831e38b372ab663a95 dd07dd9b6eca6eaa73f78e6ca1e49593370c6985eb11f52b2579992b727d251b 3115f5da655ebf5a8aa608d762adceeeb5411f96aa69f0401ddc589f90ef3532 7d6e34000d64205244d74a210b8a92bb9d531aa8c694a6a0ea624ebeb3b73612 6776b0832f870eb22a19c79f1e75b90dbaf8f4ab11a03444456fa999163f322b be6b3466eaed84ab9305c8f74846f9f0757163f8a2dfee372356ac735a957fe6 4eb512388d999f3c0268ed977bc850db0b105db6e9befc145f875dac99526dd2 b60737a1bb5522c3922d0aebc891c50a2b30835caa96efb4d9e9460f184605a9 99bf4a132b1c594f4102cde6f11550df0ac8e25c8266b418b0b38fd70f1b37f7 6989771afae85d07f2121e4b98462c24d108744a5277ae24e2db55ace77d2cc3 c4e12122499d4465edc53c62a6828328b914a534b4b02e4551cd873bc567b3d2 b9730f39604333a1e0f031e307977095cb2203d0638d224ac389dc14685ecab7 883ef38674e2a9d297dfad06e2c14e2bf9f73faaf88c23ba68948c53f284b942 1c2627e516f813f3eb2b8a0ea098c4b9bb013c7b7c62a678bbf9c2d0af808a5b 15a9f7805469bcc5ab68b0f2d27c981f48f22ff672e9baaca27cdb1f20749252 4990e6d5d47e0098c9a7f22e01906bd1b19441c61602012790ad53f22c8a3533 79bbd448911a8748c23518c2e8b0b2e456310deb8b2f2ca8fedb39bf641e7564 f975914f93fb8b87bfdc4d222446c3e0d3f3eeb32d3fa90da1958719a1e5a508 138e9ab8612e9e700b83ce0795a0305a10f07130d5ce8dc8336a79ff2d1ab7d6 4ac1626dc532a58f104dc699943f13c32292fd2d1e4a41fedbf0d3c490b496d7 69d1ab2082956cd35158ec683fae6c310535dad230cdb895d7b0fb8885d13186 da27c5ad119811b61d54159cffce674c443302a1b9309cd5ba0d1555354d4885 1424e15cf3d014516c812e3a8f443dedef2cc42f1539da7c76d459a10dbfce38 9a81acbaf5906f3243131a12bd3acbfeb932f5c2c6b9594c111c5747a089208c 0ae1a9535b8874fade4afaa16f8a6cb832b53e643e097ef106dd8717e5df11c2 9cd83cb9107ec6e6e9393e9b7c61bb3661ce7f99cff2c0eb2ac5fa9974cf8158 fee10e0bd9a658507df01ab39e453cc29dba4140633c74c1c65b43bf97f37693 7b3204f988a4bd915062ad2a4ea217afd2527773453c6a903ff2b19d6bf5174f 0838ebd03f90eec3a12d8823363e0bc3a605b2d96151aa2acdbc97107cb85cf6 5d4d6e2d26a80ede34a67d7c2538b7d6a3f6a7a4f3a7ea77923ee2d0e7f59c0a 48602ece66234bed99815664ce266340701e3236e8c19ac18ff5f1817438ff3a f7002c947310456dd39941f48b1b59612ec17552c2eecddbcfd0a1d3f3f2bd70 524d32f41e05fb5147f6e59d7de62ac64bec4b1b63cba4055f41c554404d8998 24c08306fb669a9a9dd9999f488cff672324d42735143066412611aae08a6f8d 119287d1fb56d03c8b473e859588077dc1f63a2c9d1f7fa508a0cc07d677ce0d 18866eed8a9d2dcf94ca6087a250769b45958893a50cc478cc02adb094da5759 457c2c8bad7c93c56f2e2bbabc4d476bffb4700b9a1dc9e04d0bc1e51471f883 007a9c80c633035abd43b476348358698af7c26765d875a186db668366fc1e55 cdb493ebb8f97a13a704a291733261caa9573651961a15f6d880488eba84bf91 6290f5712cf453a153013ad5e26eae101f3f2e9d5057e1ebe192e0dc1dc40572 af657d659d64b127d0a01fe8bc4794cce700fc5181ee851fa83bd28622433da5 c2b7ad1ddbe4f78087f4e3674bf8426737b1680d293aa9171c1ca9455cb34716 9bdc8c9ee6f10d4631d2c62071e24fb560e835e2206cabd3bb31f264984b0858 0140de20af70220206d6bfae0183bc17dc38fbd7e6e1050eada46da9e87fd482 9685fece7592fffe2d442dd1dd29cac19b2ad56e21456d820d666fe3e291ba0f 37f27b809e9d0b03277ebeb5c1db4d70d12ee0b5a053ee711fab06131a420fd7 49d6cbf1707165357e42eb049dcec1eefe81d53f4feae5a08238da3f29f30cfe c89cbe40fbdc618653f2bc20fdac4f17a267ff38eeeddaf4473583f613352d56 570c7f92b1d8c98a3764a995c6d077e032d3aa43298e4e0c71bfeb968cbe73d0 db63d1bff0252274843eb6932ddbaf364832f6662f03e3215edff938596900bc d3ae6392e72427efa34648354d74ac78c786418df01350886b595acc80a83e0e 8899dd616bad8ce7e39b0cfbf2f51ddb412924e99c3ee6196e2827dddbf84360 5e26c7af61620261cfb40619d6a6de8f62d687a1a863875444e60468c1e26f51 2ba48a0ae46bea5f321774099a6cff80b4643ec9e888ef4c90b53a46bd6cacba 12d8c0f7f8175dc5904fe0b8d33f7c0de194092d0f556d58cd04c95a91fddcab 6b9ab1ba04bc76e127712bd189b423ad42d09436953615a0112279b2b51c518a f91d1a93f96678cda3be3107261efa45496ac55d438dfc0eed3428e98830f3c8 d32a952727a33da34339ae892bfe98a03f28c8ddd6aa93c207f198a7a89aa51c a8a39da48cb73725656e6a405d4c2e4bbbb704e42f019cdcbea48320e940a664 aee67c5296d6f5d79ea7ba7409a3b09279f9d095659adf8b0387d8a93074ea17 a209a06f1b2abd4100b1db36cea1183036f5a2f381d88334a07e8b1b7aececdc 5ca8b47f36a1ecff41249ad8a5c73ec35dd9c7565fa965bea54928c1abe8a142 7d86904d52f029f5654df6b7d8ec9c371b89a9e001628a845cebc9543c5d12dd 14f37db7a8652c5e9de24b3af4f6e2b63c4b89e1290f7ba24df68676ee6c7c4d 9fdbf11dca6151ca96952dd9f8582a8bbf0b2e0b0a53ce8c25e205652f94760d 1d0baa9f8ea1105859913408cea8d998b6a4a2fa5160f43836b00e5548b166ff b046899d08166a6a13bdffcc296345d813169628e52821da47838f6cecbe375d ff962e2372874f32f2ab74ea4bbf46f8f69ca4979e1e711524c3c798d1b1ce16 aca1e51773860a5c78bcb7fc033b2b33fab9cc4f6d4eaf115f061a6e8e5eb59f 457c5071d0836e82b1a30f049ee38be0c4bc888c77a05158bc8f9e1c6c5d20ce b5f95b3e291c5d8ca5d1cf2d5b450a897882ac1e7a74044442e73f88e1bf86cf 686442b7c2e901432fc4b1810fca11f6c7e608e4b4d0c8c3d55b15fa41d42cc3 c5b5e5cfd57dfd535437a599e29529b1a3b72c6f4ef01310f9e9e5325eb4a11e c576d640968109763a07bb6663f96280380e7f8ff5e4a70f89bd959ff898978b 694f538bd1c033981cdf04593310d3bb25065c528b0f0c4a9b18a0b9f6beeb4e 80c8c2f3dc52099fb7e6e5a7686294060dbc7b915b96aa103f5e881976073914 49c849f5b9531437fcafce3fcebf6b57f4e6f73815bd464b01faba78485a963e 394528b5b871d1d4d1e3b57eeb3a394e51af78a21f28dd4c99d9e9bb9e16e8f2 de5648bd78c223099df4d89515c7482123cfd12b816314856c8e376e8ef8eb3d 7ac71e2c5ed5ec57038cdf32e18882955869375acbd0ff06b514146890a5a37c 38e86bd788fb00343c0078f7ddf6af86c5b7473cf433d4d79b2bfd6c217775bb a004d173549a00b1e4548d2511fa5c1fa6647efe64e228e76534eae78e17d9f2 3c2b3f20122ecfc9d0c58f58a3b5e8071f0fae6ab18612501467c73197164467 64891eea4a97c5975550f74c8a38417fc2f7f62b0478cf8bc656a32606229dce fbfd62342e7657e7628b5135447ab9ea76e36fbc16bc4f2a85e77d15fb077b63 06a804ad7206ecb3cac99d8d0b883187122688f577561841f9a7ae50c74d7d5c 0e6ead60679f833b0226444dd6457810b5d39e6370831295c1266eaca067626e deb640a50c27d6c7750ce8038ffcd47d042998c3877fe1086363fa7ab281f524 e72cdb2c4c6da8a896cf427e3ad9a0b221fda14990709c7747720a71deff7edc 45c35888ab0b74df3951c8772adf2b0aeefd00820dea50f1c9ccc26806d5d7f0 ecf5ed236ce610003e7e2f470fcc3a230fdaee3a3ff1d79c7972017094163d83 9644a87b6434e32d1dc0fad4cdf61ae72440831e305b4d96268cde8c0623bc43 b4f93bff4e5fac36230ee08d1a281e3dfa52e9d7648aec003a73afbd37478331 ee0126a432f3c2eb5b15213eed4f535560ba679c8d2bdb6f1d213ba688ee5b50 497e7620d0ef3ed914322291b8d3b94d9a33aaccc81d7564550110bae09713d9 e9fc0decb13968292c56aa893c438ba7bc15c93036144a9e1ebabaa49e794c8a f701b3b616079663fbfa33d78da000e4f1c66b7819c3b61ca31f5e46f99700ba 45804282a1648001fb44a80ca2a358ef1bf3f3b9a7d52b80411fd6bc3bc1d0cf 0c30aa17f8ad65a8d0b34b969417fcbf2336486b07740b51ccd275affdb8fa0f 3c903789c1de2b5f9d91f045ba919a3fd6fe8139a653635c2e713218b8e60a9d e60ad81723af4be4a1f17a19be5ed92f2710d1586e1ef64e3c7764194acdf120 18024eb8a1c3651514cf13655537bdde4595df6415530aaa2aad6302a308b7cd c4da33e90194213fb469a46efedf800e024b26db0b3e2e72db9f2c596b307a7d d54e99c65a4b0076d9fc981b8278102434ee0537c1bc2d38990a62aea304aa97 234ad5004aef195010314cc378d5719b02c4b4ac6065c19d670febffffeeb184 84d4c4f3ffd1582f28e8bd23ba8c42293ef757955021b665aa9426b0bad4cc6c e3285d627458668a215e595e3f103e73797e76bc7f83be1e9833e31a6ea2112f 42d75e3ed1db104fe4340eae97e4e9149df1a5e37d62afbe6ebcc3cb13db3451 59dde711e9f1022a5b13dd5e20c9ef109f55c2259fbc97eefd747f302e3f2aaf dc39376d27a9baed7da4c851d61c2b1b952715406a6deabf98d32743e3e7ae75 d69f46473c0d070e13c7dc26419a1f411b2111d496092d7d27619542ce30c4e1 1b31901ada9efb21cdc492089cd61e697c9a785d84dd234913fabb2dc5abcd72 a24ff0f3bfd7d2b51a014adf5d60dfe706b5ac8807965dd2505e555e798156f1 7d2d185fffd848770b2829a0365f5091dfdae92f283a1671474c66648751ddec 04bfaf5e34e46b6299b8f7561c2352b275d77d820531bc99865d943fa8318263 7d1ae620bc7cd4fbf11521d090ca3d7ef515e9af1b0043d137869d99e251ca96 ec500ec23d14d00edef32256da66da6598f0effc5121a526e7874934dc9ded3b 3016a6bcc1033e4dc8e8be06c80cdbaa0697fd2ea2c7716af80390cd914534a3 0376b855e4e3ed549806d4bed85a6c211702c166cbcef308ba4ce7de0b784498 f90c2cd6e5957245f99ceefe09b4530066df44bdb7b095e390aa9485daa65697 782224a05e69a1122c82673822cb5d4c8dd0827e5196e8dd4d531928c1de61ba 5d8905a6941c3ce778e74dcb8b399adb7f6e80d143bb4a73ec52db117a2b0332 ef1d4c0c97ae26d5f7ac089aaa19803c98e7e8c8393ea3facaeb24f98d543e17 d0d8cdcac71ce7917940adbd9083f307e17d8d24b8324ec81e6290c30ca9f465 7b2671296b51a232066bf70f8b29e0010f9bfb50c47aca95b6f6b1efe7a39722 092eb82acd6252e50ba450ebf29288530784061c3d1bbb87e1ed0621891732d1 021ded3ef6bc79548754a42694a313cd90fa89fccae8fac722a4b22b033c0186 e9c950b1982663faa6d388d8a5f7e6aaa3c601aca3027d9aef7cbcd174850c6d ef276bea2f5c1f3c7fb26cd8781df4879bc813826c5fad2b5912ad997bfc8987 ea38fa165ed37d2ea5fe86bdb5b493250a3763979a0e262959745c64725708ff 710b5ae860b4b992e2ffd2f465a2f7d2e453882d5f559482f6530ca118d561c4 f71d01fb9c7c532a11af2502886547660493505239570a939dffa869a899bfad 43101bd4c110534b45d419d38e45d2cb3e9d01e0af8a12dc4620bbcbdba29be3 a291eaf7725a01a52d2efb5322d3926133d6543799662fd0f239258b5ecf64df efc96a446a795c1744e5c7343a9cc19288527464cd8b747ae0d31002abd6e539 fe7efc11e68364234cac2aac1bc8f87f1e3e046aa6de1cbe17569a2902bc8d69 539f07af0803e76343817d6953816bbb2b2b21b7cbdec5057d411f1349879e39 cde59c5578aab5901be1469569b4beb8d4be9af44a6e2573d5426dc9e1451884 7cc49abdc70ed44cbbd458e626275490d75d82e6b27afc8651d142efd21e83a4 eae297c738737a9f38691e8204ce9cda006479d67954a211e29a66b39af61b1f 02ba32e7f9572a6ae856c76b3409bdc327c1fede0ff9e87542da474b4a810717 5018ecc796a2e77aec3d2c3611b6d3604f54a4ea16a273392ec60d104bd55b7e e1c778793279c9ecc8581bb7703ed6030bd52b3c860b5bd7c7218aa384ba5888 4c6095d82ba1cdf3795e20d906d2916dfafd0d3fd30444596459013a72e35d2b 67002d8cc78b07afdd4f1b37ab0550a603f986d35a772670f27dcab52669f295 70e702c0d4eb95abdf3c41814622d0d4c31da62dc4e6f9cc41fbaca520bf948a 5a35c12aaf9241adb51c851d4b46ea6bd3cea8039f31bbb4a131b8c94eac7ca4 1cac05863ae0b857bad14a4869c2cee624f3c49d6694bf6f1a8ec1d97cd4b41e 6fcda3b3afd064cc1946daa86257c938bd342d194dd1e48db1943248751fd347 27882e67720e2625dadeaff7c0ad7811832457ba44bc593ab72d3e69348fa6d5 1fec0eead2cab806cbed067eb5d0e3f33ae60a282e3ebbb17c685db92d723c65 91d979431c5d515e90c4e150e1c27b9c5a2155e6da57d1c6439519f900b63813 ce526cb6341fd975feb3ef4302f444ca4229026e1f3bdbc88c66e843504b2813 d5332f8b757eace033aedf0dda92b6ae8e212af47462c7efa82b4d86e51275f4 e9037316b513e94a5a7539f1c0e53a054692257436ff8b564aaa53a43aac6199 7f73d27f241985f5c867210e20b3a3e561af8067f78320edfc39faef61f956b0 6beeb31c2693d71406341dc9500d2132eac64519318eca483c96432e26eb7fd4 ab00d2c3dfaa66080ac049d3799f10286b18a41e7dde466b11832a37d42b7f6c 9581983684a5a22b28c818b6c0ac32385e8bdaa930045119e250f99981681166 e9cfd7c1721b3e6884d70389375e016251f19c38a3030f6d22e2044dd1483157 fe1f57a62bbf715a0d5ad74f89a085ec08f1712eb694e6b2ef76c6d7a2cf121d 082e096937bd8ce5462c5fe32534a69d09591f7dbd8d299fc44b77fd89e8a985 4bcc76497bbeb1046d6727eb0c1dd1043b23690791e936b8255b822bd76dbafa f4bd616a99246420a2c3258155153b608f40896def0234ec284ee8badd86a985 2bb24a852c5ffa8235bcf8c79ed119c9a19fcd0bebed36f0a7d02fac143ce27d 8eeeb66f80f9bd29a71b3042fc955fc9cb1a78521a6135a48d463452e7e8894f 15c313f727821a2f1f0b72e348a816fb65dad9877b9f11fef4373500a31650c1 8c4413a0b77fc4db2094993a60aa29f85e79d847854b6dbca8f6f044a8b5d48b 68dc57f401f9b6da2b539f65038765d40fccd8480bf0dd307162d6cb55a0b8dd acd8e89b14a8f59304d69e1e4050b75240143011e99431b912e654a0ae657847 0dceec2f032ae6845a7c98f9754ca2a62b0766c14e4dad330af53014cd3fd038 0f63c35b66b94fe2aa3bca9d7727c77b8a890768e6f5b2971680a3e27028faba af85513d4ebcdec02899e3def1d5db3e5837baeb1c815d7f9cb693a6ab2c52d5 0ccbd9f2f6148799e69f565a2d8a9f2204ce960f0a745fbd4e429c9d17c59fd1 951844b5119b538622586ffc8cf24a07d8552e1573c7d40e98f8ceabccafa506 1aa8ba8af971980dd30ab87d773c9db591693ac035b25e051d518a563e5883d2 78f6dbcf4c1e006da5935bdba57d0a7d5a7d636285bf813427841b73e4037dbd bd4c5298d36a06c9a5099859065bb6020b09fdb77bcb2086adb42ffac1c3ac98 8909b3a32a462023eaa240a85311224e7cbecff9d313effcabc9fdcecd1583f3 cede24b89f955efd642ebd2ad4a2a7a842efb55b9d864f222d0c23d0ec338046 e52e364c8361f358a543fe655570d022c5be557647188f9a68e5f1cea915640a d315926ea8fdd565443be708c22fd737969bd47b5d9792d726e2d8c2d0efd4f5 9fe85a83443296cf2ab1c3ff08daadde7b88cd109b77ad93f1fd9e3dd993a7de c4830c6506db3116e927eedb49346a625b1de46556c17f6ce059a6551ca807f9 7d9b83c73587c8c0d939b224fc60555b29d720a4672688f6aa05def469339695 67c98297df8a5eeb08b2dcefafa1b75ff490d89dddb904e8626d9712eb01699b c87e29e4aa4b5cfbf75bc4fdb82f6f68a43939ba694522ca98c06e9ba78c3a76 f82b3aad62318ce6ac528a9dbadf3e7b32cc9b76453ba08f40c4d08787176e60 b7237c20be1748e29ede557fa294c91bbfd41884d62c9dda9d3600e29523bba3 40902e133e0347026247d37f1423643638ec5d4955bee51b90f74395629bdbf8 39cf69947057935f20581403a3eafa3dff0e5516135f38451777879268573b11 d0b7af0ff8bc2233cf7de23ed7ffac99d04c77c592eb7d0be542f5f7c5523021 6a5dd636346f512ce9d0bb7fd8e90f688ccf4644d6ee5f3dcd260ad9757aff78 e4e70a75ab9bc484b122a1b6cb0b244b271aafe367daa18e9bc63ac9893204f4 292549d2856c0e6c81c0808a24d843038af49a501c0720e8533e130bda9d31ca e6df0fb5b0296ae9041f7e305fb449c6976a4b57b5eee5b317e9fd637ae1434e 96286e5265a0ecc7c4ce44255fd3054d52a7a5e14bcba0b46ae88646520c798e 75c2c836ba44c610da75f4727d5bfe702a4ae6b3e56bc3b1f961ac8aedaaaf59 298ba1abe3b08e07e54d1560c435a02eec7803e0e5c295661291638bba48e9ad 2b4ea43e0a32cfc059ab86e45b9bf0ec2bda2a29e0aade3ab853e69eacd6e36e 3f5385f8163ff6e093c3f9fc9d0f4d7f07347e3dc0ecab3f8d896b6aa9ec16e9 712f2013d965c8cf58f9e58ba960af228bfd868a58d7f48b852f390cec924f7f daf27cc778cdad0d45451571d462e7b67416af910641c23e569f09ea6c9f5124 e6f94c533f0ef97fd8baa83d67005573beb1d3a590ea5d6de6a27c67ee29ee3d fe523c9c25730e613c143f65824ad0417e08c7044d62d6fdb5121359e2ce3d4a d43ef742418a8bc1c2621ecedd92f3163cf465d2a497efa39031ed593ba6fbfb 990cc4305b317516eb309b75225d8c07f29aab63aecf69dba066093c02b75d1f ac8324253ddedbeaa90bde3847af539487080773f6859ac0a29dad4292732784 554f781fd7b46afab4d590c3cc3c1293676d6bdbd258c5790c2f77eae6360a08 284545bbe5737849a65dc618bace5fa758c83a79aee9cc959520ca8cd61d1ad4 7ada73139125649c52877660942fe51640949c47415beab31dfb1289359e6539 0322f7d10fb48ff72faac2dee352f9c309b6b26e5227956a51219012d129b58c 9446a3508369a648041f11580f2233756785e691722760800f915e2de4a2c213 e6c31c79c75ea18ccb9cc64ad6fc3365e37639de9e6b53e7923b04a8999f9bc2 9687e2cd109cfe8ed19db953a2ddfcfca9b37cf2c0da78844ec6920a7afc7f28 4c538391d427d035e810a72fd6e4855866f16b0593a14091ee9a9d4fef2c468c 1a5289485379221302f80b4e541675fce28b181802dbfe6f76c37e2b1764eafa 11a8b51cf493b76ec87525618ea624f4b7d882998de31eef9a1f5b3e7f81aa70 7352936e05c375c36005a3b9d65388c7f1757fb7df8411de232c94cf44d7d244 73e4e6bc3de5d0cad86bd53e4a4c3b95bcc4cb4fbfbe0cfa84571c0657c49f5d e910b205fcec20c651691d823671ee2b5f7f4e4eeb1ac5093056c60a37ea2952 bd8d4955126289ebfb41fbb82ac44edf7e91ff2c6a47643acee8624a82ede110 0002e0be55f9363787883ec6d3dc4bc81f6102c18c2cc1e803dd69f9214fc6c2 52715b4b779a1ffe77aaf46206feed7e4e35f4136831df49a8a9f751cd67a8f9 2907158a129d59240b8e3e299791236a86be2a0a5fa32c41af41c5f850808155 94361f3d57597bb790218a54bf5f9ae42ebcd88eb79448eb3fbe8015e81adfd1 68e8fb1054acfd565322a0ff02da434591965b479a9d352d9c550d1749404011 96b8edc9fa03cf1cefcacda90656530ae86b7f46b69130ee7a92a6b52bf71f87 c629b0c4ee4f77eeba9f46d79bddf2feff27de687703ccae5052f87efa14bfec 37fcd11ff58a1bace17795536ed0e8579e6cf26c55299f6b869b3b744c5a4b36 4b5c727f099c76628223489dcc57da394a59e74d684d8d7000f9946fa3aca43f 939b4a1f7aca3ea362527c64bdc3f8602786925d449b2b7284e9fb3c7ababe06 eab05990ba0b079c084bb6c82d3bf8489f312a71f4e254ff44a2096d7797d502 87be3a74ba1e807780b430d76693981b60b6457d72747cd823f38ecb4d37a33a 82fef1a1167101fec09f378e346c78a0a85ee7e9f88677943cc71dae02559ac0 8c87c631e41db4bb6ce112e3ac41abcf9ac40b99f5e11d3c53cd1f0292b75e48 f9be458a4b54c3fae6ccdba7cca231cc08a10a95bd9ae06f7bf5d62625f7b8b9 31495bc51db6e5961d765a157d8f14190605a0229dd5dc00192e383372d647f8 87f97568f2d3ab020a4b4fdc9a00f48566d6c1312f6930c9c040b1144363b5a6 1a087e0fd84b09097e4a944e8e1f1f5901791b513e0eb2082bcca3002070917a 9160496591cb3fe36cb64469b193ed9cb7135721eff7289ae8685d62a4066954 cc868413d2b288d14820431c00c343b4e9bd1e619fe321e1731a90c6612be130 22f4f4bc5ccd248feda0cb0d47622ac1957390f56cd35580003c7b8f78f4fa8d 013a8009267cd8533fae86b051c680a7322f3787e7d4da83bfc94dc64d8234d4 1558d8c79c24706a138ccc3f53913428b9b4d2ad8f749c492578f21f157b6e19 20775c010c80983e0f9f8f06a0739fb0f8dece6ce631ec09230aae1db811a355 10736e838f3678324f94637e2a324be2eb60c9f24968b9ce4cc1e16b039f6449 a295a30901652fb7a80093caf7f10f03e76740e21795a1d90d0621db3f573d39 24c5f8f0df68db375e82905a71cd2d2e99bb6c4ce8ff547523e8bc966667e2d4 2dfb1662b35ca0e25589cba9662aeba0be554ccc027d8f8d3447cf6e3cea6370 2371d28b0239cab0b78771991ae23984abbd622c8564c7c44c588e90aa9e4f0f e983bd947ec600753a37c4d9a40ad789c5a3eec66b0a9c01a41be1aebd38c08a cc8c3d670f97e06ad3e620043e7a338a3e921537b1ae25a6e7ad0a45ec874782 64d3baab23dc6bf3bc40e9d4370ae208a72a5c02a6dddcf93d6c5d328920dae7 94c6c0c2ab912f5aabf7fccbf565d1be1d6316fe19cd8a6a292382702db55c6b c149c91b4fe92d0bd37b774688178e4cab23776e44f126d7832348a5899f775b f49626dbb37508df3d6b4dfc3717dbf9a69ecc2703dcaa779e62653518707910 4f86dee2495405f97274bec0ce9a9366e1309e2473f33c2704d5eec468a82d55 b2fee058eb3d2c983782383f641cdd0c8dd6c0eb44f766af1df1cf212c431b4b ca9c0b55fb34ee2de1034d4cf02c5644b0740045d938cd9d7e680c2e7fce694c 6cf4e25840f70828fbbee65cd386136cbbb41756d558c10700ddbf0890528eb4 979463e9f3b1bd9166170419e64cc9a5aea7e17aa5de9ebd0a08e285c0a3ec0e 476d0578a8d6210739144101518edbddf9a2de8a3c49998846e342bcd213ed9a 52446dc5ce7922aac80280653e48b54bc1031cfb41ebc683046f77f1227c9177 d8a5a2058437b859cb307f46a7f4d4ea78d171affd63793c9b07c545d8f3e8dc 7815e7f2a2ab2441055e56161e3173dd444444563522d1add3e838e9dd28a587 484b4128a3ca09fa8b4cfd2147a2b372b967a53767739a3bce08bcad802a0ad9 c5dd64e641bf02443d8bf40bdd869f461d2628748657ae9f0c5d7cdb12c647b1 1315a877c217bd934ac71732a162e1c6dca5f24187b2424658a7b93c97aeea7b 9a7519f3cc75354a16dd98b3a986d9b35b3d34bad04936729f189fc09618902b e0eea75f635b7f820ab491fc1bdcce923927a2235e27863f1974b808a66d7edf 8c34d6b737ed463675466d9e65d594cec1fbd0d276df7be3e8523e6cd5e7032c 99f78d09abf88915f60f1b0de31f21d82411ee2dad972b33d9419b1ab3f72392 6d61c84916e1dd33e285f49a4d2f1dab92c04a957b4a9075e7b5d14f5b0a584a 36bf846831c2942a72c0a9185603b014e96185499df98f1371c85814854f7424 bfda071b254df65242009a9a506564f87cd8d2f9bdf0f31c5c985a8f3d0a49be 8a9a9b897723b224df5e8e88ce936f69096836f5425bdf5036b8345ca925182d b6a56347650d5e65c8fd80e0d4783fb0b0c9af0f2c4f0eae52f5aa2432633dcf d2aa9cf520a6a3e879886885ed151c44d6ab8de5f90509a97d6955970ede100f 1ce32a7aa51aebb47e0419020027a29e5f35586d3aab94b885b9bb3ce4ceea70 6194a13aa978a987d139a41002cd57c87a57dd477ce9da0ec49728ffbb335f31 1fedba39fa29483efd5cde14058603d8e0043dff247bd0104345bfb44b22cdf5 f5cfa8c3c541585e92a147f18fd6b2d4f03e6b1978b89cdd41d903d694b8770a 365d98a69298ea1ea501bd56351d979feced59a0024b3be556ad1e81b1390c12 df9efeab577cd1e155aa54c9628866ec63d1c6c5bf837506122956a1df0fdbf2 fffb5e8376713b1273b0ceec888c2cbced3b3506bc224ea3b05064544dea6125 53352b7e891cae0bb7290aa0dc589334e15aa1b736c93201fa05aeada72be498 fe464410a1baa9d620df2995f77552abe07025c4cc1e34b66c070297dec064ae 03a0199ccbb75ed74dc074b86063326c4c64383749d4a02a6894e02dda0cc301 844710df68e311fc9150e0567be4d73b30a2cb3fc945d2105d0346c244a303f6 2343f91f1ece25e95bb29a781fe21e3ae3636f193ec8a103bb2b0ee375143656 465933d75015720c59e54751c2c40ff8ccaaaea8dc05925198a30ae7d0226d54 052185ceecac7ca5cfce3e607f2b1e8308e69edf0b44b03a3f6b462301ac6f93 7fa0b5404437880a8f5ccabe23d3099c54eb665fee3e52d505bef591880fbb9f 3c3d80adf914cf997a13eaaaf07395ad87456c4890804cd727f6de0b2fad77c9 c86e942f6b5913fd56344e6a65c9510bc1db2b45dd30cffbd83530f511d4473c 1df2329d588dbeb6f681dd1bc3e7a14ca218547a80f159b1d89d5f8063361911 a8de278fbb85969f27966a3ef8633a97aea2e086de15f556c8175538158c4e98 f24307ac0d00e9bbe74cba7b02606ceb24b6c4f7412c734d7fc60edf4fd70c37 03833049841c965c27dfa3a82eb628fc23f02116e3691c82711decc69c5da663 6e688200b59ff4218006490b46b11134b811f1d38a56d8a2596bc5ccdb4b5df1 49314527bc0089541874ec6ad2a04af04eb39b6e189721d66917ca4d7886940e d2890b573a11e8ae2bb390de23ff1defc244ec6f8e32e25ecf9b7d9ed86059a7 c32010fb076a2087467919fc9258289cec569b79660daa8e8c228976611743fb 43163b8cbeb449a5cbd4cd1a8bb003c3e22588452cd04139334ebfed56562fbd 70e441367ad1ace14cc0f4b372e0e71ac6e0dc1942b64a5210f60a7d7a3d888b 3abd1a48b19a3654964b8a0847de2fe8824723625818b90922a9f26092c1b732 1c66e7fc20ccf530c19483b291fc52d1c004053d1b70b67304b4d11ae8bed407 13b9410fded393bb4b3d4c1913cf7ec0504d7c6791719fc72f233f763ef3d4b0 ce72bb0ad2b2028b0a5e71c7353b00961f7eb62fb149ac4f22d07c7a46aac3d6 e2ac600e3977d4817b436e260896a4d68c6d31e0ab4b3fa0b9d6d0ac6b70b8a3 81f0c382415441f9054e8fb8e53f2175c0f9c04ef7f5d178412a0b0045bdeedc e459eff9555a1da7e1372c1943df4a4ed97e4bf5fa9a9122a20e4dfc9f1abb61 3cef078b4db8f7a7c261ccea2b38486751439a9162da1429144fd86261192404 f45f175347b8e02fdbbdd451b25b62e9d16beae4fd11fd6dafe9ceced0aff22c d252aeb42306dd695cfd5a6bfa46c4d7fd52a76b5af81c59c6c9d285a6b84457 1e990baa876e88ba0a468db923d6353853f916c41056998425e660daac13c90f 19095254d2e3c73dd46db8af2ffede3cf5ac03af433b2104c4ef481df2ae4d45 f74ebf804703fb8f7338c02007d502f439400da62b15f1c5b3da6d6228c56551 81639bebc630b40cbbada0c3bca606bd89cc682049aedc29325517e8e350e5e4 218560823c42206f96b3d54761130fdbd5f3b9fecf198af1c21c9d861927b282 4ece8b7d60dedb3b5462b874246c9895e9c13c755aada02ec06fb8b0815580b1 8530260de35f8cc177c051e68d7405c66a2ec35e11167b7f0054fb51e8b688a2 7724e0973b1cf31d4af3b4e7facdf61a4e1dce497bbbd70a07ce66bf4b1083b6 52c0ad45edbddbbbf65369b241161f7db378ea43578b7fdeba3b04e5606f9d83 33ded10f1e773f767c420ee62ca3f2d548d74f4f74b334de94f448edf95a0dca c1570c0f367fb0febba08396a8ae758eb4aa9efba35788db21f0680c79c9e098 70d8081c8b296482c72b4484444a4e128703d412489c040e1674edf490242cd2 8f2183367729d51d1d6b52be4acd6161bf4370167e3fa420ebb1f38b7ab4be9f bdb46c96cb9dda0ec204d9932ab75c71154b319bb71140b6ae7c17c88ea6d552 b4386c789ac0c422735e91de8690fcebe770247a0fe52146124082905c3e245e 4c820772d45b650d5a851087b44e17a1b4d373c7dac0208d668f7194302fd182 41c36548d1592519c1ad14336132965fea8b61948cb208670e9bb1372aa16d13 6e176a436071c15afa188de94adbfcb1a136693bee9534cc215ac4514dd601f9 0c73ee42455340e552d84d960f2e334ea30ca3b0aa68eb4624ed19531c944485 f108c006bbcc8f3acfae156032d1b055f124ebf1cc1c30cf4fce979255c53270 eeb9dd40e6da81552c28925fe4e0bec8b74eec438b1577bb2dedfa4930daf2ab 7ad8960da141e0e3f47f67e0ddfb609e2eb36fcc9863aed79c1b4a55b99efa00 5ceb757f66dd7b8ffa48b4b98d0265926adc8fd5749aa3dad48de1317bfbf3be c34ef921191ba3c59cecf455bd0b217ef8e096ef4bef98a9f03e8b37901dd289 1f762475ab44f86570c211b01bb96bf0bc7ed47004d63f541c38eaf4c3c383c9 f09bc3f4f9dd1dbf82821d73d2bc89af2164b94f95e66ab531128df670d240e7 f283b40bbd36691ccc8c4c1575328780371f08c503d78900262f6cd696148656 c73ecec5e7a65eeb2dafafa97ed8a486390fc612f032e9e54db88a36b71bb3ee 174fff955662c0d5ddfa7fac9efddc1e4b6117634c26d744fd8b0de5af092ddd 33fcf422b28f60f52b4f6b5130abae4c48255e830619bd70c51cdbbc857b4974 87b01453fbf6d6cac1aeb48fdc01b7d2e7771b1308642a9dd279f6c90801852e aa2bea899aa07d7727ecd2be52412c551385a1093f0d843c5cfd20ff31fece74 bc2f13575ed590a4277f12dc249ee62f3997514e1059bce5f946cb1e41d1f3de b23894bccddaabfb06555e80cd29906334fe1c283143545bd6c64a7379e9c03c 299862427dbf0abadd28843d779ef4a9a6d91c3c47ba81e2f70a449fc67eaa4c 647063c144c94d784a34714e16318d36c599232d93acf6ae4b79f29c29676fac 7f7998658e1079c8b55b92d9f84355b0145cca47da67f7962aead4b6e27a9201 9caf13f8cba6cd5bc930f501428b8c3f6df7d249d39f942ee3f902723a0818ee adf57d044d1f1f41c28bfda6f2b81a1ee8f25a50ccefdc771a817b15c860c31c 111a2134cf4a57dc899a6ea14f7e63d1c709be90a42fe25f30171986b3e60b1e 7a9630b9276486af37451084b1593caec2daac1d23d066df410fad091472445e 3f71585f50aa1dbe6dff2dd9c867e5362a81eb94f1795030b176a399abb6ffc9 0f7eb7d7944216f7eaed6f73926dd635d90a131c13f5b99e6d96e43fbe49dece 3dcec9a31535676e246d0f94494c95b2075baece90fac5eec405db0322a1ad18 38a861e199d6e86937ded01e3948d397d0c2571913b8787a983573cdd8c97c8b c0c4a6b666809ae2bb86658b913a9c7ee50ce79bbd4999f4014c52d44b398ebb f0ecea504784695b026c98312c3a8ecfdf3185186dea635b08df116163fa201e 533da9d8f0416500974156cbdc9c8f18c1997f4d6d6003d9e17ce8cd6d3044ba 1f611d4f8f816becdb5de3ae620119496ce2d19ea2d721adc130de49daf27a74 ce4a8657ed30e0ac2a49793f89d1aa67ec7ef8f9429bb3c303c3de1d6b0646a1 eb817f937588f67504107f518bd78183d64728bdc17833293e6054da44e2dccf 7323d8105af724f19063c0497af45819275f0cb56f6d04928b131682acd19e82 7e1acf43ba3a3ad7fb8ab84916245e6f42dbd94d2c29f1d3e9ebd6d2075a296c 5c7092612bbaef5585246208bd098b0971a34951ef7b12b28c5ff667954d9081 59c9b112ebe55c8feee17e6819eeaa5f7f446ae0e22e63249b874d0cdf6f0066 1856d28c13a06be645658c219b22c7f1d14ca739577cb13520a569c63c80ef2f 35d8abef83322b02eb17d4ff0fe7f3af471bc47cf504af1200b681571337a15a 9bdefa7b678365b3650ecaa93e6679af9653165a5253408340cef4b112a57e30 c6b62970222b9ef6950828bbbd831cfc31ff461114e76bd49d6ec5ede9eb8eaf 4bbea4e71b2d01042d2112f4d9fca3d09940f922f3ffd95864befb1b2163a057 7a431d6a1a2280f9248316f556ff69f078196770d95c06bb74a72d50dfb314b2 a739092f2a4edaad889871d93e36e6c4110f10990b2c0e570f23a1aeeedfc889 3026951c7048198e34539126c95bf94f1739bbe040da5020879eb2c39ab4c429 2831123d56e6cf783a7f7471ab5f07583c6d247f1f108b9b3d807ceedecc2eae 98cb8c8261ca40cf15adea9cff4b84e43b5be71c8b97bab45757a596e7166014 6c6a4306603cfb2b4ad125535e975c14c3837615efb5122c096ee06be2a9dd83 c63ff693de2067998121b320140bac205f1a28ea688fe0bbc0cb458ce59ba29e 11a567465ca982c1e66ce8bc228c0bf2344825c215c2d1223ee37dd5303b3ede 3b620c3eebc8176879b966a4546b214b85236136d5f3bb7c9d3ba4a0ed2044a4 f8ba73cc013ed5dd6914f696f1fe94f29a6ffc2df09e8e3700ad5d98ad4a0770 d36fb4ce079b81ce4ef3f97b09e29b69e57e29df49aa9965e99ceef18433a76b 8435190696e7646874b8ce2732c8f6b4b77927876190beb2b49b23adc3a2fc73 b1b1ebdadab9f63c594f7a902f17577fe1dd6a68265de694b4296e8d33eb0f10 de32449d14fb9f5c0f885254380af56c1955734711df7eab83532ce2ac168f8e 15b7d523d91987f683c779643abe06422d570978bec25467c0a088a54ead46a7 c5b3db8a85be2c4af15cd60f32fcd9c7825da3d7dd738805df56b9dff3dda97e 39f5bcad1f675d71ed3ba7b5c355da78d36012397f74992a63d38043bce83cf5 96549aee83be0ef23f71bcc7ea9e8c7e84131378d80d33ce9e3f3264da88d907 d375e301d7f0c434e1b501f24b6b2e674b509c2d65280ef03a6ce0c161260bce 7608a81b98fdadaaa9a42eaff0a3ff0111aa60ed117113397b5d424a68af11ca e55e46a6f521e7cec54d520d07c0c7c0823f2cd0d7f27ffccb1fe6e2e64c7a86 b3e1c31cea7d108e2863a451a1be8b806b6dadab495f397f554f65e8e65cfc78 19e05925b5584818c5587e837fe9beab0981bb611c1d4d4ed9007d57d0ae4d73 1c298e1ae6e0e825aec188cbd01922912b360686879ac54e0e59cdfdb3683f69 ea0b79c4257fcd7db735e1ef9d00ee02a6f9f46e67d9e7c0c32df7d82a6e1d7a 975a81eced6f989404bd43c825cc0af4f358b5702127c97e0a5c1e74b9efcd83 feba515a4cce7812dfe83a75ed6986789db22364a1c466df976fccc9aed447db 38504908246ce9524fe9eeef741ae1a11ffff3ae23a15633e08fa15fc7a718dd db73e9edcd5db8be1acd71d363b9bcdaa4160113c687e47614cb76e7ec21377e 658547922058641e564946f15795cb7eff565405dd6503e1b884a64af9f2b058 3387805f60c9ddd121f2fefdaf98915b32942b435b576be7ec88b2f5eca284c2 00077bd8b83ba08626b44392e8e7c36b764d71a17f51b885d775b88929562a3f c085a77d2743501d2793635d6e2fcc7de05ecff6c9d449653cb9121eeffd021d 1ead37bc5b78f45e74af64bacaf1889d66d693826f93808c06672635b7f236d1 8c2ec8a2a353f5966ec4cb32bd605b6dbdafb5b5350d987b5fdca4810e7c4184 cad87adeabeb2b237e01d030f7744cafb754e75dbdd7aadfbd4f92da73d01c5c 459f09b22cf4b5dd7b3f652d43555e937adeafe81c700e4939cd8f8afa35ba8d 3e39b73636e20899f84c32b415b10613a2197d60e939e9801145602030de6ff2 dc3611c96b6cd1cfbae0d7a4e41f57b04182a75e0c1f6986106e2c6b6bec5284 6754a3c69d6fa27ba34031be876fbbf1801b35030c98e75b2562b588ab3be4d0 3361af3ff05de37ff99736d710d4c575c0e36e105284a459fb091699ad432c26 cf7dd7b1f7c03446e4143be36a26c58a0f9d8dafa16eb03c276ca6afb2c4743b 7ae3c5c95d9da88aa994e0e81056814c813c8d4adb4342a3035a4310d72399aa cf5a29feae148639231f3fb1608f045e5aa5a9e8d543480998ab9b1ec00ca76d 0aab0e300a88f780b4fc6637586a5357976f2c84cc2bc6c931e3d833f488a689 50c3b8be367c6cc4363dd5142af9fab5720a2012fa3fd9afc86398bfffcb7c82 18def3523a3175a4e9714dcf87a248f16206e935f41f482b995733d80dc9ea65 905f9bbc33fa3695b2a51be442e851f7cbbc068595055c06d5fbf59d49dee074 4093088a535bf5132b5b7c0ba6f5e28daa587a51e388d2c88c9bb121dd723246 0746911057bf56e717eff345dee5b16560a91086e5ac071a82cfdebd5807b137 2237139d11be9022732d6a72423b2097197cdf3261c4ba2d6408d6746e22385c a5adc849f8144f2de8dc7a45ee5097edf0a3ce6efe58ffe318b1b149ffe142fd 6bad6ff06373496a8c6caa9da0c997ecd19a79415d7d3fd37b80edc3d9242580 65b8424fdcf55d59f087205d812d4eb5fa5c83d48521d9b147ee9c4481f1c882 9d186cc877a07496bef6fabfb4436cb7890199bd37c7c545db34ebc47821903e 8932b608bd18200bd6f8c8d30f24bd1b3c0be5a26563fed9613a5fd37a473bcb 299767517f2f6c711c70d147ccb11bd87a30165c571b71a033e911cde364a990 06a819fe3e8ebef08e027718359e988d4f3ef7db03e65e835faabfb2bcff42e0 3db8097089f7eae6704ad2a5320d8da434def4ad51f76d12d9d9f3e170809229 5ea263851d85c822036783ae7b21e9477c3b54a5e5a515c11ab55778c0ea6ee9 a81f98093dd502fb5f20e91bf5eebe7cbefeb9ce198412fb53b693864d29018e ee554c75c1f0e252ed202bbcb91a03ed9fd92ede010b5a05e6d3f5cb34cdc8f7 41a48bcd93940cf99077ccde765e9b67e84b512b467d9a8ccadbc3681366a6df 1f90b581193f01e0a43b5f937bc1dbd723dfc0bdbdfeb9fb4f76a988f92ea387 e715a57eed3d840cae3cd5ea9cff80090464f4c8a8feb83baeff5b90dd460390 30b0f29194d0978fa30a75c48516248bf0424a967f9572d0ce1a12c37984efca 2432bfba39ac86b3cedc1f1ef1b43f83d975cdce37aa057fd6816021a58e6366 4a9069047ef39d739fe1345b9bf4e708d74b239cd6b29f4cc700e33489fc8083 916ba506820ea06d9d664ac72c0253ff04440fd899001392df83a6b78eefe0f9 7c8bf37ed8181ec6c6eb7ed2fc64766a9ad50286a9dcca5a3c5e6766d800de7e d06c4e78c10d9cef7abdf5fab9218f287b70cca535bb1e930fd5c7576b48ed32 63767107d2183ac57e8656a372400cf14bf8d37802428dda3048bfa23b0d96f9 8cb8cddc3fb01e82b875f83a85720f9f1105d225a1a4a9d12a5e70ffb4703025 783fdef87f656dba814b320b4107fca9f99d4e9e52389918ba8604cd08933125 90bfb2086a5b046e500f15116968e781960ece93b811d8497542c1f588b450bc a03ec6eb53121807ffd4c7f39e0be3138ea129e78bfda2425e1a21651e0d3311 9d9455f44b92fb02bd5354a70989a9221fe3e0999c26402a8b0ee43c02b55472 65caef904d90958da638a81b4536dbcb3bb1ca8cca8a75decf75f966935f9e72 df6ff50bfb242cb5ab65d0dc9c9780a9f5406762cdb71fe201a1a17e93f2b823 c960766bd3424e69b4b63090f8988c67e8be73febfa761d0a0b63275faa28f37 f2b8b1311ccebf5dcc1b1b069e25ca489191d97a650b916b3b9d064b9b1d1e67 6d8b82c0fcb3979486d0aaafbd8541c9bfdf6e717cfb36c5179cb5db224a104d 040b4c8afce18eb88e00d26ec8ffc2c10160bfbd43e2fa5ffd767925da789346 cbb3cd8d271435c9b700fcc26797b85821432ce4f8650a27997999ca8a32336c dd7f29ec58896d16bbad592604f639635f1d3917d64df59ed8bfc48b0380d803 32666d191cce99d4534a07f0787952c57573e8e4e928bb6b4abc973ab1e05ac8 09bbf98e0ba31323a4b98c93eeba19f6e21ecd51e3b583bfa71f274c57581698 6e80d2df935e3dd887b75e8cf613756b96da9c744cfe2ebbd52ee3fc586bb5dd baa30d508432583fee51681ed44a1f8958094ffbe525bbd33cdcb47fa6402960 2d9265080fb40757a5a33ba5f414f9ab942ff5b88ef9946c84ad5be363866f2e 428848d90d4ec8036d9e3e52ef79eca06cc891c5c5bbcdf45abc5bb7d6af3bef 7b254250fdf492f69512ccf1b07ba4acb55cc4b56ee5e3486e809abd1f326278 6974fb4c2b74a1328b6974f854efa8e5bcbb8ccb25d18013a2d382c9e9616b3c 86c3cdf6f313084bb0575335b9b52c9aa539e4b7088653eadc57ad1518060ea4 0c18392e0bd43c71ee0f7a926f8c08e10322defc35e7d56c1717e1ff4c82dd73 4ee4697d2938abe2128d9648673b5a51beadf7573431a8068410459a36cc72fe 5f7dfc52516d2d0aed85cdc40f233fc8221a0eae5f5cd337fb2fcab903ffa62f 3e2382e97601f7aa9b8e2f2e49f185aa8c5a7ade93c0bc0ff959aa6990a5c127 617a5832c508dbbdcddab3b95f810dc56b7d328869a2aeb714fe7c205c2483fc 92ccfc09d54c027e2cede30423c1f4d5b4fcb30f4d9e0307fef7a74196babdc9 c264e16dc08eb7de7d844b1b3af1d5aa0539c57aed63c362838613905006524f ffc7e8eb3947030fa7bda889d07c13fc6bb68c14771912102dae0c007b6d075f 8c4f850f85bdcd44f3faecdd17fa41963e1d5cb877fca97d53f86363df110be6 fc604d1c1a3e5a368b2bf79d014a2254419cbd7b9c2b7ba6423d535e91d5d1ff a5fd8196575bc255e07c9cae2521a6837c76ed77abeed3a79c336baeb340abd4 c165f92eb5ad595bf6710a6bee4c4bc8966d9596289c268c2cf370c26894c6f7 e63f1243b14aab787917fc932e7ea783785e5090cd9fc3f3f8d332db5c75bf63 fa687a9ae08794c2cfed4188436e6423bb0b42624fd36ddd909ed7d46040c71a eee27987bf5634b4121be94c0240fdc9cbcd620ff35bd057460219291b5c3af4 10d8ac4e2407a6310506a09d56026c7330eca9d2060f9430e995a8a9045b09c2 433b990b7c96efc3b6b6fd8bd1e20663dda85b62d63391e7aa4e5d568b218b48 67ad0120a58261116bbb469f3fba286ddd8aa203c81332dd6444159e0adc824b f295a161f5e05e59bb8397945459963bdb379663be5f418ff61994bf35785cef 16b3613955294138ec9f9123c2dd5ef8a66f055b4d598b248204d7c5d1a18673 72eda707ceb2b4bd3f42e4127b11a26bd587491891caed17337ed80b3651f073 8350c2374279ec3d0f6bdc1393c71a330c027fb12a1ff2bc75e2bb9bcaccaf5d aae2f1a33b53a61c61f3e084bb18827cc1fa358e51f45bcbb622f97795680adf af3fb62ca060bd393ed015195845719e3edd3f243b830216b404640a13d7c551 c17892133f626612c7631272ebc0a1dfd19f96e3515fcefa4b6fd7413134453b b7fa9b1cbfc095ff9c4e4c6f9025dd00f14ac8c305a261e8ac5c928fc96e7911 e47eff9b9f59674f08db69ecc2cc5bfae4591fcaee7fdcfb43105fb116bc5d56 16fb0e7d6a1d940f28388ddaf6ff0907ee55c6503ba5c45ffbb0ad5480277776 0e2d6513708766f00b2f33b8812546196ee7ea0c706667f885a422915584e84e b12356e5fbe389cd4d0f3839e8ebfd1c551f59c8cc398bfc7f5a330b9962abfb 31d1051f91e8a8a5050fc7832f88d16e39e5c8edc532061c39b843410efa8c24 8e70d775dc1c680c091a7ab2664491a6453f45e929a5476dcc5d02165cf62e47 e7b8766c6b2bd280a1e1461a7c716162800b066320730e7f040bf9eead224b8f df890a9cfc0c0075962ef34f09a0aa1a2ee3c15bad55613497b57034af41b8fc 32d288d32cd4d4d048e9b7738920b7cc781dee53db41ea2caea271c3becc9059 9185290bbbbc4cf012187ae92a34ac5c4d51cb9a54be63293326329c3a41e19c 75fdd54262ddf757f2fdc3c1b4dd90d196d9f10ab8dfd066d3b57117c57b8851 95e90e118dd527bbc65b68ee92fbaf9cb2e5a73c01d4f37047ecc9425f9c568c 282164c188cbcad80b547b35a4090576831d4f7cbbdec994fbeb5b0278d180aa e041ee742eb0fd9cdaf233c2d6ce80a42994e9da7b79e4f65e3322ec0b944729 6ddd5096ff810804008a603864ee6d09b630df68e820147f0ae71b939a8af06b 4dd8a76ceb4ba5be1fbf229312475e4d91ac25331c570ce79c3ae600bba5e6f7 6657ba93ef953b342d9013f663113299cc21bfb4b8e8a76abfb29b2b0c4b5a2f 56a27c404007fcb19be954dd864d273123dfe65321ebc92ba1d40900a83c98b1 41f94d5684f05a109e1dd1784216893b61cf0bdf63b07b49d3c8baf2f80062a5 f4a435b92e1b711aa2358aa6b96dd0b0b457a4cdfdfb22834df900909be17ff1 9f6fc240e7f3bc91f658c3b76ce304ca212332229b8ff26460fff5b3932650fd 200c42686ef61f1dab29f5f83b7d69f3738488efb66fd885f35d3f596c39504a ea80abfb9c6bc8487910c444a73f8440626e8901df1dd2662fb997e6db3a7cf2 0286ecbe59aafa9665dd1244c6c5ea8e48579f13a73908abe831170081591d36 2fa38d645f906165cfa8c5a41c3643d33cf6627569c7b868eec59e326dd9afe9 85e987bbbebb2ef1c52b35831ab3db495962f0d9905b93f8e45b4a54c515e91a 11997870cfff23c96c41d1786e477ae1bab69a01f512b11161768b6b9f1a539e a8d6959207422ea7496570d6c8b9d95dc3eedc069bfac63faa37214c20876224 df54e63a962d8d1683cb8779c42a31663d2d45e40049adfe419e532926148243 f9366db13fd96820cbaa5b5b1497be5e98b69cad2eb94c8887769067ffa3d499 4370482f53b85f3aecdc9e2a81ff7ed121278f18613b11d78687c5570d1c906d f6f9c8f2c7fab819300774ca98ba8582453518d14189037693e09f76a27e5950 8c1cfb3af2f66892f0977d9838409c04d2748ce0928dc3db99faf1ec9d65ec58 98ae59a14cf41890b445b9815c51b8a3e44c8d7bd716e5ec2fc691e53f6cf4c4 50e5b56c7729a5c160c33cf9c050becf5bd6c0ed75e73cfbc9976ee04f219a8e be16ccdff275c7b586c9f94024d8a232e87b37f45ab608d476452b62c8c7d55e b8ebf90bbb969856842dc95e864627e41d11408c9b7bd1e47bd22bf5265c760f b7aba3b18c3b1530b2d9d82d7b6ca02eaaaf1a92473e32f9a936e9a71f055134 10141462c0923b5f9c4aa02fcbb439bf8a85c9c62e7f047325bd01ed7206e28f 4a0d4c94653338f8ae1efe7d54f74e96ee3cfb5fd8d6059b423215a3b5a4b4d0 a9a01164c7b332c2cc59296028d17c2457b41bdbc8de1e471aba244ec568e1c4 683d858ac988021973fa28980ecf4977d84bdba2fe65a77085a0f0259dba0bf6 9627b928cee7220984db614d1d3de04132d328e099c2c08df954b2afcac33f6d 4e6f019ff5b5eca31b707948d25bf1d341b4c30cd79350441c88c174cf913018 9671163841866d7d589adeda1a8258db67953f8fce3b26bab0c54720e4512b2a 6805e6f4cd54a1a1f359c96df919c83a708db8db6f549995d75e9bed46f32fe6 d148bf7910d3315f1fa633cf2a58bd3ceea0c56fcdc66c2a6789d0278b67f39b fed662fae8413ed72dc7b3ee950f8ecb171265aa7f920891178c3aa6c5aaa21b 8711f83ee26c2af295abdb27e54edacd23e5785aa8df1b1afaa329df311db7b4 54031b6e023b0dbcb952f8ee1e7b443a17dc81124afbf7739b7daf4864686fa3 ee75d222972ac8145bde361dc0a61b971e38319b6b472b78b42b6b302b577171 28f6c36800a409ee443eabdfb18e07c69c0290c353f47b16484a754daaa16dcd 4fa367d8e02c9850d624903dfde337a417523be34b72b812848e84d8b571fd76 692b8383a08a0f42e92df79adbd06c71a7dd2de77deaabd08963561b25ccab95 60a729af35d30dbdb670ee02d74262883822add2549f94af781edfc64a744e8b 4d0bd6e493de14f63c117a09a15253b127115f4caf5d12835cb4291b294425ac b5ca8d37721f949856a195ad3eaa4d8e97b987eb4f8ac7592eb9e3ae78cb96f9 355b12d830fc090238554b394affa8cdf4091791615edd3ba8aa5c6915ebb762 ac1b7c19597b6444c5be6b4e3eb01b73bd1646d3264a1df2ee5eba42293c62c8 a742d23846cc08586f8133c7eed40703be050e31709b8749f64454ffcd0ba5c2 a42da38f21b879986b841e865c32bfa1f0f132ce46155d2e3dffdbdda3e43b72 1ebd7ab0c8e706b4452088bcc903e8bb23b37c14415a3388bee7fd5b9fc69e08 b345e1ee5ea0939c95a7602b897c1ad9df8dd9ed5988188535fbf94a76a9106b 055a8fa2213c4ec686b25a5cbd8731aec154743b69d193e19cc3e050b4c45847 8eb8ff610923ff3e42515284b4fe54116abc952553a1757d796ecfe15134fa46 936935a292439f4d59f571e4bc37c98968be0fa8488b6736abc9e6c33d76347f 3261225a8ae3bdfb9d274a065b8edbf89364f63a982aeb3e7710fbec094023b0 9c9c52271611257a1357b23b8f38b157f445aedeef4ccda799f1dc3afdc8003e 6aadba915ce2bd42481a34414e2034a89874af9b34ce4ff71175446e9bc7843f 7de2a03dfdd286dc94cc625320ea17dce9edc9ba116e6785cf4200f9b3481c75 b79be9be6b8ba5c885f17e4b613977a9a9b55ca8e79bc8d250a88d6f7654b99f 76e0dca5d1be09e093b8c30b7005f3ff3ec69500ca0186e37a31263fff7f2536 7b6e70fae62d80854c1742fc5906622edf4522aff65c4d153759eaf766ae3b72 519c0885022d84f362df3a20f9c92d5a331ded22de0f667224be138d5ef0997b 3dc44d791fae305f961c02faa225a8471c02a34168d5d382197a0135450438c8 2bf877029ff3bfc55eceb6055da4e6d86bf6cf1356b572992fc7c6afc8c11144 6a743d3082c8d94e13b9282646e3ce1ecb88a613c2216a5317d0068960bcbe37 6dd5a1ec709c6c03b5577cbba0b92b08a5286ddf5fe6ceadc4be260f3c5289e5 c02cd3eaa04e07bf1a94ee8bac940a0e1e39f5638f99779234d8f35315610e06 f240fa98fa42b83ed439836a1af415d8928d85d8a3ec5fb45cd1e7500489420f fe084c4330341357c4fb9e5654e039a85a8c870622ec2d6ead2679c545435679 92e9a6d75e5765aef046730eea705da6516794ae6a1223e23c549344a530a0f6 11b474324bcd7b5cae23ac678b34a9c24b4e82f85b6a2ef597f50578cf9e7e96 78ea32d98d16b745c8d8d9f71aad18c9fa583945b7a10465c2f1c9d430f9a073 81f489b5356f34a36f56cf94a20f62dccf65903b84211dbd47de26ea246ffce9 c12e9f58b68a5a1cf23d4c5a2ceddb3c2f124c9e9b7bc4cc758548ce324434d4 1673f70e3f70702ad2ef3cc6df9cf752e9474c57dac7e0ef6e924233e6978040 28038c64aabab908fd4ca8bf2cedb3599eabf8499b72a9372dc11e57fea79139 7ea1f666eae6bc7028876cdcbe0c6ebbe2519e3160065b639ae2ed3c7722387e 556b005486efd766b1819ee2c0316b034135b4c5359a3090fa06fe95a0be3631 d449057147e183d63d48a80b56a0f09d88a42514b2744a5cae1acea2c4371eaa 6fe9c98448967e06a2a6e74a3c47b35e75332f1c98cbea3a8dc38720cae1b542 d3697a446894d8510d25de4b8b5541c07e782eb0ebd936d5ab9d998c6a0297f8 985112a53fa14fcef525f7b090b2eea1ff613b6db855b99a6dd3fd2a76f7589c 95eecd418efd1c818265abda21f8ff8b2638075b8874d4e38e6e559a2bd8cb6f bdfd3331aa5c3f0dbca74b82a9b2087a282c045da76d8e9d3930bf46c810f3d0 cd56c7701c72ddfb227afc053d6c12e773f7ed1e3f58367e71d486c8f4ae1acf f9fccdbd6a9a9f6ccc90296e25f3760912c4fe0b3f186c70ba86b104b482ced5 ebaa4f0baa2528d776ed4f1f712e508c38639a07358e90972c538d0452a487d3 95b77a0e401c4f7c0679daa671cbbd4ec76b5b1d401ed9eaed28d0c28dbb2900 27dfac9bf2e27a05d2a8fe17aef8566da27ae1ea5d52f7f6ed5329c86487a206 b942071ff83a8a98bf4d7561f0fab8437f7ccd0708bdce483b67cc9cd70119b1 122945acf53475cd334ff935f1f6b1baa27e0614c15100f2df631ee443f6e0d1 52e3b0684d0d60ba854a75f557647cc6c0d3cf1e76351069004cf918f4820602 faf986fff0562cf047adc48ad6823658536583ea7e0700e31a09794ae8f73482 54a1dd8b6793955f051ff267d6be36cfeeac179433435428ed5dc6df6a93f779 a13b21903d552cb8e818efa19c0c9398eb56583c6ef74b03761c85cabac6480c 8947f51ef4fd4de2fbe6eddf37d6dc7234252c0572101a5ed42def296dd06f32 75c5bf213ee778a68820e8cde4bd4047f2799b59ee51e075ee91799c81f62703 5770626433609630e202ad79d2ff58a323d3d9291bf83166bc36b148b0575be6 8d2b5e60f4ce355ddc48c2fa550ca1945cbf4e004332c9f852471ebdbccdd9ca d250ed2d35d5fffb0d41a7b127f0026566ad05502b97638bdfef8a789f4ae535 0c9fa77cef180c2f0c2d325ebc3f73f0fef46be6d35bc2bd78841b98299ec03d 23e3286f9ac58251ff29eb7b7887a1626d3448f703f0be7e289af50ad021c21d ca7bc463a8f966a8d8d437262884f7fb72abc1ae0b8d37b4111714a6e147fbd6 742a5812d3d58be5c3edf5680f44d94ffd89d76c48c7fe4da8fb4b78d53cbf71 0f8fe70138177fda4bc4cecdb8ff588b4ae75e218def761a875120bbc3f0f1b8 dd61060e408e5d51ec4056f976637fb1ee7bfb608df8174ab48bbf5ba70880d0 8660263fc899799a0d943edbf5e6a0ca6162c0821c83d66b4fd365ccaa68b716 02b7f563e2398f1978df4c377779ca8433db5e3785a09b3f3f083387fdc80cab a7cae1c8cc53a9ef24cbd4099dbfb92838671b0113de101d2f72ada171fd9268 b89c827ea2f5f7f0f10e39584a65ea58ff12aa4e96091ca3ac7010cb212cc69d 6b1013750af0ba7a122ea017f4faee0103b18d3a61804814709ee182d112a8ba aa04391cb54a41645410551e0623c414c820edecbd38cca33a40a95696b4d5fc 99ff02567e6f1d2d29b37f8df09f64fd1c377ecd64a877997e4514c1da02ce43 5712bb26171682148998e63ae886e0166e85e0fdc458e919027e41fef2359a02 5f6f699fbf3b534f125e42856ec0f8a196d592199aec0750a15b2148931ef8e5 278d302bdfcdf7403c11ca5d1c37e1d19abac3cf9ae7171a997974bda357861f 143372f24ce69fe29aec0d11e26a403782b7a94ca2e25be83e68e73cbde0cc8a adef3f82fd77123531d29f008bbef03492bfb4174925b1e8ca252ed0892acd7b 959f06c5bc192b704856124f7b4883fa96b5baaf6516fb913db9089d348be4ae 4051addfc663ac6437359553a6d75ec0dd27b8c5ecd522c24846b9d5069ff203 85c584ab8f692e7710279afdb9acad1a792ca588940d390164a7df777a89d8d2 40b5b0ff0ab4da54427efb6f295a10f72d87b1c23b5a6201feedd89e653338b6 b66d3e0eb8943019f7f6d8af47cf1c6007bdd2f939d6d000002cd0a48adc70c0 822bae7f9c1f584f253925114a410d6a0b1a120867d631b2c0fcdddddafbbd0a b388b487c5acc3ed9595e87ae01a07059b7b304de0349c3b3fc05d540ac5bdd1 c12726a04bfed58d923e1ab08d2bb819bf958661579aaef5400b7cf0332c022d e4b060ce4978ac23c711d0a0beb8804d7cf6f7d79e7c419a872a35c2588c5859 e78eee9ac326eb42fa7103c4b6332a24e280e157399116de64fea2b5c140b64c de7dccf16b7a832cb55dc586ffc2e695733c50c90d33bc3e3e30ce4e2b51ef32 a22d938da61cde004a856de610f89dc8f61ea1086f118503ceb3360f490aa121 5dae94daf5d064d00585eca1e4c9be6af5d3cb5fc130e27940a4a08a5aef104d 663a29cea406a100431115b5d52d415719157a5cf2155dbdf3916e3825429b2a 788bee26f05c6976eebdeaa039728790f395bc22edb6c65275725e381afc0f73 7c70700aa08a54f5e5ca735214015602d1ef9f2a66475f1df9a4db268b3b891c c1a64f98f80b79b2a5d4935fbf882b8f917f3d3a7bf824a5a8f1964bb19c88fb 7afe8725ff503e18279273c300536a4e8dc3db59a3a1693f28a654543444e633 e1549dfa58a14b30e7974fca29d2bba1a375e395bfa3bae2e6a972639d2484d3 d89105ad6b04422824a74071c7832fbb7ec3745258376c2ba25142dfb6e0c6c9 9ede8a8c9fa8ed598ca1137762d6afce59c913612d35c268b91c7f6ccd754314 d27d0cb62e54842a2839a80d5970575288b4e7903f3602e81fbb79ce98e4acda 8eadb3b9d7611def0318241a0b78c044b844a151cb8fd902b8d74bda3b3eb3f7 8aa00bc106b91abf53e7278679a745bd32e7d4484b842fc7f7717195e944d464 19f85065948c6d197919b7ae158e365b8282d79d66ba6d485035d3a00d75311e 98bc9d0c8828840df3becff04249db76b00707fc8811dfb887d4754b953a4863 de4df8b7a293b70288edd25c987bb4f5ceaee1c7a56444ef9b8e3eb00cdc7436 3cfe5e46606a8db084372cb003c95736159b8bf444e6829a9f2d84839a5b5947 37fb425167df9ff39b10a125e0500269504e844345a5682498d705d0a13042a0 809e797786aa222319a8a8ea47aa26daf8b43cd1ce59d324022506369abf1c70 ef010f03e4b320b1ad0b4d4110fafa10729b891bf06c7814f4158fc929d086ca 8a9818d29c01e054c5e68813ab03b9701832da3cc87af147e18588c0aa00b61b 8a27ce9b14a689455a3d48ef691bfc36a1b0644f622272e7207d9a5590c9b691 1c4b783ca02ac46737f2510ae6a6d32baeca42cdaa848929486024cb1766ccc7 475c792d429f9b48498037adcbdf8f8c52ca3f64eb98b6fcc53d2aa1831ccfbe f78da021db0e9f1462856f574e59f3491f8d0a39c0f523b25b8e013ec903f919 ec3b712fb569f343d07cca8feae046c37496d1b547531295d623c8e10d75b35a 01cd7a57b190a39fa968c8c6e8bbeeb0c1321b0b1192126ced950de5f2bcccb2 54d9b1594528d061049e4360527a57f6727833196fe47ca8450549824bf0e166 169bce483b907a0ceadd5ac0584c2b8bc3212ec28208a1d09a9a12665a01a2bd 237828a08008889ce3a3746fdc7ef2ef2a6ef9df103cfb93fc31bb0441d150f7 b68861f2b709decdfb4d81199606616250f1b8e0cac1c66ac66db85a154ceaef c1ca03cb9833c90a803ed6b2fbc7ed7f5c3abbe5d2334977668becbc1cb37af6 06da30ec604bef8c60fe111c496d8024c4cd98021d826c25302d586e17f8e072 a01fae536f077423989019ae47f7845f91e1a6c425e76ba73e0be3e8f2b541b4 980c5175a4d8d42d1f7ab5b69a49d5fdd88b31034f8f1836c8e215b883ccb3ad f871818441807d64c492ac8660c64105f2b87f9149ad9cf8dd6f1c7fb39096c8 48cb2b3e30483ecb0ab0a166161aa02011c1b6b3d7e390928653739ff7c3db3d c97e96c2efa247cb6858e17b30d29a6ece8c39a70944a8ef8e65a1921a1edaea 774d8fe7a7a654b52da3938468b1319eda11effb1f0e4bedcd0c65d355b49ca0 720853b8b21d5eb2229b1146a5a21f571e2875a2c3b03853bd6bb817b7ed9fe1 9ea3d77241344939b00c98c5370722af240c4b5f8e5e5228a1417c5412bcc4b7 359f8323aed60b8d0ab9e3c1804ddd823f839846258cca085330702f22e14fc4 d2641ae09e6ea5adef72550039d0a1cc28804d672e17a392ef1b9a98ea51178e f30d4cb33c5a84ff66f5196cbeb28192e20104c293fb66a7631bfdc3a17a93eb f2cf681a00702ec5c050ae70dc5f88a35992dad9e866f854f2375531fbe2a6ef 2c07508c8843fdd382a5c253e6eef74e3180932b3b3c5ddd96e0df8cbd94352f 3bcba1d7859fa930f4b7598328cbd479d9debee2ea34f744410e18b68bdbc808 423448c98d9382c445819ce26260a64682f9799e5c29248c3a3eb8301ea94fcc 700583ee6a5624d8428c3dec7789bcfbf173e9b81d9eecf4728d2d8933a608a0 948eca1285098fdb7e6ded1cb7907e88ada215a6357d0ca01c1b750c0f4e716a aa128b24c1df0882b7c674bd1efc42705764b08994476c804d12843a4af19405 8a58280782de4abeb23f6f39387d2d67e50c9ecd93bbe0fa81c68dcfe7c5af9b ebcfed8504e573e384d02df7984b2207f8f5e5c114503b740174293fb797796f ccac78524b0e8ba380b5943825f28195f75669ab124d33aa61de3b9980056191 cc953ca3c9d7fa0bab7cc8ed1b1527afa2e52dda878cf1b172ae86fac0e9001a 496f7bc08d694bb93c10a87c61fa902538af48954a7cec2d966c7e745b054603 32b8ba7031bbaad5dfccfb4d8161db1575688a2eeee1291e07b3c22c206cf902 3a92234cd0c016c9585de87d401769de0f9a7eb2618fd39af1d7d3a95ad9d3d5 f3e4b8c6ef286d23701a9a3181e79b02fe7163b2c5a49b6b086e7f4f8c053cab c0b525f775b2bd6cf4e944890aa549b326d4e14fa8b8bc188a35d107dea9aabb 85353c1fb28838f7a980b4d236795d75a03ea3fdf3524b347d92d71b875440c3 73f6ee7817df67c1898033a72c2c91168cdbabb3e0296eb4bc0bcc1af0204c47 41a1d43f6f4e5743ffcf3eec53e26dfb85923f35b5aafddad26a589a17136a14 ad11db00f1f1b986880cb6733ce011c0d727568a8aac00b86f9e68bdca3257f9 898caefd60ba1a7f6cd3648632e0a966fec0cbc7f01021426553fedec03002e9 cc506130c32abdd38a8bef6b9aad96c091891128ac2e7a9be11ef1b1f143f8a0 6deaf323a9d05b98f3fcc868241ee04ca222224eafaaa2d294f551bbfb338099 0fd9d2132bbc529f010addbde7328ce295d573373b7d892a0b073a8bba6d1ac6 677898876f94b2bb364678c2a153651b1d211a796e1c958966bb140b9ddede91 64024db6369c3af78aa0cc929ff8297ce558679e7114ec60385e889a941f3f26 75bb1f904df26658645f9a20a064042d16a177c90689a4148b7cb332746dd2a7 e3302f22b14a24bc18ba1ca66ccfc14773f783b716f964d95ea84f3f8db0e4c9 783dd8645cee8ba8e94d3fa96886dd11e44cf9fbe0d0898c780160705d2e1cb0 4425233f957eebdf58713fb9f8b45f28d95945dfe9cd9975bc08cdb9c7ad99a8 15a66c7941a3e6cf731cd06b28279d4407a6204516615d37590330474f0e1889 db42c1b54086e970a5feb1dfc8cbb83d43255f80d3ade27bb58f6f96ff3a3cdd 11b92a295a59d01e9ff1e099f12f904b799b150f090da6be7a795b2cc177544a 0820f0da5f4c1f794dac543da97f5955c761ba412b124020f17694e230a53245 cbd63560d06609627474e662f88666099fb8d7f61019d27c17de76c025965897 6f943214d753975fb18a3d4541ff0d67b7ed9288c439bfcdfc2d508edbaa1083 38d91252ec437884706008f2be5ff1f88090c97c8bdc1d30a611ca37fb5b2043 1326b3cc41bdb421baa84eb1636524db103681b6927d24a69777fb3597d528fc cd96bf2ab49198025ca8c4d20b3f8afb3209527edcf62b0e033b67f1776a4907 267e9caa5d06c803ab23e7ce3d1b787831d68f641b19df99c7a6bd4ce8608f3b 35166289d0983e9efa422e84fc958d1f4b2d0c4d25fd99b95cf6facd4e9566db 504fe6937b757a8b6f1413f5d7ef24f37a05a4d19bfc38c1cf893b0d434ac54d 45aaa7dabf30821a6b75b60f3ac791f7fed12273ab1bae5f17005e5a2c761d0d e988683693b4d947ab5d9fa0e9125ebf49ef0f16e204a784dc986c7b3f05de2c a139d5629c6eb376c129f63df34d43df92cdcabb4b951cbcfafbce20ed105b9f 5e3af0d8148fd1c4a095a966f9b9865c66c04d84e03e152a0066a0b37800603f 26e117c59d0baa3d1bd6990c7022cfd55a6bd93d7ff5d18f9974f9446713de1e 750a09631ffe737f3c57390022e5e1ec559550a05257f8e09d23fc58a1badf16 2ccb1d86b17e7f1fd5fd4e4834bceb4a657b8289429ae7a5c3d109821905d15f c600cebc29bdd6de98abe3f4bab6f03fa5502c2888cb6541c89aaa9f1680d8d4 c6d3dc918bcec92131f0b58a05c662a63edf904200aa0cee58810835e263f927 add8cabaa1f4e671bf073d3d9b441a4d7a64d1d6090079216649bfa73c489fb8 a325a48399d66edb1c5ef501370c892e97a1444df7a07817330a066c3cf31bd4 bb9ae176e4f3e9703365ead0ffee481e8823d6701e39bd4bafd08c7c5cb434e2 33d472f7d6f5fdd24100278e2a8044f74f21c206331938d06fc26c2f88fbcc2a 75968f9a644217326bbeae19724c8d4bc8f0a676d4e2dd57ca9a9f9a56e36153 b5517c0383f146eaf010b9454dd5c8afb41ba11a0de4f85ed733ce88a3d2136d 513f5ecf50ddbf42f48bf8c48e561a500ec6f9f1a20d7d0b6e9ec55965850d6f 8dc8302ffe75c6c64ec63c69a16363d5cb0f522b09f82020ae64d2b1e3513134 597fe85150fbe5a01082730320ca4bb1ed261d965b5ea9e8820405aacc2ff089 2bdce546cff8e52409e5b83587575d01e5f4351d5897c27ca34de516cae1f2b3 ecabf40b5218e712291b2b16576520548f7fbc06ec7bacc64e78eaf6b15efb30 23034da7288ad0fba033031eab61cf457760a7a23d1068745a21cdd76371d081 aaa4e0f41b6589d5a1a17eb3bbd5252b34464103c55005748b293b8e3f1f46cd 025bfedd1ee0e2a9f735fd44d34ee3e432505bdf8392530f658cf1ffa44f940a 6d5e6d0b9f3e9370b05bd356981adf6bd945b0abc5651ebce0621f8b36cc487f 27a1cabfacde136ad72365d11f2e3ec422990738b14c2d2eddd6c64e63de0475 5d64bb2679d7db151b56fcb9f7c4e5917cb6377fa45d74912210a67004b398b5 15dc8278798464830d22c2c1405b2bad293d844662936a8d228b5ac2ed09e9a6 cd8fecf9981d689670ce7a5584b6d7a025ec960fea61326225de792eba8f8927 6ffa02bf8e67eeb47735f3b4feac464497555e413acb0bdfb444cb20827f41c2 7300c281de387491f032ae7ddb24030b10091fd187de3bf51b0d8fce5885d095 011f5e92b6e9d589a231eb880bcb72ace754690ad3852cbb7993b85d1e2d1cb5 75c2cc48f11afee06fe6b7ae66ecd4b64492e9ecbc86b49b92f1fb9baa493423 2e4721279b984f294b81f91ee18db135a16f7897305967e4c95cc54af64c0965 3bab157d0d40cdc515fa9a36a97ac3bd8d1aa3a19b2a043a3f755f772954bd62 b83130c4c675321a3ba44a5ad2e787bc77dd43bff0c6c07b8274e49b20a5fd9c 4eb000b5c7627fb500ed91a328ebab522cd96ce3ab7c9dc96ee062e6fb7b60e3 89cadd958ab8ab507de8523db3535d7ea12faa58720868ed445ee9fac6486ca1 8cc9c3096531a3428951c25be9ceed4612b2b2c37cb0c490163aa3e33d67a64b bae2007f39b770dd5841e5241320f1a350e111b17a3297a38dc68b9043ad3b72 f94ec5fb751db8e4d9ae46e565e14c59de95825c8b1b65d9565ac2213dabe881 e22069d82c28866eedbaca8e93d54e6adad042f8c83eb20f7042006c1137b56e 843443d41915fa1629266a6ca92f76eadf509a1928f3e0ba73de7876e4787369 9bd6c2997df74e834091946589fcf4585bed4d6a9b5cffdef64b7f65869b2ed8 e8e257a265512f706554cb0707873b1198c2cf09e884ac0726fdae32a85d051b bc1cd32f131bd7453f106eac65e3e3da6e8b9ed0e0de0e2a6881140309f3f195 0fadf2e6e1a2269a604b8d021a39396bcc6e31b2ce92690c49bedf98213d0a49 2932fce6af04f6844f2199d963cab3f6a9f1061e968c1ec2d946ad33e7145a15 6eb5c519a7759cabda69fdfd457277d044346fb31340f085d54a765529dc5264 1606903643b2f14f91d65d0fc93154ac4e11136edc582ab9cfce9cd3ce292eaf 6db5769cf34216e726114a9686378c8a0d868d04ee081dcf738d79ee0e58ac65 1d55a0a87ed1f59812c204ba84c58ff2d20f48cdeffa69d766b226d08e30796b f49ac5b0ab5e1f3f7ae8aacc9c0f3482be807fe7db0e1072ceca07cc620e36a8 184d182c8bbc933cbf0f4f2e506ffd1fedf7ffc9661054e3835337a970ca18d8 ae3e375364d180e13f079e615169dff791d45d0805b42218d496de2038b20b8f 46da3ea0b8148d2f6dfdda31af47e2b55054457783cacce62fb0c031b00ffcbb b7193b2b7915aee203a14b83175016484230a3c53cfce018856c3ce940a12742 c95bbba2f1e49e68e6867c032eda9add349bc9249fe2d0147bd585bb65b15e52 41f9d5c8147264bb1edb528288d7abdbac2c0866a0e6e98cec009049f0908e35 e3c5a5a415f7096cae749e48e8572a7d22e6e6e05cd455b030bb44f7f04c79bf 1bf9ecd096dc916ea1d1ff65efba0a51bddc6561161ecd0dba1e7c25062337dc 1c7da0dc1a07e78a9aa3f16a731d7a31fbab5e95f73105caadcc10fdce03f71f 9f53339f682dc0066a4e278d6598e9b4f8062473ae0f447e49548c3669e5a50b 77dd41458a4310bcbe89c0957cd944e12e0751a7f390239523e397e6cadea8af 9e9a69b3f3030b6a13fe9465d643ede781cd04b40077d2e2314ecffc9dd543ae a81d5df9084e905712552979767b0b2cfd133976ee4f654d72d7abfe7801b466 c95915fde2a914482f8ddd2c0bf3b55ed7b1d488f183605b71bf7ed6247602ef 536e68bb30d423bac91995b3dfafb1e75d33347fd4be4d650fe300b320d7dcd9 73342d3b1487dad2ee85f7786665e3072d7629f14cdc0a6005a5062c13209b3d 7df30fc39e0eba691c3233ebddbbce3cb5ce694f662169045245820513c4b7e6 53f0d3ffe6037405b65fc13fc0a5781f9f70737520b270435f90fd92af75474b 1bd34e5ce8fb509dd651a79fab845ffeb44d815661d2db3f5df48bd68a690c2d d025c6aa2c1afcc495670616bab9eb0024784eede635b4fb108fa4c9ed850373 78cb72bae5ca62c4637c61715643acb9ee86cdff3ee2066a227d119e7ab2f762 9831ef5c971e3df9aab5b2a9f6b0abf29326b61de0cf79ac4876668639484b8c 7219799eef4e9b2b761be36fc2cce77dd3e20bc006018a075014ad733b83d562 17fe03fcc11297d4cbbdf200edf4147bb23f2a78f7fbb6fa718df1655cd81c60 1b6258d260f6447aafae1152cbeaaa43eedd24960e3316d6f4e08690695952e5 39d2e88e96520a825c9517bce1bbd65399481cdfe7d3f22af8d5e7c54076ff86 91e8c1faeffedee6954f4c626fb9cf5b2345855d46782e76a2aef8c7ff057a7f 5c5c599371ecf82fc45284f3bf5b20e3d9d8c11f63fe2c48aff18a2627029b67 5270033f0e5c2f59e896902c1078c2807ac5f4d4af5d2781837fec44b56647b7 319a22ffdb8803b1873b6ac964212326219c4a14f8a42856c1a97e607d0c3716 af61e5ec008fe85599eff545d29f0a539821251e8f4e25ab158392cba2f23979 453893d694188efc402a4ac191c321e46d747875d209ba1fe0e1f14743e4ef29 0d5fc460127669a683d0b81cb09eab4e689ea21cf845746dba03b27cfa564c12 b7ad19ef2c0d1391980eeea82d0db63b708d735d6d65e5591819359581a88f8d 0e95de724abdf0004028ae7677b0ddf7712fc607774ff7f829b4e0c218e17dc6 d8c22260ba70e2950b43db6279c697560a6432bc81571a9a63d0803f7d86d221 4e9cfd6fee8ab09d8fd3945fb4844c8e7ae523ae7c06fc1df04020a40d8d2901 0d3f0e19e2089433ba3ac0fab17a96193ec6636c80c87091d7513fc8c52513bb de5f3b1232064a58c6713cad3ec7fd9e00652c2f0ddd8eba42977975bad905c6 815c248083112021db3261cdb3d74544425ab8f1655b532f090cc92162065dfd cae42008f25dc95d0575766061137987fd17e23768371ca0e1a5e043285bf709 e82028a3107dbb2f36833e679cdde001c538e30b02a48df914c75fa2bc44ecdd 9f6d12bfb2ec1936ae8cdd4d3971fe782c1bfd9bb42e1a1c4ab837be1cbc1e2e d04ec719005386fb1d2c441c796a772cb37c5288290b75331868555c11744a5c 026db40039c25bf452e6aeee94ee8ed61aba62c26a25aff6ceda9c64afebff24 6415ce50e296f0071947745750d70d7ad11614313002834018f69d0c9d792c32 f1babf896dcf002039153d7090407181a48857ad9697e2578aefad079ce6bc4b b328f51fdee74709eaa6c9aba8a3bf78526a0a0f30f1fff3ebcb7cbc42f6c3ea 51a05cd0e3a51f23ee3e9bc218e04052efe1a90eb2d1e6dbc518e02f802356ae 3490dda5ffb6e4c1baf07efacd3b54fe655b9ce65c909bc8a1a483b00dc67291 311ce3d8574b34b85aa00b0ab1d21ebde302570c2b999fe550bff2180448645f c040a0c8ee193eace7f13490eaaa07d3ca1815ad1ebf7ed8ed9ed5880c0b8f03 eadac8337407fdc5174c4e327eff2c8f22edf2d60a9d3de10b97c81a8497c0c3 96b8e9fa5f224872ecfbc904f9c88c480aa5e07a5db03e6b738104e3b656d472 92c1457f45918d9319306d5806c234df4e571c097368f31b8cf9ce1fe1fecadc d0bd390ee96cee7dcd1421f7d72f6920ef3476d4764c2608f3528078f6abacdd c4ff6b646ee6f5497828c0ec3934b76e04052c9c093ff4bb69ef2843b9373844 e318bd908c67811090b68e86913ba7fe7817f48ffb08dd99131dcfb3eb22e5e1 6f17c4b40212441dd05c65b787c1d3bf8062be7e996ef463bbff89dfea6f9284 f6f278167c3dab1044db77915c13bfb73ca188bc9c32ce6e6d920878f1a1a143 458ebc73adedc2799c9afa333beb2a63413552764d267004fdc27fb5edcf4a93 d4924089148da0e167ec1bb69b5486eb2fee11a4eb0a1c9aada7f6007f9f7429 2736f1ad06fc5abf5c5738b01e22dfaccaf19f752cc05c03ca24559e8cd7aaf6 97e89c35b724d5e94145b6d5d5ee210358824b6b0873142ac2cefaed1986eb0e d76b2b2fc280379a6e83d8cd8aeb3f2e18216d1a7473be6c2e46704f539db1f2 ab5d013da05f92687c40eb999f2cbf8d6290fb7fd5a71d72d2ad37ab318ba339 a429dbac49de81b0aaf283c071ae3e15b92fca4cc86d6080279d9e4ce6854379 2a83bfc370bbb160fa097eac651b9062daed97e85d527f52566b572de3c7fd32 0c3102e82b99704a72cfc81a5dd40b5b7830cba1b688b207bd4395a263366637 a46891a322cea854dcfc359b531b96b8bd823fdcea4b5846bd926cfbd03beab6 8d78021cab30f00dd75f0cc8581b614b1e5a19e6900e97d8a8b3ca8c2b6720a8 f292deef0cae97c63ffd4b423f7a3138434054e11bbc315788a91695f2ebe894 32c349eb8d978501087509dfd35880b5e92796078d21d2c7c7cb172343c8e7ad d7bcba55e27c3f52bc12746bf9a13b69d2e06910d433d757156c38fbfa90facc 01c1b5d5be9c267c247c726b7b8a9f30584ddacf82384ff33e9c5de9f350a220 fa625fb3df38340057b09b9742853e3c0a0f52cb62588b52cfd0bbb4e64d12ae 91e04e3ae07b2cb9edb9d57b38c59824d1b7ff63ed747ab343f2b83c1857ee84 67d1db787ce363d7a3a83cccaf8d913ffcdf291a8b0ed0a34a7bea23d4cfe11a 37472a0667b3a9369545f84c5979f240ff1fa2c915ce402e1dd4b9c5a60577fa 2d5a51517341d71bb13e234322052f94533424460afd245c7d5189d0fa113728 8e5d96ca5a6fd4a4f497f08b6a3ecf20e00873269af37913fe1b9df76edb071d cf1cb348efe42427a4271c6d7042d0835ba5d4e02d609df78bcbccec25a7f1db 2a121d71369304612405ff4e8c1e8d59eca40f018cea57784106812520d34e09 f3baae996fb3dbcb1f994db9ee22ee7f8c325373e673d02980aca10288e29f30 1ab07f96ea6faf8b437ddbb96a3c7a1a1f1de913aec7ead5e1f26123e0f6dd38 45fc98ff66acdb2b634ed201fbeed07904f1c4db21a904f90632e59245b492a7 13eb1fee2121418d8a0e36cc32b474173d7ec91efbfb40b5af1a23da3f50b86f a6535b9a0d963b59e9680b3a2755b5601d926fa5cb80251cd4cfc7cf65c2507d 3ec861d4fd10a3437b79b7e04f20490a04c7b92746ab45d34529b45fa0e4c136 f237378a2f6c686deee33b2a60e59dff4fde2c100bb0c075d5573dd06a8da681 1ca894b02a716734625984af42d9b3c8a947ba3715eec2fb1c5276827a4d9535 6e02832e17f91e142e69d2d848e562e8197a97a074dda0f7584a8e65668f3443 8487246f4c346bb2463afc145974ae66d555f631ae5931bbf41118049830200f cfbd6152f433e44dfd48c367217d5cf6b3643d9fc4dac25829d0a7543a1e2c06 4b6fd9ba66e8bb7ed922b7f2a87955d15b662c2759b82ebfc52595bc165d5fc9 905b8890014c1e83b693860401b794dbd4718ac0f6f83780c777f75edf11f58c 563588cdf25528ecfb71a79809b434c7fa530d1224175f6bf61b862f8e58757d 2989d56824bba490bbbb62ecc48586ddc8e5d786aa247df28f0277df1ca9a07f dcac9ff058435ffd530b23c9f034356ab32b7632cd030c1862a3e3763e88861d e9eff091128918466d13020b2c392429a458de9b0883daeffea27bc8fbf77cc9 cb7b3f27eb311df446a83b55001baafbfaa450a952d4e2abd892ea6d1f11fc96 9f23e892a475b45ce4a6e029c917adaf1ba304892cbbc1cc581c3be441c6ebd8 7918bbf023db46042c7674a419a2662f2c358dc40350ac6204a166ce79a69edf e503a85b7c23642af8c7af6544e7dab0e829477059cb60e2a814c57d25af7576 90cecfea04b41aa6a3e0991473ce727f0d6fa9d3cbbf390ac580c06c61107a2d 631b9b6006947fba57327f5281b6b09b601a191d0b27f2f3e59addfa3270982a 5f9f1568ce1c2dea42f545c2bd8b93c82fa854237a5f302ccc65da6563eb51b5 f2e471d0cf21e30cbc45b8f14625e50664e201b8363d2b2eb3a155d25e50e149 6809ad106a6e1da82a6c5dee528cf8d45e5bee53b3f55397c65819802690484d d567607492edbe4bc5cb8660af43e1e6180f07bd0523c530b623976c9d223b38 e71b02d6e1acef9699f7a91fe56d9bc081e259922e6e041396bf0f42d0cbf067 5ae72b87d7de2b4c789d9fc98ea5451871af45133ef14c762aa2be979e8193ba 710e5f76828a00fadbd05a8d0f2e9a81379df326d5ee66dbcb5065adb072d56a 439dd1722c75c286e07088e34a583b09b9059ae45db0abb4fe7d591677ba7f3c 3d22e28a53a344cfc9880330ee2419d6ea614b79ea510dab619f9c136cc1cf5e 86c8c4db6ab8bea4300b7af09e52766d80cc0eb6f6909ce57d118484ac46657b 517a6abd172fe960283a7849290eabc05ad21b4378224fc730d40e62af1f3521 c0abac20001f9d4e62df8f2a3240f1104dc3c2e4c52d090ff3d9ec280fb9c4c8 c02d3309e6fa4dd0a58098278e22b3aa9eeedd15e2c168b65fdbe919c3345b41 978604dc8ce5b05396625fef6b693e76fafb2de5caf1459456126c5a9d13b15a 428217f4af74ac150c097015bd63bb18f3734b972855d104dc08b0791b68961f bcbc3afcc9081494b1d1e51bf01e702456539487d67416b061f90e316837acd2 3f12749c934a6f480f1a063853bb5ac3af30db3d09bdb5329e4cc80399a91134 966e40cc2db9d4bd9eade96dfe34122b371a922bcc738734c6507ec1f9835c57 9c089e2997f66e3710c60f2421c7291f31d44d5dc9049f5087bc8c0eb9965958 fc6048918c6d575d8c1027dd39df54b7934d997fd6c73036f4c5d4689e2de046 02c0c85f053cb20c3b553cf54ffea8ef817a55cb583ee044f233a0f9dc4cddee 836fcbaae0bec257ee410b16737e26fa54049e016c2693b3e46baa618693fc91 e799374bcdae1b86a0b93d5d67dc48ac827888e6b76b252bdcccfd49d29c0576 a27fb9c35e672ac403d8b73cb306eeee6b011a018d3f4eab866c26567cbe03e4 10365f5541c00304a4e4ebaab577be17fb4dc8378c8e05b97d323ecf27847ed0 17508d21e75004d18ae5f484edcb540bbe2fa21e88c9a54d5396af75154444fc 6533f52dfe23674d97b62173a5eb9faf6efd627279f32694b5a2c6da89e4723d 4a1106722253173c7ba8e2c0f051893d153c2b9f3a8ce2af1ba72100dff185ac bf42eac2ef5312caf8fe6ac0964e7241954b6c9075d82f1cf767ebd2fdf54d3a 747abc508f32102e808aea06cf52cbe5b328a6bce1c1e71448bca15796719175 4927eb802b0e51444b3aefbe69f7b49298e56b38f155d2b28458a542acd82016 e497f959f3e1671dc01e7f48cbbf4208d22cf0af306481e6686e42b9b4045517 81bd3d8d27672103455def6ba782828596ecd26c7d64bc9108c5a1394fb3e2f3 72aa1c4de7e800bf73034df45ee33b3b070a5fca8756916e92c1b414f0eea045 c134450565d111478aed45e01d4e2cdfb3a2322e5df8b79cc0b589c96e9c1bf1 b324ef12dd1221ee50221cffe591889739193f1d124de142c467bd9cb74d3f62 a94e5f245b1090735585f10ca659951fe7e06c652a428d7fbf5697bfc9eac82c 247c84d954208e1e2d4555b9567e5731fe608b278649209c32031ed76fe0223e 103248352a9c86b960b5b2db6adf85a1bdbadda8a48582ecd3ad6ec0041282ed cd0dcda08bfe8352cade71bd95da3fa2f37c2bcfd39da4801f9b307ffe73a7ea a2fb50a8e4f0de323fc03d69bffd76a202bade84d521ff03e55f46232267319e 59997e5d0333f0d3a9664055a6a629d276d8d625e1884fb6cdd5991eaf8f4017 438a9aaeacd60dd9ac843020dc16188ddb6fb8757bac3fd8ba12aa5323f399b9 d0bbd602ab04bc94b320b11aae3143d2223b5aa7f25c38bacf92085854328e65 a7d3f81c0268eddb9e75849fe6369947f2b1b082d9a6df3e90101922da584d64 77a32f6ddb6ceaea343182eba0d48c565a8113819140b82587b7b364c9fea6a0 494583beffb0586228e215c801ce62c96660b61b51b89ff3d002574ea24b93e1 f35182a6cd4ce8e19a26e51a8581152c60884739034071dd98cd6ab1bfdab382 f91eff48b55e4acd241efff01018d503613a73572deabc5b2826e19be2b4b83a 39e1c05df902de838eb0449bb54a32da3c4ad826423980db40ebe37a55441d1d 74386532ab5c3313e122036ff9a2fa3a0059642b6fbf00a3cf049060df9622a2 1a4232f7bf4834584428274a312f12a7f7f4d02ba26e624eebbc1a21fc5f750b d603c57ade0f2693ecaecc3a519186209263f071bc1c9b952b0d707a98e38d58 076eaf6923889e0a863f4eefa88969e7481ff87d65c4ba2461a61bd895529e07 c5b983360f2f304790042b522765f6eb0eb68dd399fbbec22b330b978415e7ee dd46553407ee4f652365aaea7c4b66c3597ffbd24ebe955c69cc5a30be7515bd 40e88fb508cc332b953a7bbb19464b1fe77397f7cb040f4b18d59d526ae041fb 1b2aa9795bf8a0358b87b211141a57d805e8cd961c6deed2ac6e6be832895e2c 1bc94e2abc699e730a35dff2c1cce6571cfa718ff598031a52f38881dfdabd9b 2ea175fbfbd5a2d6eaa616c83531f3270785c16eb47d92f264c68d3663b61409 fd033334cb1296221fd81c763fc28be17ce37971682be28c3f1c384e2d688517 bbb4e1fca663ba516e64f9c66020330d8b05ac0d42f9c767d9ca1bbab7b81f39 61acce8fbcbd475bf1da15197dbf4ff31b027be32be0b01eb70f314e0a106b8f fc0b07888aef113a9c128b7c3c67b120f5e93937dd7ac907df45ab80d30185b3 c9c0b169a26d3f88753e127c926f468648fb6ebf095e8e4115a7acd7805f5649 a9f19cd6eea8d686b767d14bc368eb2445b68a99ae910a68678e568332b1c94e 12ce2f70fc33e959e8a317bbc3ee1fa59c61540b58773f4629c1b15ad44913d9 d7349e9109d4507885afb977d195c455f2fdc0dbee200e892fc39b09431bed85 f77254b95ba3e1874ec2f4af4ce2be8750fc4a26fbb2e68aee39b1f0d6dddeea 693976d5048fd3df32c5c2271ea022029c463e0f15fddf86a7b051d79f5adacb 918b0968a98a61d2dde6e0c45ea1fd64a8543851a17920ba2c793318ec3e19a9 d6e081a75f992c5358518677c9eb01025a83528e1658130017495b347a1e6893 04b6def24b82a85107cc3d03cc72ed579680a0a63d1776961ec26af6fb07b2c2 46cb97a5fd6858b7e49d58e7af3a6f9980eb50c17398b66d5d4adb7232a8f178 13c301dac44e637f07932c0e9bd39c0157a64e0f8c529e7fa432f4356543e6d2 2d2915d1252d037462704801e0811d7587a24dc747039d8b46a86134c68911b0 e1fe061bc4a3f3135b74dedaa603243059083fbff65acf160f928cecb4c85304 3659cf5a994618a48a88a37693abdb28ab2de20245eb5b93e282b57160320420 42770fbecb8eaaa1942f2b8abdda4cdc5129b9c944f77a1c05eb15f5b53e47ba 5f605189ef5fc0c6c940b7cdeb02bba34ee4427ae52a736857fb8c4a9819293a fac696a878085085692f85bc60e0872acf0fd1d1468c48804b609e1d362c41af d0d988cb3759c4a598bb7403d77cad9221e75e7a86fa08c8395da8a0f08cd399 e47a570db952a283e2ec15b30d74ccbf2688b7a5962f9871aa9cf883bc607b35 a4fa3c9362e61940509d2389ed2aef23513748af22315b92163960af43a6c4a3 e173 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 cleartomark {restore}if% End of font resource %% Font Page 00 /NimbusSansL-Regu-ENC-00 [ /.notdef/zero/period/one/plus/m/underscore/C/l/i/e/n/t/asterisk/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef] def /NimbusSansL-Regu-Uni-00 NimbusSansL-Regu-ENC-00 /NimbusSansL-Regu MFEmb %%BeginFont: NimbusSansL-Regu %!PS-AdobeFont-1.0 Composite Font %%FontName: NimbusSansL-Regu-Uni %%Creator: Composite font created by Qt 25 dict begin /FontName /NimbusSansL-Regu-Uni def /PaintType 0 def /FontMatrix[1 0 0 1 0 0]def /FontType 0 def /FMapType 2 def /Encoding [ 0]def /FDepVector [ /NimbusSansL-Regu-Uni-00 findfont ]def FontName currentdict end definefont pop %%EndFont /NimbusSansL-BoldList [ [ /NimbusSansL-Bold 1.0 0.0 ] [ /NimbusSansL-Bold 1.0 0.0 ] [ /Helvetica-Bold 0.936 0.000 ] ] d % Font resource %!PS-AdobeFont-1.0: NimbusSansL-Bold 1.06 %%Title: NimbusSansL-Bold %%CreationDate: Sat Sep 4 16:13:43 2004 %%Creator: frob %%DocumentSuppliedResources: font NimbusSansL-Bold % Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development; Cyri % Generated by FontForge 20040824 (http://fontforge.sf.net/) %%EndComments FontDirectory/NimbusSansL-Bold known{/NimbusSansL-Bold findfont dup/UniqueID known{dup /UniqueID get 4052336 eq exch/FontType get 1 eq and}{pop false}ifelse {save true}{false}ifelse}{false}ifelse 11 dict begin /FontType 1 def /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def /FontName /NimbusSansL-Bold def /FontBBox {-173 -307 1097 1199 }readonly def /UniqueID 4052336 def /PaintType 0 def /FontInfo 10 dict dup begin /version (1.06) readonly def /Notice (Copyright \050URW\051++,Copyright 1999 by \050URW\051++ Design & Development; Cyrillic glyphs added by Valek Filippov \050C\051 2001-2004) readonly def /FullName (Nimbus Sans L Bold) readonly def /FamilyName (Nimbus Sans L) readonly def /Weight (Bold) readonly def /FSType 12 def /ItalicAngle 0 def /isFixedPitch false def /UnderlinePosition -155 def /UnderlineThickness 69 def end readonly def /Encoding StandardEncoding def currentdict end currentfile eexec 12d37711f3ef58d4b0160ca0e8a8b30beab80afbe709c0f3f0e7c12f2b9fc5e1 689c97a13ed505485a0694b1f9f5073a68fe9d62a62eb903fa05dcfad309712f 35d0572161bbfa0e34353d4dbc8eba4cb64daf302e5a572af95ec19155d8638f 902049d1de099da7c141d9f1af3cbca272682c2f68b6b73299a3f7c10c735c5c 2343c3dd08fc18d5dfc809764c249432aa7efd3423ee8bfb8c8c24869dca9829 d30733c8c6ab2b758a9e7b443712994d226cf97b4a0290ce438d6a26b068c8e2 2b1fafd0b133cf5729c70d0477a70dbf32803a86de61d0a525c98bcfed20dbf7 d88ee8d57298222054b31ed4d4a32ac1f2e28f0cf66be2686e270a16cefbca12 e1bd019c2c19ca05eeb47fb7cbeeab9be1803b792b75db055fbc90c9fece7e0c f521b5e3680c546e2c05eba1144a0ed613c0828765d544ff1f18ee49c6df34e4 8de59c59398d5480c5e14dd9afe0793f1cf8e39e473d4ae1af21fd6997971026 ff1ee1366f70f6eab18ef2c48b665ffc4d478d84d2f52460903b1f18873c1dd4 6f9791abfdee5f715bc45909e768818bff61e374cfe1247878dbaea92a6a3ec9 5ebc1f02e99d80ed556fe7475804761e9650fb0e2cf892066a7e22962cb950a2 d07b52849a880514bd840fcacda5696ac2ab71b0ee2235c7b7b364ab1d2bb335 696db8a18f3e3a74c8a9c7946b3b8c4a6018b166f6b84fffbcbf2a77a023a2cc d2648f077345da8af2be9421b61921d18560d5f5275e9a1018d6e3446870d7c1 60ee11f6e072870dc3e0dc28ee4e9c78f02cc9a97459cf3eb93c8d6dc362bc44 32d1f5bfbfca58a7d9b2d2281d53e3e913be71e189500960d2de46fdac773c49 d54e43f53501175b00d238ddcfdf8f3ab2d4c2b569e1d57351ef9e0699dda0e6 dd2eb21254a4217452f044762bc97e651cf39f7f171dfb9b4513e35e1d2b8e57 44b210449435a1dd7d3587f62eceeb667b266e4cdb6a40234894c682c6168635 da57673256c9b60cc6c7254a565a560840b2491a430b3d4414285ff0e1649868 cc3dae82fa82d60a95a48b82e4db40f330d657fbb121a200898595dc11e873c0 471f0e870aaa81df7257e39e039fb8eb43581712ac05da28042eb29c0a7a4c82 a4ad32255fba2850384ac69bba8c4d401cc772a518cc89481e8feadb7a970fb6 f3ea75b1504de159fb4e1d5d1e16012e67d5af21a4479e24be1c3efdafb2aacd dd1631dfbeef91f3cefb83baa2a061a2e39b65d56ebe4a6766cccc0e8eed30fb edfbc4e6eac5443f03615b5e1cef3420638f8b2978de6ecbe6654cfa96095ee2 974e6f5e07ec36bf7b416cafa38bc55dc9745cae8f77ed2f76c094387b2b0dac de4eae0ac64628ca4a8453d58754c0b15caf01f5e1cab0be32dd084b63173f4c 64edbd8b65afc94347685188dbdb7573827868a9fb128f1529431225f94fb723 6008e71a07afb2a9b3d244931774989dade07e88088367c85f813d4d24845a53 6e90dd976f0ecbc0a0b703dbed8e55cf8face122d09785408dc07428f260b08f 6e2f43524e56f6fff7cd1598ef6046795a622ac54b1ed16481e84585e86456b9 93ff491f930c1351cdc9339fee2aac077510478b346ec14b87077921db58148e 2064c504f963ecad6962c3409a47c2e13013856fb62786aa2a16672f7547e910 60a482fe7fae7825d029a7c83c6ac9f2a5414a0cc5589f6166f3c351efc43325 69562fa3ba3f1cc7b5bf02fb79d4a51b502af6f1684682239a2254d6585e3731 e47951066cf8d0a41051cc471384dc7263dfb82b20e63bfd83767e8a23e734e8 7fc96f3c94bb19c8220e9810f4b8bb2cd910072785435f73a92f058acce10d39 633c0df683469e400c4935a918b43cb53d7abf34f32e6607c1f1715383a8ebb1 fca21438a494ada15c7b9a1b6eb323b1cb6ed435fa19aa0dcfc12bfe99eef8a0 29547bfdb4209a6c767cf1c296b30f2a99356fbcb06504fa881434c78d17891f 313c5d14ef2d65e7fabf746c14a74783c258e0ee1a12de774cdd0a70ec1922a9 b8ccfffc0a3185aa7465d3e63f9b33ce5f285f32fc4e501512a3ece6cfe66c53 ae754087ac277a608edf43eb58383df7ef4dd10c87f9cf385d77c435b1295e31 11c45aa579264dbd04eac244cad6f9b9ad46e53dcf5c2e9b748cd5acef992def b8dfb8896d1a57450b383afbd95a0c44798838a3917fba1964e236f373722055 611b072976d5991088925f6e6c5769edfe76e1d3558963928d85b2a7c3a02dce 431a289c1403465a7188117508e17c4d60827ecba66c50880c90a0f36b694c09 cf052c610497d442e859e737a468eaa1f41baee4706b6c67c0b40695952c64f8 85654b580a57771b8ad9c1ccb2079580b29ac6b6ebff84389b0d1b5cdeebca49 760c5f6ed8e3202e094e6995ea806da24f7e4e0c285574e653f9c27f5d782555 f7aa74da7afe6c5a1ab0f1c8686becf8f98ae82cb2b372c80f59070ab6a6b6cb 229f9b1e48d7ce20b17decb0702c0c7c3ddd312533b39b795660d590a94a4466 b06b453316fccb8baa2419d4b6fadc8ed6f891848923e6fcae65457edcc1204d 521899361f98b25819b966c39abe355b6496088a0f775947dbf7264693f8e10d 7441766532daff48458c1719c88f7734c6255505ca8485e8d6285e7f2e9047f1 e2752958cf41efe1214677148556f187ed533d7f1003220ea302fb1f11d7c484 35eec0768fadabc84fe19a575a2411e5013d8a74a1179fdf6b19aec4c1476b7d ddc6e37fadf6896e34cb677124ab07bc76fcd3bfe9b1c6da50b837fd9f15694b 8e88b0e114a782f0cd7fad5e24da7f672f6c8bb6651308365eb542fe028a9726 9c2880df8be16993652a22d57ec149a6a210bfde78477510a0e5957bca146fc3 5a9de805f057cbaa8215cb79528c2be33875159294616f506bdc0f19fa8ef3c8 19282af4204ec8e64aa8e1ad0e7816e1c212d1b347bb8fb8a5e8a2035f6345f2 a5f5562374361ca9346f34d3a5daf995a08948d8a4f8a9ab8eb830d2bfa2b1f8 16aad7a2b307cbecbb955212bf4e373b20cca61e6fb89da468fd409e4bec679f db8fb4ff048a7f232ce3d44c71c875632e91d49c2abbc4b9d95d3844a6b0751d 3df978abfa38e8d2f8637edc186022d5cefd913265a372e4cbc21733bde3622d 7eab7c59438c42bac4134d04ded5c5943f8a74e22b527e4617bc41ab2ffc1173 936ca332b5b265a03cd8ea4d1f38482ed25201cf89926c933161820838e04751 c44c31e2b474da33126b022c6e38a91681e2acf9df01856b74447cdf44fa82eb cf4e2deb56b03b8062207755066437cb4d6af911cee0c8d0a1d3f36f3e224f0b 44b9f1f93b190705b7f49c8cfe2c83859053af7637913dec2823bfb08a68a6ac 5ab6ff191dbd6c15b2dea92e629ddf2ba4d7102367ba684988e0e429ce7bb654 51c37e15069483c18f66007dbc02e23cf7b51881e25c01227ccf4e9dba48160f bbf8803be5fa931fa0855dd798f931332a95774935461529f3c1c78e44dc614b 4a28f2a6ff6b33bd3e063f70fd54cb87a13a3acc33a8080a3c9bb05f4642f397 71fc185db7bcc9877091c7c7fe7488b9141560d46f873c2e721e6596abbc9a18 0597370493e02e6feffc74ac8ce7a3ce9cf3d1e153fbe3ea057048ad60666d8b c9261fba1dae82b02b4e54f0e463f0f997f8ef881a3ddeb27b1645e2e43f36d0 6c2e35a4f199a1f617d11a1686b8bfe87fab2cfdd6c920762f8a1c6bcae7eb67 04e48bd3e0a0e19cb63a8ddb039c9eec84ea40dee3fa92bd94a394bc3580c8e3 41cab366a596adbfdd7deda6cbe74efe3c01588936b793cbe5c3cd4c876a0a86 22d1d6d6091ec71b76ae9078c28227d397d79f81fec2c2eba316f10afe920efc 0c486a8883e67b5625c135192443cd29df71c6f36e67cdfff1fa4c2739e42329 0ff87effb17b2c7eb3018dd3b2ef18e7d75c474c662b08743db4d5f358e3a627 73ce160f630fd001f5a90fe94429bf71076b6fa1fe6921e92c883b58de80120b df2773db0972a3cc9a26de9f063c1b6bb73f07625e4772ccd444567e5608d9d5 0ac559e7ccabfa3432174f61d1ac2ab06b1c29b67569bf6b60eaebd6da6e5ae0 3862ea1773b9e20807dd3805d641abb927267a6932ed2f1867622580808b84a0 6feb13afc2bbba506777f8796a23b927227e51eac691c01d7a62c21602d3ec12 052a23c7e7e7fcd89d2748b96542dd54e28dada878c4fed79fee1fe4214fcf43 dd8aa2c3b1eecd4c5310af8c928e8a74a1a5c8985810182598bdd95a33842e5b 6f3764aebb47c8d12a7645604c4f7d0c3ee252a934e5300f1d377bd990b1c895 7cf8003560433537c084a1c4db312139c2a60cb29daf96a6d143a56d87041659 8b3e31f4981095294d9c8b46c47bffe24f06c78f142d8bf49e8c0506518bf6ae 76b61e23decad55e04c9768348aa90cbdc3d262aefa77ed4e3062f4e6c9d2bb5 04bb93dfe13cd9f34dc87d40e8ba194107a1e67bf418724bb49b8c450e2e43ad 5db67593a878e3c07812462681474f59402952c9231c38861c2d19e0b779b642 6d9ca219990298d2139ae846ebfee2a83926b5ca6baa4edfc3ab7bf528711984 cfa5abb53750be12c84312a441115cb537789d4bdbb8e7307627a647aca0da96 a502bc592c6b4e1ebeb67006b4f6bc6c8692fb78931db78c91f7899d6defc088 223c5b7690591bc545b80b6b602c5e15bf820b682330fe1966d36bdf76b97084 c59842953debe194cbb2ec8ed5f5a4f32c9ec2cd1adfbd2499db53bc026546e6 537f776bc6f06c6e83c7175d94a1984a54c1b5c64a142425d40a732afbe2e284 aeadcde7099c709b51392f4dfa771d716d7cdf367248b2236ccc54ca24b84b2d 7a0b45b1a72bc8a4e2362ea1848749b798aa3cbba9cf459551d24b1c64c529ee d0af3dcb82b39ea37525a6c55d54b9e2f1e520e0f4064b24df2010948d2b2982 2d93cdfa026674ec02619ffa408c639f2833320d97a85f467a4d24263e81bcf7 58b0dcdc1477c15f09caf8d2332a07d67e162162c7565f2a9f2d430fa071fd7e e30dd66c279c6a6cb7d1561f1409a7c7f5050c16ad3dd3906de00fae9d389124 08f7038f34c24a0d60fb786a5a55b9bf4ac2ee65d1c0233e3f637fee6591897b dae7f6d76b0a814f6266b4965085bc59f30ca8b36ce9ab82c2379b8348044700 51142814fc4c87d01ea25771d56a98b9bf2d1dddb92917717263118584bded0c fb141cdd351ab0ce85a90d0888b8d81fce969610a7732c5f39d46fcde5959b7b 5ee9a69c2570c784cdcdd079b7c50b1dcf69fbb58dc60572d66b336b84d6bcad e6c525fa5730c4bae27fc94d5c78f393775a97ecf8c9409b00aba7ef8e640ff4 352cff863e0e39be1b856047e579b9249ac6b6da543c1616774c0b069c689880 f5079f45970aef1e7bbf44b749b32769baff59913185a2b9793c0f5ff5427ce7 e736bf55e74df67428f8ce55bacb994a26916391248b9844b4a67bf4c5e431fd 182eb48bb500a645e95fc2333e9b38eb4136f4c7079f1176fe0a260a386cee1a d648e6479ee07e7dbecc5616a7177d7796fb95cf68f2c7224bb26bb8795b747d 6dca1676f0c89c66e5801c0c4e4917e79c11d9afbc409178ae62864c3531fdb0 7dfb6a27d61e247c6a889f2d8bc8c629fd66c321dac4222bababa3fcd7b94bec 9642bee92b5677ee428381cbcb2abe403f2e2a612a1df6ab12552ece07bcd880 7bbb15d3636fb9d3b06a85edc10445f92a1d33d5b788d3e5216935e71bb1780e cbd90632790fbb5ae0795831db61274dd629f3a82d48ca829b9583f024ee6245 63fe77aedd5c7e735baa7906811e58ba334f3703757eb5262da3b961e42bbc47 434c1fa9aff45ab78f8f20f5e73493357680453d3c1e9a5350ca2f7e527c5d54 8a1c19c4335037a629743c74bd227d09123f6c995f45f5b254084f86f1bafeb9 ce7a4aaa85ff63fe2608ea6dfad2c02a8c065ae73b98f8203b2ae3ccae39956b d485ef5bf55a5c26d6f9c45344aee8699b27694c1b81d581563369c72c13ac5e 8c97d4676219b3a783a8ef8adde1338e9fbaa5098e9bdfd16ec8d7b92ea29622 bd5cb0bd5434baf4a20135f0c73c49ce96f7781a381ff6dfe8dd27e5f3605f63 8d481e2fdaac0841537929a8d2c9f41f97a7353629661e08732cc7c62664057e 17d771c635443e41678fe8e3ddb053c3c138100ada70669c22d9c2c53bf8ea03 5778d9df7b56f50c6fdb1d1c0ccc126d94365451808a696f67ed391b862287dd 2a2e87aaba7b9dfce3ee77144a3269ee3ff46b759a3e367d6097064aa5166a13 d6849c200c588b93352e173b6301aa856e03a3e6c4aa41016197f8fc762880b5 89a356b74451abae4f50e0f3e30eefaf4b84fcba462d7834e44c4e7d649ac21a 445ff6550a29e37c854308eadd6ce899db7483980d61b36defa77541ffb1e134 bef2dd8e87b204dec4602f79b914b6737628e56f092f97abe83466dd43775443 7748222140cbe140c7ed6a7de1dfb9d37c7c80a98fc0b3ea3a0cae07f7e9bd9d b95e305328187b068193c15a2a9bda8a34dab5b3eeed955fdfb6572cfeb059a0 19b99963672b7ced0cd00a3dcdf61569c165a125cc36d0bcdec339e2a947fa39 a355a3270beee907432dfa4654d530256d5c3699e155f346fb7704fdd4b91a96 765eacbb2ca681e42d5b2a173d5ae88a3b5b10003f0b290bc24ac9b7f548ec24 1a2ce1f3dd146c45649006cdd8927189d2d2a6aa5d9d0207b39eb5c7a8cbacfc da6f8e328166526e70d749016797fb13fc7c50184fb421d079cf153751cd900d e5a0e2fe285059dbd074a5b01fa3c8f432834c043dfe6f770c56480f18e8deca fb4dc5dfdf720c66033f0acd07fbec8f272da433bbe8ed03a3d55f38dfab005e 67db8a021f37873cf93aaf74952309798f5064bdef162b40947ae314a38a9283 c37cf1cebc1f1aae318bf3640fb896dbf8b70712929c73c9cd89271966e6336c fd6c2714b364f66257298043ceb03d2180e4d98ba0af91a866dc63619316bb3f f21b505fa0c5c3e78626e4eabd9ab608c34e5b6b0b4c4b53576a6af1004ec02d 5cd155ca52185eb0765c300ba2864b8241f6e22188eca4269b092a9f491fa8db 20e143b76b9b3f7cc59bb4d67325306c3079b21a057bebe817dcf4427c4e9340 8c8ca0369ce588a690bf24ca1a48b97a394e62bc3f5cf4279d74438c9eb7c14d 850b52186eb09e2249532e9b1375d3b35b3d59863830b0c27def7528711535c4 a3e1dbd82a4a1baf8954123e6bc51fe80675ec732961d80f175ade8425df674b 75a83e2901f0074222fd5c406223247e845a80dd99674749b33c6e1658c1858f c2ff64baa03b8ce5a9c54506cf7ff82f04f09ccc66bf0faa27bc25e01d0385f0 5433fb872d804df35da98c72d8f7bea9e489f1cb267b678a0d55fd358323496d 88825cb4aed03610f3bf961d47d210025c68e65cd380298e6ae0444f0cc04440 a591084ec06e047461ddff480aa009674de15870ec35533972fe6e1e8b0613d0 b3e6b9410fe7b69edcfcb878fa848e6791d2333a309e4ded441423a0b72c3707 6515fa51044851e2269ffa293ca9e8273808b0ba0be9d05164afb0b90a4a97da 7857e99566766898242055ef9b1b335abe3a52d2ca44a8c3f7d9823560831c25 fb3503ca957194fe50b4b0462188fbab714d59e44c0e2857950b64c5a984cc9a 1991142d4f220f20a812a7a3d0df450db1c6402ad692d22f34ede899a33a846b f36e0df29037e30db8619293d680ae8a8ec6ceee72e9de0aff1f3c98460983e0 9aa6fe8b76218de43da423382b6774e2bc0c38446e72b111e8099cdfca0587d2 53eec3c03289ba41ed616ad4981c3bfce76f32e61f3dba3e0eab17d31c722468 e8c4210aee9dc51c574a61ec6bdb3286a6aef026a4e7183e2fb1a8c34f6c50c8 e9343635e8f15665dbd0796833fc90353237c4a821e329002535302a80d88879 20868d78958391208ebe13214e89e029dfb53e9ee13701a4996421d717ba7078 924ebc1f2d0428a7c60758a63129c789ae93c7e9340f8a6154cc00a148752d44 f552f40559ad0275b948fdd86f4a220e7217d89d2c9610ffb1564a8ffe3eeac9 f8d195d1c78619972458c1963cfab472eadb8d0e16af99b94e9ec3d59342a92a 11d171b7cb1a3883ec4892ff687a0c7a9523bb1bd9008d12ff06b3edf241085d d0dbbd5dd24d41ce8bc36c58b7ed2747a7193ddb0209bf079768d1ea9c04ff94 44e4c45c7f90189cb4da4d2b53fd536f67cc6c4c6b34eea83699471c61ce196d 8d1b6d3d132644bf41c9a6b4a0ab36875e9130f0d279fa9515d92bcfc5c109df beb12cea75a432932f8a5d396a192608da70a15379d29c827d1d060322b2267f 79589478940532e362debe75a38f9723d040fbbc18bf33e3886f62c8ca1749ee 478851762dbcdfcfce898e22899cb15267aefbecf7c77397d6824c894bff91fe 0903908342972ed8e7789ef98995a0e8857cea807c8d730c963bbde8e1e18ae8 987e2f73eb800b3171a9f4c25f23ed2bf63a091bd14dd10a465a6f7adba8bdc6 ac5eebb589d22fa96b01e9b7b810c4dba7d225c1f53713226d0a5270949a47e6 28f7d0169cbc17969f59fc482bba8ee8fc966cc7af4aa1f0965f920b26577b77 66f8dc61b490133d8c0694d81cdcd7cc0ac4688b502f0d87b613b06dee3273bf 1fd22f4032e1adde965ef266e63ddbd250bbcf55b26639f02b123d3a36ff7f2e 7b7640c2a21e8894d2c09da438073ee77786129299a01f71f67d5ecdb1703745 15c0c3248197f55fedb43adc5b4d7e899830d6050cc323732606c9dfd0cb0d55 06b4220f1f04860273f2e90f535fc184c9bf3c781cc4eb209f8c18d01d8ad4e0 f0014b6750ea6dceb33e58bb8f559c98ecd69a73b5ac6f4e1861d00638982734 c0d2ddf78d6a984a5788e95260e9ca766bf0428e48d04f986334e0906bae9c7a 25843d3e470101855beff23f8d96829d59f267424afab2cba3411cbe310e5e0b d198b7d0605b5f20498c8fe1021e8618a4f3a9eca51743c536a2dc6bd96fd056 45ade0d95dea748a6d2e7958d77de5acecbe5b1e26b14baafb3e1139b32fc51c aeed7c6f422376e818027632010405caa3af558b20afb317e413ffc57458dc00 7e55da9cb44938dc40b374f447100d969cf197f1bea87f0d4c5d2085c8d7ec43 c53cfb474efd15c2fb48077b1a5187c47f75c343c085b9c99f1afc9f411a53fb f95649b8e4f0022fde51196f72328e5867552c27da75bf3a4b1e05e99bb8df61 6a7c479638356da4032b9f34a9cbeac3eba6518215d75034137968135bad63e3 6c28f5b7fb243ba3b4cfa1aaef1abcf382bc5b9331e8c7f53cc9a1d45c835cb0 96f1f3d7b1bc74a1c11e8255b83d0de489abc84ab576901b67495cffb9c7c9d7 d0be74222fd24d48d36a050755346351b8f7ef1ded5a1a70e88b64fceb795ace 129e094b4a8c5b05615df2caab1782c09b89ff12778daa51d2e55ee4325745e6 a3e92ede36dcc06be4107b3ba53a18385d9ed79992b257c2dfd61f9adb3281fa 262115890e71699cd9bfac868a5e51ded36898dd3034eb62d9588f8d6eced7ad 07294e6e58fd2692c82ecfc856474985bd7fd8b596ab02cdd4ec14de67dcd136 4cd63faf9b2935e6d7d1cfd7382aea5cb1e7871456245c4b9936f09f762819d1 7766e10f21961c7577a62835099b7a551426924fac329ede79009f69527a0f91 edf795234a672d871254b93589402e88ee2c10dc8d5c720e4c7f124920f46186 d0f376399ca80687c2e3f6da7e0405214622de8c7df71d4fa77a6f698db974e1 5edddfe4d3382967877bfa0b901bc254107cb9bb3e05f6c08e44bd71f80d9499 6c1b03197b370b4908b31afdf294a361c27d1db252e5871bbf6ccbef4183749c 53a08d6f2558c0baef6f3fa88045f51fcdeb9a8ac5be2adf8964fed0ab1ffeda 9c028b83b1d9d5275401f8f0748c75c8769fdf339f935dd174e10379616f0800 2f7a1a5063f24aa3936cf4fa0717290eeed6b668e8516e5cc593161e01d385f2 6fbf3b7533c41d7511f6fe5c409f25eecaf448276d24add582c932f59d55ee76 d7e9749dc546ad2d038b9f8637afdc108dd1ec24dd36dd4605d744810b54044a a6a13f05a35c560244c3d5eae7fca4c34979d96b482181dbb1f99d8a93ce8c8e 2f397d6848ade52e536208334dbaf3ed9237c19e128d9847ca5577b756f37028 3183041d03ca8dd45845b9bac72fff9546b6bfdc0bc2ca2c47db109e98fdb616 aba43f1f632e03886e0cb1b1788646aafc293147fd6b72564f2c36d240c5da19 3e078fda357c3d5387e0c956f2b56dae2d20b7365bc48e9e9b4ac36f04d7a728 04d6aa6ad59ac21fc387e9ab114ab520f308890d06bcca292ec65922497bf611 0c6338a791d33674bc070c810e6d60d62b0f2f6dc19032aac93fdcd11c1ea176 fc9946806f81d29b4081a8825116334478fee0adb963d390595911a900fed40f 95546fa70bf98438c412e7ef4f1e14cc53d0f95614bcfc236a967c52d3fd134b 6ec3b4c2ab0676a40a8130f0588cb0a3f4167086881ca524f2c0bcd79c09a70c 73c214f9d3e864989cd3fd21a41d80953ebeb7a8b227217a2575aca9e6b34814 db003cd4e8684120c452e867a3adf35965d11a984d659a3b3027a19afe0e4a08 243d46a59acbf3dc71af0d12c1d27cc6328ca1c86b6a07cf12074bfe4da721ef 5c4c7de92fec661e3e128e3b04b0fbf632f92f872215581684371433179233ee 7850bd9666c7ecbe9647d5230a6cb0da75c769c90d569b00605dc5d845c88441 3a4130999e45b4181134cb38c42b667b1ebb88fcaf3c7e99857b0cfd099fa7bb f38b320c37f566f4bd5b50b88af41f380976b27df932649a99ee1808d4fae714 2c573473ea50b192c7b95f519f7eaea5fcb5653373df47dc4ccd4b310422f59f 9aad3043e49d176ae29c3305080fad775f63a20500167afb5ecdaa96392891c5 38d51582cb56870e75781fa15ee2dc5b6a7c808d283b48776bfccf80a95c20ed ab83f29b81b08d471a4f68e0fd8d706ca601aeb9aae1ca784e392d06ddc31959 5585d7144444d14a9d4d5f6a76dc36bbff11d17203eb8dbf9be3ab011371501f 96ea666e2d0a040b68ce975059f01ca8d16d9a2d6e64c047cf825b5267fe8a2f 6a80dd8b7e58ac08cbd7d42dd128a1acb46cb6ced21da7797f22f14efd9f0012 45beeceb358e445ea29aad6dd30186b961518eb26780a9efa9b69bd4dd307a07 97dfc4c677a8a4bc1d1a57ec7ece9aa48505a59554f6a6a0c4884d7cc9803e9d 4ab4fea7506699f7d117ea4c4aefe47c18056036351104b0fe46bf4a0f5825e2 008be91dedec4d80157f57e0509776bee483e0c6a10dd5f4c985f31451210b10 bc08d0f1093bd0d7bc7698cbc8184fdb18868c733426c9a2c7bd9c1ade9d20df fc74319a2bb22682717a005c92ec84f66026e5f6589daf7f285a0120393ace62 53491213ab38b18e3121c6bb29e90537b62fccbd591d141191da22da9c2e961d c10e0b85aa6ba6e3a1ddf7412ec5c6a3c784e1f5f7a453f4c39e88ece7a924c3 bd069f7f491a48ded20371fa6c32e1e7364065d3319c47d9d9504a2ec8fcefac 2cbc5164794a4547f56244dc683801d7e20e3d3d4d72faf2a4bdcbb81c54be58 dadd543459582a5cb99b0b09749adb136a31a4a46a23ae17b3a5f2ecef74fa44 943ec6506ce14c91115efda51375c36c4b30655b2d01fd4258d7617c6e5603a4 2569d163c0dfd3541b97f6f224b65a9c287de79aff0c7cfa81e27a9a386ec325 26de04b5e80fbdf21268a03d1c471396e26740e98894495da4b2aeef97104924 bee8ab02e702c1e0fc2ea53adce22c609dd2720e8bb7fa7734b566b61ab7eae9 47a88c2a1c6e744bad9bcff2d226c271238eb78fd4efb56da7d847876254eda3 8eda2b8fac540bd14e4fb9db9aecac5baf564cfb084b1fac16122eebd4140fc5 3c635d58d5bfff852e7eeb380df504792458e6fd28b98a10f4fe2715ad0c53fb df5d3b23fa9549dd2db8b7b112cf7839f7360dbb207def9096d27d3d6dc78e2f 1bf3d711405c2607b77d18de0fe2e127e98613890ff23c74d6fd6bc149de1d1f a772700b663a74f5d3b0e89e1e54afb4913f76916d47d2dcb502fd681880b3ce 799aefd0368cf7958d2316d2176a5d03d6b78c0ffcef50e2535ca7dd06dfcc09 487d98412372b11babf0d67eb82900489d45875e50b128b7fbf968db5c231d64 64b9c54edfef315301f948ea127728b08dd6e43090c42c12c9388c4c99bb382c 62b5759d7dea689c50362dbad9e8395f6556578fb6d6d2768e6d0cf965f5b4b6 97d3d1628d134058e779512c404ed48ff1299a77424130611a6a9812707f518c 3e39ab0599127db04e93f5c9e135230c41302a87b0d0534406014daf5af13f39 a6a3daad303748ce80d0b8dd786e290968bedb03858ecbae0dd1b5f7f048e06b 72248fe3c61647b49fee532ece174f874ef23e9baa5bfab0a9e60dd7f32d4965 1f5744a27dcc8307368dde18139e2447185a55182e4d7e9f59292e14510dfe13 d16ba6fb0f3824678468797241b5ba06e192a714e29b0428a71b76d0f868f300 d71ef2afabae083fb6eb4da37072111e7c3ae30181e955616c7003024b08e521 ec8fa7a2f291c2bf893762ad02ecfe849b9c0ad561370a56386d7cb1cf97f2bf 4640c0055b95d364c838b422461d88a5b0f3d8cf834e45e49ebbe98124ed50ae a10d832ce12a31a08ed4547bb23e091fb973bb0948800458912b40d72d1c83ab f996e72458862b5cb736978057219dac7423166f8e47eb07d78888fe2ed23925 5f38e106346475c37204346c0831744cd415f66f5f5c2f25d2305bd39ef97aa3 03248a535baf6e59932eea84b65e6440da97555a92ab9bbdbef25525430c7274 518154c72bf2de6b2f5b0be042c5b12d9c880a817c5f0ffc6d44971f95e16ae2 23e98e723afd17d0df0856c84882a5953c3d6a3d93fcc7af2e4c60eadb7e7b83 3960f80a262928176a818796fb9dbba8cdf19d5fc4c75297b04ff3e6682395b2 3578bb923fba37633f38119fa060fee24cd063afd386cdd6defb25b42a796709 5b4d6a35b7f81e5d4209daa4b7295cb94232fe5818e81b949f1c6443ab2011e7 c44888f2d7681feccbb4baba1f33c75ca9c5e653f920fed5cf21be7c009336bb ac93853297ebfdd80a9848755978bd41c9864ac00c32e36e1e0f676e78bdc765 47a15556b46b067013c0f3a39e819ec20cd2a9a0cb490ee207a7bdc9ec9b8f46 0e2e52173e513bd9ac1c5104d36debceace519b690e0c889684de661132a51f3 73cc641f5a494702c40f493886fe9df409f39494094e398cd61e6a82ba810760 5433ee480b5bde0407c696fa660f58673d3e582d9388d47eea08446cbb2642e6 e29259311eca35df45df21835361a90919a8d773a792721330f7738e407c1217 d34280be67386f2284702c5f6beb96cabf54121baa5f64d3793364e9b5ec5618 45734c0733e7b5f0246579d88d1df0a39ebdaba5f3f8511278db3a06a64b933d 28ceefdbe6353e3ece51fde3a2543f9796d9ee018e11c9199521c92f180feae6 91d619fdcaf338456d3c2ed9b8f088d17816173c3e69f47405ba034722118f4f c3e80add69e223eeabd2273a151f4b008a897544d8d3ceb7c2bfebde0df9707b 91c0c52c626e703248163988d5b31642280f6b62243755950907d8712732996f 9572e44b03bce3d1028317b975d228d3380ad391cbac1dd40837c9a1cf4e14c0 2b5b37634f0f5f3965af7b023cd578bcf94f6b67251a211a271cdde9c58b89b7 27fe7b03d5fe35fd6d7a1491c1d06444915b169ae467e8bc1a84845b351441ac b215c75b6a308688ab33289527c8c4e05c24c39b427fbe4680647e7fda06126e 587e8f5ed4f12fea6f7e7aaf6c3c46e0ed692ec7b789aefc9fdcd71ef9b6193f dcca2beed797e64b4c2d5a249c81bbccb88846c6c253e91ab2f0b7258fb2195d 2f2519d5110cdb250f51b547793349644d56a965644f3d198b275cb03fc693f6 557ffe142e69d0d08933f8c1d4e9a4233d51bd214b178fda1f2298ba2a10b031 f6ddc77e518c041e29a47fdbde2679a7c6bf1da681ce6af5644250cab986322f d76e1e815a6d01420808841fc689aa262ece08bbd9573c792d45e618c306c943 b1e5ed9419d8e5e30fa3bff162ab4af6de5ca80f909b168805f0973c7356d1d9 be842d99c24653a62cea686ccadb88397db60ea5d012259bf4593e8c144b5db4 066f3b916348b1d61af476fb0af64b7ed5551a36cacc0d65e1587bebd96400bb 310dfc4dbaab288285cb3414381f399f517533f5ef81bcac6aeb7ee90525c116 7a94e7bf3e877390135908cd416d0295a6f4bd9692117733976840a4d7af0b57 d6378f17c5e2c67226b5d710791b7841879163be5e16d777cc66a05bd2b069ca 8d80059115f74fc42b4e984874830bef91ccc9331639c1cac1b8a3c9d6174e43 49a7ce18d6498b2f62bc765632cd8f7a835512197dd3bd40aec40ab655bbb570 5e68f61cdf3ee53398d418c8d726bd0c3b3b14ca4cfba5347dee0b130c7c215c 62f9b3629229e9c332c74d1eff085d96a76a55b218761c29d6b1e07d8ff739d5 c4f98371a79e9350df42078f466d34df751fd1f7904bfeb12d4c1cf6b690874b c5511de130a9f2002bcd8a17abe72428bffeb9d7fd11ae92d0788a0fc63c6f53 b1087580d9dff8078e771c4c4208c51e7a7cd12b24da4aa438b5f36177cddc24 bc27bb4f3f103b7e9eff308b2253ee29d6495afd37c14f7c833d65db470554a2 dc6c0fd16be9b20c14cbc1bf274e46115d293912d65831b8e50c195d01dbf0f0 0dd710bf5dd15d0d84f8cdfbbc7b7da55b5018aaeeb30df9ef96fff24f68b168 2d6b23ace310eadedb0812dfd1e6453937b19eea259a1ceb60849e2b4bd1fd77 24cdb7d1dcf0b8ecbeee75b0c6f18fecc1cfd351475f05fb8395fed1752dfe66 9fac2f3d7c598b22be86d5a441973537981baf084857f3c816f91f54606009a2 c2ad16e04b1b42f6507c66f01f172aaef5f3c4d44cfccb6fcafa1e002f40da40 c25fa70652f4f773654379b123dd850598f1c4f31c5f21efe003ecf0dc63a950 d900ddbc7edf45730f1dd6f4c4423d10f8a4a8394657a956b6aa8ce49c5c1969 54899c03028b400a93520bd0d467aa3dd227c51d0b1613e545dbf5cc6357086f 0c9c9cdf536abd59104a89287580e3c5297ab0074f9380b744b30a1c4404ce22 9e52ef7c1f4f8e7782f5c5b94d7649046f83151d63585d57fc3ffb6213812374 0ecd037e3cce357dc6cbcc1ee0bf8651e17d650b12e95f0442305fc4a9f8b99c a1f44de4abec4f5a67ae1ed295693ad4bafb3540e550d10b9750630646a7781c 2c48cd75d055d6e1edfcff69eefacf4e2ddfa16383bfcff4b8c8895c2d36ff35 e3e93fcb480268a0bdbd548771bf8134baf9314d07150c7e075d990bc32254ce 8fb4893cb99aea564e9a8a368ff43e12cbc3cdb4b909b2bc0123bbf93c796e46 44e66661660ba147f36343fe0fc5859bc54d8f5d3ae6deeb96fa2f16dc0ad8e3 8d8feb01fd142b447900fdb1da00c66dd2aef8ec62467e99b9add14dfcad3b58 846e4f70d21dd4c46a355287b4e9ec4227ed29b9a8cf29da9a5a586ba7d5a9fa 4d16aa46e292b26aac90c69e43467f381c2c8661b9b55cbf7ceb6f94a50c01c3 7395ce75d20dae42d9ab731c2323a7790c4142c80ea3a0b53d92ff2304657548 e9cc8b590e5de218554b7a51ddc4538ba471c66bbef2b93f6e556fba594b5096 e67078b4a165025b53e02cbd3d68e0a4ab6fe3012e01aebf58488aeb58ea632e a2729ad7276813b350bd5baeb36810609f52a07f1972bd06daae68793ba510fc 82c8da6ca3ca9bcce7589ac06f191a95009cb9cbf29a12492b4dfe786554ae65 d9cdbc1e1cae1779504a733aa38973cf4cdeb5d9cbf0004dd0fed9442bbde57c a84044c98d38f1554a7ae3b5ba2fdd1d3e41b9d76bbf0cbb95b97314fbd5c669 9a97e67fc3140320d0831a4700c72a2fcfabcd54abce0dee5c8da762095c2a5c 88c8d611168403f6591189388fa6323b8c05655db491bcc7b4e1d9ef07addf6d 07b54fed44b175e6af92f3a8b9a8a382a36eab6ba6f8027224d3e82e348994db bf5323eca0ea40d4939a0001187b84252e04eba8feeca82e655ba87f5eb2d25f a997baf803bee8b17879c06387caa1fdc9b0f09c4825a4272408d86df3f75c11 56ce1e3c41f60bd6d2d4e464fc6e82e2c198dea6393b9a1d564a6495a853b6ab d31c6f0a2525a3efcbf622520446e2c3199eb2dfdeae5f45fa34328d15c87eec 9581da17cf6868f792d3571e4a9774ab4a0315c92a91c75f81c418cb7e7c4591 a4f7f97eb5cd7e3088f4d1c99603aeec4cd41a6ac1b6345a35981e1952ea7b66 034c16babaf70584ddde26c029ea0ba52c7067485e759deaaa6c9e414b7c80aa 1e0760cceb07ec9f527c3e700422b7c645986b3b35d7fff2be07235e3875006a 44952a0e6c62206eb3eb4d04702354ac349440154ab370c7bfe814b2107d1a74 dd4546a40bc164ffae66c0a9b4fb9b366b109921e1f8c145915ea478784a10db 5bfcda6cfc5b169d554944971d8758359291f83b06b1d16045521faa1e3a427f 9bf12c75c637504231b6046a68045510b1829c053d43121343f2c42410d7d948 427ff679d2d98db8e7addf86b8a117e658cced4e4574f8d3faa9cc2826b032da f380f184571dc0d4a59bf74d1818954a1a4eff9ca494230a5517d433be11fa0d 1dd325f3e0f83191614f063d0c744fd73b69e94ded2ae3d39c42f5a4a980131e 4b6126604ba1bd39536ff3e2b2068a151d8543cd37b443b9a223a54c97b2576f 576d7570d2b23ac29c02133d950a393f5081daa40833996a39b8240f2fb9a930 e7e46cc95088e8ae014c26937bcafc453acce5e66df991fdd1065e57aec5b22f d073701f4bff135cc554725a92978544bacb2b8d2c9a7f0440bad8771642fb71 c591c1cb6254c0bd33bd27e56ea5eb1a1abc4804c35671273824f98b76303544 5d1f877635ad152778292f7cbe982f75aca04dec01d377d553130deb2b120d61 e275f2546ec763f39d565a6412b17f0c1fd7529da3ced89e1ca906155423f588 36a8062291b5a79a81758fa86378485c34414eb1e5bf7968b8ae763ca4ce57cc 59845d9e94a32ff47f6041484e1344271173529372af6cf29cc521a582968532 b4427c9deb2e7befab35a4cd6b639cf3ff7c263eeefe2d917cdb0add125f3bcb 32e7207c5f9052a032be8bb3266a0120f84973a847723b6156e5092f3134b718 abc787d9f229bf98416f4e8a45a51f67648f44ac4d3c4da1832d7e532dd15506 1b0aa4a71b74c167a0e56fe956f619f2c2bb7e5072adf11fb8b9fce68e1b686d 625c5fcedf427a0be2329763b8c8193ca864d000594087ed9eb76abfb5664173 12a7ee447eb37a8bbd7ecf0479d8d531c708a3f057a051525ab403783f3f8577 2fddf94e7ef2b0e331de09b98d3cdc857bffcc63bf81c839834aa7950dea66a9 bf2bf5841ce9e2cd6aba43c997d077a4472a1b4582470659b5f86a17187e2b3a f692477cd4739b5b10a07323b1b05ed7f3ebd1b44fff6de4bf0e83bf7c0c12f9 43c900d58d0ff9d5af937e985e7afc8b0b69b084e04e02d80cdf930692b1d5ec d3c2238ee285cbb04e1be9895a65501e4933be041b1259c13813aec6f95b1351 635b15527eafabca84e0bfaba2ba71cdd2c54f26f5c9feb5e59ab8773e575a74 386debbd9875ad6ab25fed518f6b6140f3032eec83097f67505c722ffd40f5a8 7a0fb32533cf4a100b0c09c2c6a6e8f9de3500842da0e9cb4dc55909186d08b9 5fbcbd978fcf5a40c9286d069d2230233db653bb8cdeaf5d1c76779e7a3ecc07 95f59e8df4b9dbadd47852b04fad5147c66b7dc49a4ee76448d1cd1f496fb6c5 958933c5c4b7de94252ef76b728837fb2fba4915d4a4cc07c4ec6c5599129bc5 73f535755ffa6b31bcede895ca0c0cd75ca74b1ae4dcfdeb5989bbf6fc0afd11 63d014fdb1f60993b8d52a6546d69774f5c9e93d611b0ee9e36fc498c9dc37da ab66cb4c00391b9ad5788bdaac9ddfddfcc6dd156803c99688c836ca9d132f60 3edb37049db4a3a859b9bb5fe7b325567b35ab3b8b67394836e78b3f2dbb24f0 3f06553acde1ebd61c67f975c26158adc81ada5fbf5522a5e81b78c1dd18c835 6b7ec2f082cb3d07b3aa7fb76848b5534461e5c023c137c45ccd793644148e7a f88fd3ac51ffc0210c1305a1ffaf3d6e7aeea98e0aad58cd18f6523cf7b635ae 6382b685bfd7fb63b2c567cc96ac6521e35b3f60391d13c26998b54154e3cda3 d88b03e8b20859fe97d0a95cb25e26a94ba7c6bc40aa5aefe25e629414ecb8a4 1a5cbd9cf3c54cef2594c2593dde352426aa262ae7ca65b45c0ba636ffc52068 ee1516ef39af09784ed9c3acac1f7c1c713ba93aea88ae633202d18be4faf8d0 e00306ad981079c5b1fc968bef67158ba7f3e2db20c2d8530c626fa27552316e 16c3c658a0081f3791bc73fe9225a19e621f96978d267ad3ecc53cc85be3f44b 330c2bf7de5ec45b64a1f7764c3aba6569dac5685ca8a696200a4f29722378b5 f5d8984b2b1cf40013a32775f148eea9bf9d7641bd3932ab3a9ce81a03ead922 a392011b083e1fefd01103d28a0a91d830e9ca83babe929bd6a533b248926352 b59804c7ce885549b45e36b514b8949398df6a42ff090d29e3a4ce8df04768cc 0cb623e4e0c20e43ebd11b297cfce0338aba4963fcabb193cf2ea42ef780d1d9 fac8ed3ef4d4b1eab77d77ce64bf35afff9e56fd888185eab1f83cbae67e304a 8f85c25e262ae3eb6d5fbad96b899ab128f97a8ed05fb60a1f5f2486e85d5b75 7c656f65c4fc2737662acf05c7b11694b694508aa46ceba974899fc297be6f0d b23bbd851e917f2c2abaa0c0d234975145bd5f9217fb0ed537aaf3fbddb6ef52 c72153e3bf96e9a3b39b39e29630e9822c48dff10600288df650516f54c844f4 5d8cc777f74fb51d5aa173f720057563bb6c20a88199c4e85296f2eba1ff56fd 6eb98599ba5bdc94105659591f60cd1ff431e4ec4f246b3e3a460eee6c832239 127f14e2bb020e97f450aa421297a0a34de17199b918caa8cef6e61bc784ac32 dbf454e3c5847d24f232c6d89b72796035fd9df19460bcb84192d3ad55c84e11 d7ed4a8b269dc78b8c44ca5a92c36ae66f30a2bdabdc16bab66e4f0859c7af0e 460e5ab6ac871c16ba69386f08e5508e071afdf228951e056921681afe45d9e4 9b4fa4577bff9ee69ac86897065446f7653445c00eb488a006788f2b5833e663 a6333772cb7e4dee319f0305199f5eb901c8bbfb702111f4b0e7169fdb84197c b1edfd75caeaa480f411aa4758421960a357858935648988120b20911d9afef9 553ca57eb9bd63f7a6540f9543cc749ac8595431b530b494fccdfff0c507be6d 04f6c60f84005d82e48079ca3aa1abf75c856ba3518ec7667e07ba5a7612a9a2 cc2aec3b079c51cde64bf2902e22631036d7c3ad05dc5ed1b38ea7329de27556 3dab6df7c45e08b4f39a3242f46ddb4b0e4943ab7caa99c18d825295d0906ad3 bbe22dbe725f0fe01ebaf6d3067da698275a49782fc43973b38d3f4b1bcfdaa1 faaaa32fb1adeb641e165449bc3eacece121ec0af0074e5e5a137b1566d34630 0c34d077e4eeed50c04921ddab1651b1d86f6fb632f7a8a461d440a36c9465e4 b1f38e4e2b8e6b7a7a427187e72b273b7bf7e9228fc41ebc43413b0340f8c85d 2a1f2c28ea4c3f2ad371f10ba0fad8dfc90cb51098ea4db9a9cb9c7119254455 22f6a4d85db65b96a6152f91a4ec362248d616b447b0d88b5337a11f1678d074 d6673db2f948fd32f460ebe5de509c2af95162c9596aab6c697ad5845d58dedc 3aa5e19779139f23f5037d6eb2a0aedda3bac217ab4b169292aab49fb5ca6ed5 ffda2685c42b4bd48c1123aac17d400a1889dd54c55ed8504189b97f92ec6dd3 9aab6aea261372b90e72142c61d87c901c9aeabbc4bc86585724fead407d3551 468904ea51359ac09933734002d509c5b11c20bb49f837fc83c9deadb65c77e9 66173f37521fa97140c314033968f9962dc1a2649775f5801debadc2edbd114b d0fda3cc3a8318a931d495deda7eb00a5c8536801e21b2ccb96a152e2a1421f6 b4a0d8bc4a44e02cb1d8b00cef3b694748bc7982beced5c3866925652b9ec720 72fda487a32f4f8ba54cdaeca8d56e9a362f1fafcf4e497ea7de0e37a1985054 494397927b8ef19e0bfcf371a8ab4e6a003d7714d825149f5238d73ab1d38be3 1b9c1afc639486e6ae747ad18a914f2db599fea7bfe9ca3962feb1361ef32bb2 5d7de3b40a241db3fc84ffdc4d302e1d070b51e720e33ca846c5d4e271265eb3 8a4703d45eb622443f0c099145f351f5628c633b9351fa2c68371a2408217752 ebfbdb0f04b473097b6d97ff23fb4604002df92a82978242213b53fd0e3ce380 7c7c8218a5b93e7d603d19d85d3fdc0abb95e0c51f53025175fab73d74265bfe dc39af9272da7820b46cf5123e59ae8bb7818f68e465243c333f3700a128e7bd dffc29734907ef555b19161ffd01a90591be62934c48376b91fa679bbad39b11 ab43f04a2bee307b9013f21132660bc035efbbf98aed60810ac033c8995658fa 37c7661695b0b543af2a86a99f4d557a0160730c7e14316d2dc0de91c18a47ef 4faaf49a1e755b5dfa0714fcad91036672a632bf4c142a58a08a5d15d133452d 3f741ce51cfdb50447bae053f11cae5388a6b0ed7f6d53f76f3054697de268ba 0a6708a123382dbdf4bc1d48c6e88a323e8c609a47a5e735725f6af4768107c7 4ab503e553366be92748ec494674726d5071e383503349b6030b45c2b00d10c2 03ded050a4914b9ad49a10a4c6f8a17399191d6750f4da4120550df0f5a414db 479bbd2837b67db619c52354be98472ce4d7fefdb7b698030672c53966b9b06d b031e70612b985a25585bfa19378ca53acdc6560dddd7524269b29ee00d53c55 412d8cb5137597f9697b34f91c6bdeb309b4e5ecb4830b1e66a18cbc4957dc24 318c682cf45773d2796ad932ca3c497c1c202c03647abaa8d7effc4c7157d9be b19e8963ccd5d339264e33febaf4fd074a29594c85e2aa783e2c01002464b9ed 9aef4d62de8dabaf21c05ecb4378b0da30fb70c7fc6b479ea6fd232701c9c4cd 6643a134811feab5ded91030783f8cbc195c1a4672915a858719931623799614 4a431ecb4ca5ae503b77c780a570318ff2f50d4e3064016e8554b9b4654e7b23 c44e63e903c88d9b47e9f21d2c7ab06e5c2cce3b4036b7830e1de29635b81a95 0e6678ecca710f874ae70c7f4a44621cbab0df709718c6e270dcfd8663096917 04b8de3bab1142516badad8cf8df891e5102f913066b7dc1d8a7ff76ce922c95 8b12bb304747c7c2132fb7a2f7e97795a15160d548b675fd5b8fba5fe666317c b61ec2af0c4653ff4ddd3480a3b4641e3a087a0ebc965557ab33d2ee36bf55e7 1bb04e9987a5acb22d26b92cede81c0a43391aaf1f373425a53658e3d5c88fed fc4c72653d9a6265432322a2b58ab04f68756e0bfdf07a5db3c6ab6d56b68780 0b4bcfa0b9c7b2590aaa4fe6196d94e090de7ddfee23cd7a222b7b661176bfe6 5e2d4c751c9865ba9cd05ec66284857517a5f51e22f6abfc9bb8fd8bcddd986f ef551d64eedb3ed910bb9c78125167448e95c9b282db76aa3220f0670f6068a0 0542a37003d77b4516e250e0e8082ff9558eee5ea6e2faaa04f962d5063d0763 699d4cd910756e1d0a1f4f7b046bae0bb5a48853d52e63778cfff1d5b63e099d 484089ad2e623da41a45ab8916070fc4bf2db769c2a0100f9884da1ac729b0e3 bf74bae385fb7624f5e48cf6c2a204dbf8663cfaf490b6c371a5cd515b8bce0d 65c72d7d87de6fca80d64feb26263f9cb022bfe60e41c99eeb3053e28570f890 b9b2d8d0bf0823db1bacfa2e07d91ce17cee30aed458ac1645eec33e4c8670e4 111d9aad428bf4e1e69b2f8855f33b3d9eb693a37a84349a1f2f405111417ec1 bcf495677f19c419920a05bddbca5e9945c1442bffa8d6f75aed898ef9635e48 e6306cb8ef10cd88d937a2e1ec18663a32f7b5ad2c7fee10bc833320751d094d 9c0a610aa270c031945e5f850231885db82f81ab20322adec8f778cafd929ade c0caa31bb1df69544cdbeef76f38bc76823f5b4a05331452331ca301ee8bfbd6 07c4460f74b4d9954203c2875a4e24504a1fd64d2f6d604c68e8d5df0c5bff35 f8a353cfb7de024b19eede9161cc8bb71ec4fb78d26d4e0d62b714e788754c92 b1733526859f1be5ac087a9d6fbcb073ff6966bd9320d4d1ecd8b71bc088a306 479c1149db8808b8fff65b737bb9ddf261f71b606bbaab48df805e74184c1014 f052e527c49a2a3c2009937065736cf4ed0555ec3589d0c3dbcc95e8cf795762 7f67bd9ef4c58ffbb81a345c1348d8102c60fe1db1eb5b7c8e31e70ff75fc4e3 49c22be8188a14c9bd42fe00fbb425ac5024e260f3be35acafc9b6342b4a05d8 052e32ce6f7a9d1ded14bcaadf0ad5e0e96bc0ff6be13588679898055399653a a4df60dda75a13432d7b895f8888eaf469b9df77af30cf7d71980c9059c2144a 0d4e6eeb1b8b2ae6f2295dcaa0079d1d1d32f4a3e3160c8471f23bf13a96f3d9 f1762cc2667fe9586a18ddbf8ae42d42f48fac7423af92e15dae9e5110d4923d f576c7c68904f91b54a06d57ca5183ec36de21ac8d5e90eeaa47d561b10950a2 493ab80c74c5a3a12d76b400a3a09ea2c86dfd1baf7419760020b6f1a044c36d 77de52f09d26252bfc2058a3f4d69d9f0596bd76b3d8e7962fa171248d2cf5ca 3ed7f5fffc0f2e068e2988648a873d82118a639caed42389662147d4b652a8f1 a7e0b0909beaf7c7f6703d851e84dfa36dccf7aa910ae40961c9365cfaca4dc1 f8f3470a19b5dff9c763bb0853229ea3821837e9602ed96cfa8fe94d72af447d 93c0eb4788893c21dee174adbc75b96e5626ac5e5680d3b6cde1e8f1a4aaeb28 a369b46267e18d4d378a129014e899b59450634c049e114667ab5201fefb18b9 8e4ba304618d1e75416b45e45bfb23fd8497d3bb8e9f045652dca55cca4d914a 714596f6d425180daf5c5870de824891390f15361c43711c1035cc1187d7df0d ccfbda67f4caf9a7df6a550c5c731708903c8c943122d8b1c65a674a5c89dc5c 661c3851569db886f0e25e104fdeddcdb8be714abc1530371ffa1dc6c1a91af9 0fb701be3f9b298ba679987129a94646036e562ade5aa1b8326f1813feb4f5b1 ee7dd98ea2578f8a0b1b892fd68fd7076b14ae1b29502623158bc45214ae6502 2c84e2fd67c1eff5f8cfc253371c3dd13b562ca8bc26d38be6990a3c329496b0 7449302d77883e1807bd19495df4f08f067c6862bda21d0f909c5a8482bc3db6 00c219609af6688846315e4ee35e4e50cc766341d7bdbf1e7c8f408394507dce 5776091c5b55cb4c6f62668e6622a1213cf45193851fbea0238418ed0fda4f2f 0647d4c9f1ca6a2e8a133d1747a50f464a0e7416a6bd1dfb29eae25f7ff08700 bce4fcb2ecd7c15680e0130c265710faf158e9028d171e907d46409f6ece77fa 94318ac24fd12b2685f4e162567ed451d1d54d7c44cb32733792030f55a26ec9 bb5177efef20b5ea513b2e7d4c8766eb4f2b75d26014e44c43bd37e5473faabc 686983e4fd6daa251992daf5dacee6f05c249cfcaf6e4648db1347f42bac7675 b1e47da665dbdb0d646c20ec5ccd50391c808cc21ec24838b0905365c9b9544d 848f870fe7c734d80223ecd7152b0d62e50ea638311a6500ac56117ddcb2a58b 4fa9a05dd707aae3131b9b6aa080fffc2d92581f94f9ba0280d8604644f93adf d17a7a355c00a30f16216479a8b9befb482d2195f3073108b9349b23e6abb132 f5b5769f3ff565c7069cd08287d863cd3d01cf1610378c25aaefc2d346b565b9 52a3674d2c2086557a0dabf634dca3e8ef1492d713d348403f7bef9005fbd4db 3d234b3886beec3dae37c73897a3af4f56fa2c61cd3c6fb2376ea2b16587b3d2 58e279576e4020883290cc54328361679c2de7eb1f6a3196c7df4d2be123425d 1dbd21fd586ef1825aee2c5a8037c57f3f42fbf40ddbe0e7d45c21a692313546 bff8f4c77c6c64c2685a68404d8b8604bf02c316feec6ae48dd4d38013579d36 dbf220e1679e21fd7a4474a8acc0017ab3a4dd40e0a0a26f904e3fab24957c8d ff1ab7b1cd9c94a24eb0344e4605fd0640db65cb5e9219e1217619dadd5a529d d723dfe25423d34da1db539ff10141dadbae30b290af80b170208d4c6f7e85bd 438731d0da714550c86f9a66238558348c41f25c80949a5a9aca86ee7c2c0448 6476c751059670464fc59801746bd60ca45a7dc47a257e7aa722b5b979e33d4a c8e85a13f2e77b319dff51a150a6b31041894b236b0cdbb736ac0039e3516634 d7d316f01db50191468c72b0d51d7cf5518d0922772ac9e28ddfffc84cfe6660 b65515ed8903a4567ff54791643141247f3f927e8bbff193211e48d1b82fe621 ae48486a66c74062950af53c8e10a2775e20652d9d4da447b1de56e1aa1e436e 64fb0bbbb0741ac2e477f3cef06d42292936ab334d1362120deef3515f92da8f 6ca75856ec2fd74e3f327bd791212845a9c27e4cfe3c76c97f952dc8ac8fa543 c564e5feb237b5dada51c5316f23e50c94cb1948aaedaa4043f56eb21d683a4e 5e2e259d0926211edacf72ba715467566ef873faf4a4ace6a6ad214186f17f24 300af6b5ca007b2fa0cf4af0efda048feab029d5f6c074278b11b829e634be71 ecdd9c93d23cc0ab5995e1db629d8cea52291f29961e4f3e420bd915ab7188ff bce3adb178a055caa667358a3c1f3e9633f56fb6dae2dfdcf16b13b03a285edd 56516373650397846ec97e4fcef493e6747efb52bb0a1d57e2935f7e6c91b60f b49864a6ad194f4deccca781a58e8d02295292107ccaf52ff877806442197a00 b785df52ca811c265ca88dd2c0b5fdbc026aeac6f0efac9492933e1e6f7e6313 11521458871b0c8bd74a9b2430c75de0fa6f4a72c396dcb08b4f9b2a4ae79405 96202e9ffd4f0c62762fd19ba0526d38d3656d8b2ce116d79199f18e89c8e17d 720333e6f4c21b48d3c8808ff9311bb2c24435f88f91bf13707371c1d2ff0682 8b2e0838d6a0406f1fbdf5f2f26f3541f1d517099dc63442d9abda0b9690c4e3 d619f7e14aafac9ba93c9b8c6e41cb485512582c05b3fdc528d1ff75c8bc9eaa 5d30041f802ceef28a858fff38b6edd0ee20738d9c5caa4bb8fbe37ea64b9391 ecf0fdbea693a0aa7ba7d672d4c1ab8827da50a962523cb808e047bf88aa991a 0342499b5773da94e329e4c4679066a508d95421bb1229c88a89cea38e76e437 1c045b266ff717ba9fe57be4192208a4c3198ed676f5dbcfe4d29092f07618d2 a89a74ff5e642986325ec053d3409a21ec59eccfbe9b816cce4299ce50dfba33 e77c46d286ce4cd9c5075cda5a183f45fc1fb15b0ad48d78f50dd9a49b812300 3745450b544c60cfea88234888b9b7859f9d7aa69a5ce5576836a4b9cb6f342e 1dd3a08cb7773da9bc55d79f4e561591191c38675f11dcbe200eef4e11b78d50 9ce8f678ed9acbd91408475cea9bfecf62cc5e050ae67acedcc81d7540e6914b 16e513a24a3e300066b22d0d4b1c7fdfd2b0cbf0378e12c47026604a3be4088e fd5cd6c7d98d69567b67d0857770100114f36997cdae9cee2ced4d3b908210de 7110737450d7efa27b19e916a7b2d64ade8f14faf772168df2a43a61efe7599c 4eb7ee0f09659451f83daa632e68cf2a8dd784c4139be585a22a98f4ef874806 cfa512c331660049a8df0d9bbc21836be2e9b84fbc6d05a7926d1a05fdb24364 c53dc148bfec95a9abbda39792407ea9528362fd70a0a2943cb5cd37d3f94d4b 7e09f8b7862dc40e95dcc8cb81c3cd8aa3ad612bbf800a23a43adf5896fd8a90 1250a4e2c5de230221874330b0dfc3a0d660b1402ee393f121190db0cd1a2077 8c9b95513e4632c3b05df34127f9de68bbef1552a08565f2df5c41dd996cd150 c848f208148f4777479529a85a2b6b4895d7eb3f6787bcc15fac4408ca3d9b91 99e3eeb26e6b50b3b2d2a3af123fbe07825ad3473eff3ff7b1feeec9e3147955 77ad0820bb4dc86e8a76e591e8d9fd1b3b1b47ce4d335e7d342124b5016879aa 508215c9b660518c4ebc1dfa3f18fca7b9adcdf1d79d55fbb989af977ff00685 32dea55a4fddc0c182fc059ee8af8b78eeea35103c1bf8ef14185e8c10a3995f 94a4d8155eec6888d07d436a9e3b721fe5e7d83c52de9d48243ce2768b06545f e265d74bb9af45696d3c452a59118b9637581ad70805861c2ccf1f5ff86eccc1 b14107ad6281277ad8fb61e2f17edc05aa690ea00c32528f1e6e9cea4a89dbdd 8868f4894987e574214fc356f2d24b7129ca5537abc8c2b536d1ee499e2bb801 f0c56faa7d4f775985aff51f9c705d9170d21fc23efbd74bb048ba0c08e65c04 ee0553402d15db5b11edfb9771b4e40e881098c79afee3c6b5d1ff53dcb43f1d 8b8153bffc480ba302a5bc30c812fba64d4fdb94a775cfc11d750b8b9e9aec20 567ade427c2ee6c4dc4719d3d03770410d5b61a4fc4ac93706c719c8dcc7a6c7 5b000d0307bd348f51d67d07a0ee7a2a0e5005d5fe3ad652bfd8de6be1ccc33f 968f3f6d88dcd6f46fa85b0bb7702e5cf75dd424727768f72d99cab6d670fd3b cb54de71a9ea8d54b49b76779f47da59cc296b425ecf8a1838900ff9c32abd2b c7d35bd9b3bc1a6f4e815425621776ba5c16492598828bf88ef4ff6c03e2d7ad a4ad4cacbe15924e0ebaa2bc101c1c19a159490b5a904e8c1d948ade2ccae05a b25e28d1a0c3d37cab4db8af511cc66316a1d6471a1f3986ced6dd4fdaf64193 9a603d5711821582f9239293f71bc5f73443672f32f0e714f2bbcccc57821a0d d0da7ef0030968001abd0d89fb8b3bb476b393faad0d45e604cc9555ba960905 906dd2a13b82548d7da8ac41c5d972c7bf201173c2fb9f02c50bc149e415f460 b606140da19b276efe8a073b8e02675b888c2277172bc4b681eeb6398349638f 34250ef32d43e43ee94a657288c5045266543316ddf5366c5f99107e1c2bb25f 61e98080f2576b0bc7b94ae82dc9c0312925d703d9a657451e362b4aab7c6015 123358de0bb74a7c750bf82dbd9cc3d93854dd44c823e764bb658afb7e7337b9 0eb964b7f0f043be81791419eaa11228810fdb1fbd8b952f4d445818946efacb 79f6e376e06336e65460b44b46fa559a08d8eb9895d8ce425cab57b96b7b063f a2d0b5009ff269723e211e912fb50856141e012ed8d484610c216fd21208a87f 24cd7db55503556166d810114dfe326e26fb37bd789ba66fc7d194ec2dc34863 011d7f0948e70d7ad083b7fcf38cb3a19e8e3690b16db1cd3051db92da40355e 528b267f1cc2a5494c6e40590ad87e3cc085a1ec6befb2e00b1a909a241f6ddd 2f44660026c902c5ebacfcc5e88e4e3995bea4f18e5370aa7a615b154bbe3893 4a06b0b84b4844a54e13401a94fb831e2d7dd3ee57d250939a3c2c853f061b91 df0aa7375541eb5ced37556c9bfb417e07ebbb793a04697c055554862043e2e8 6619f0cf8f1662a302b7fec9747a938c69922cea1a7cb3843f5ea42d0a99a7d4 28ab21612f829682dabd5da82b45f830237784d4aed9a9216a14df863d291632 e1d7647a47ae29ff71e4b19ef41623e5e9b2203a74ca8aa2869bd105d11aee9e 3a4c858bcc07953f264ccede8465d859855a092c6dc59f3655b2c58998112ada 66ddb373a97abbfa7f87ec8d1bb7cea1ce98be5801ba9bddbe7fde75e3ae8654 12f78e032318140df704835b2991aa8916c60d72417c49c230da809efff2feb9 7e607cf2907108d829b61c0ee7c34c441fbe5b71ab1e6392cb5d117ab3cb78e5 53e2aabb9be130f123ee6d47ec3eba16aec321562bbef9152be9d53d4ee92a17 e355d5ceb828a26337b7c248b54508a7da5b2606d95299679517b7ffa1b57954 b1364ffbc50837112b8b6f97415779d8856331a97b2e424ef5c9be10f765aa21 fc107139ff93825e8a398c521ba5cbba3be900973862e520943a9d189edb863e 1fa6548ddd54b5bd1851142eac653b9331435db0300ed34d2d1cac68f73f9216 1c1d287769faf5339d0779dbed1782a67796dc6204d7dd5be756dc809ff5ff86 2865ed02fc1722730bd49659e71a1251d1aefcb903b0cf2b4ac2455d119e5cb5 9df7afca6049021ecf0db9ff7b8abcc267b217455a7f011b92b9a247dd7c5b0d 5ebccb22c620faa0b5e6ab4160a00c7b8fdad846d503f874257628a319b54d16 44aa00bf5a354d57b9862a2486372e054bea634698e2adac53676c8115592697 75325ea9813a27a45b0813b879bc00213752c2d87b0a01257105adb3e64e0881 f2f03f8dde33f1fbcb420bd3e3fbe02896d847c1ae53ad2ef67de52163461db7 a30cb056e02de8cff68f39e9d1c8cc7c6e09b59a54dd5880eaa5f8b3a99dfd53 9111a5d6f4714ce8d86c77a46953ed203bb02b7d9bbeb25af1a0cd7d9a7cb853 4483fcc871ff84afb9211685c6af7732f9ff24bbc031ab686ba4f11bd599bb06 a6dfb2d6ec2256fa44f2e34d79b351087fac5efd9a050f8aa1b838c8ed8df89f 6e4878e94d7674632802e21cda3cf9ae30c5626751d0ae7714c2c237d5b55b3a baa29cd0504ffaa1d082967fedc72bf5ab897dc4bd5ac7a06ef5fae7cb115719 9641aa758ce48c1b9c94e935c2f5708c6bca8822d593309505553bb55af5c384 beefeb25f11883ca8d73c8d7b1debfbca550f47db195e07a76a3f2c8e7ec060d 4fc410bda47eea85c4bdda99541c831912174377d92a1fcbb63d157fc9ea86a3 e05c89fee9cba13bba34dfbbefd3f958d41585c6a6c346a0faec891b05e61ad5 1ece428173a78a37c94e281af691526d886f8b3a03bf59e197a65c685de5429f d8437670798a8bebcbd2b813d4d0c212f21c1aaeb6f442d7253535c6bc458142 56b6df1c129e94af20a8ca931f62a9a151ce30786a7e47ef2178af4c0ec799c5 902f9b2fa74e3233da8e5d3d1db86bf1aad0993851fd47b58cbdb718875eb7d7 8aba0b4059d0626a2f05a92aea489836029e79467d18e11547ae982fdf93bc8e a7cf8cd469715ea7127235f094e1ef4efe6e5727be359444a4e05e1f1f119751 f7ccc6e1bb40b866f4c7f28a4f41712253bb1b43a815f87408caafb52e454c48 66603f69048cdf6781407ecb828fa9a6f5ec1de7499645c565c998dbd705af95 f22e975ad1f82f46efd87329aea05bbfff5b46e08a8f3695f60964313bd1f709 a05a5758aa31489acc139bae691ce0637a3524a495bd6c100c375855fa1c2ac4 b1afda6b64ecf9c2d763d9cf21fd00569722056ec992a02eae51ab872925bb04 d7050e886d1e59c059fc55e984500ba2ab0634150be8b9539e59a7a1e75dac38 09f08965e88d8ac61f8d7e0b57ea410e0df10d073cb439298ca17938cc004064 525de8e368d9952a9503b2df8fa881c0d7a431f9fdd3fc76d863269ecc4b65b9 678a48ea0d22578b84a9d10c1fd63223c41697ee48163013c9a5cef4e957fe42 21395769e4d4bd6397ecfe17c8002bb0c50d1fc95daa8c298dfa7cb4be82acc5 6deff1af231d9e583cc8b2bb84f37864aaae63c386eeded813eb5ec8e9c262e0 fb4e312c030139ce1fadf54bf353cdfa29c70c894cf75c48d99897deb4ac41b3 c360acbd433e5c8dc9d935554de910872a9886d7dcfc40cf8710bca13710fcd0 f8d7785c8a98244f5511c1c91fe42b3463e1ef2aaa8507f0eb7aa8af75867f80 fa65d5bee6267c790255b760ab9d4396a2518890a1e37656ffdb80ddd78923ac 3c03021f9acbb5430e58d7d40c93f92abd38d329bb2ec6c07d380cf92c9f1e17 090f83aaf8542c9804fdf684fdf5b39bfebf55f7ecc6b28cd23f179304203597 0ca9f09ba26a91c27827bd8f6cc2d568d8634701aafcc6b98397fac0c44c05b9 4cc55bfdf586edf10263a96944786f1aeb595031e1b5ee3fcb42d121dfb8aae1 280a5ce2404db4df357c206c781e9a72b543c5e1381132b2dc158a9e175728ca c8e077b3fc9840cbd57157c66fc671688eade6cb92488cda7aad7d8dab7042d3 32994343cd234301b3bdb3ec297bddd5365829b4f02b97f6c59703b5bf29dd1f 594e442a6ee72e766d2489c23d0086c55ce615aff02a81e11a1d2772479c6387 6134b2db3d702773c8583508d9003913f75770da6346efa6e3cb6689a06ffab9 2944e439aead778f08f2a6ae80b10ddf45280ce9087ce1b129a45abc37571d0f d8c6ce5bdcbafe1aaea62e8f1fc7d7529f6be0f4545ef523a41403467cedd9e6 5aaa335f8dc44f02fb549874c4f0632f46646037cb3e2103879d51541b8289ce 832ccf7cfb0103867c6226e07f3df5430ae532485eb328b20d614b42c5155174 3ec2ac3671860829406853de0c7dc5793a8893307336c138f06c7e35f10f0974 bf74b95c7e9768479ce19abbe65b427a801ee78e39503582b10386fe4c34f8ab a710802b6e17783a07b0c7d3bc65d817b4c222ffc9aed0375ad01d70e25fbd15 5001e1a4a4ae309b5d8283ef14135e21da67e318e8a17b4c86a62b38b519f990 fd444e02dbd467fa3cb53f2c785a10f0a3411dcf6caf9bc9feb77e2b6048c53f aa7f100bc227d6a258f35b17144cb79d18d598df03878d41773c5c150c0f7e8a 7a7d25fd25aaa1387fac6f32ac1958dc1873645c8d76bd8285e47a78eff5428f 5eb2ed04005e86781f76e037e9b447dca3c2f88759a8b40258c5ff3aefb99fbd a6ac45d4f0e70b2d6c976b779a238063e5bb0727ee67d2d22b9f62f077766442 7c79089b07b48066c8badd9c5a8aa33b87283dec68d38f28402b0ed042a896f6 4f0c08e88dbb07b5233ca3985c980a4a1b9ba2264516970e6ee0c6ec14143250 48c3efb5b2fd4cf933126b141eff1917148ea168c3b49d97115f05bed563bcae 243a2ebf2bef57c637ad3f307c9fc05123fbc067b05ad78a373c6facee9bce09 73912f44cbb8f07b9b36bfbd28b50a66f2f41c34d8fb083bc051699af672ccac 8eb3ccb3783c796c7d234bf80e9c8d546133653f10cbf1cc81bd490d2ed49e0b e6ed0e9726011276601be60e1ef3717b29dc6777a08af7a6537543bc2636484c 036f4a617cdddfb1e2056d54406d79a0e42604c9ddac8b820b612750b5e6502d 0fa481fe0a217a5cc904dfacbc8057d5cecc50d8d996a98636b63e2f49480fbc 2a00c16026f79322abc9a1b31184c8b657acfa8c0c678edff17058bfc3640898 fe0ce2640ad5fd786f2cbe51be8609ccb16b3d63b4ddbb44a62b6086909a03f9 fa2fd0b1cfdae0e0669605ec4014be1f11194451f2f5b8b977cb7d019e8af76f ca8345487f4b854c1dfb8a348da88e2d8b378a37ce02d3ce00e3ee1ec9091cbe b64be42bd160a30ed0628774784889dbd28b6a00b1ae7b184cea19523821004c 6a5d3200d7b3f41ed681a46611ba1c600fea01af1aec9d5355a9ef16949d0757 f1f910c5aee2ff63fde359c1096704bf934659cbaae913ebe00959dd1d396a40 16982eb3b28720b1e4a43624279a91833e0bbbffbc7ecfe56dae153070dfa724 7ad91b0d8efc16d2192cf9a588d7e8179c6a1f814a13bb16d51bb2d23d3d5616 fa984ddafda3f61b750022f764bfcccdb7d7ec604187042c121446894e2e1802 01f8d83c21fc2ebd6d51a7a7d3140d8881fde282af96d942268714d7f091209d 031441dda64ed06be5fabe5620f4df06c137417835e8f2de86a624144ec53255 2252f87ba824641a06c7ad160d32fbd9de4330edf56a7209d42f7fb32299432e 176e1a84569ec9e953138ec8f1fe17d1f887d6e33158825b3a404e2721661469 bf86b4d187fe23c40407c80e815b3bfc8f7ab8585bca4db702bbdccec5899a06 1a1fca5228a6bcbfb814b0754a3313863a57e0ba74d431c335076e30b6c51565 45b011af98d92b3dd9dcd5ae9b776de77ab4ae5e6269a771f3f6d8de0fd86469 737d1eac1bed0aedda6281f2625ca45f63668cbc77754e9da1f2ab2740dc2af8 42dbbb5a3c302e7f62c1eecb9ff3900ce198d8063ed440a36c9c495ddf66c790 c15fa67aba99d790897c3bc90dde8d4aedc9c958c5f708fa1e25e4d5ff49acef 3cb56fd2ea40b382766c1f6f1ac9b549366f19108f7a92c90bc3f99b72e2eb3d 0a3faf1ce577ee026695261f8d481823987bad1a882f2391156300ed24f1dfc1 7741ba8995ef8bc3c796ed3fce2e1c54132796714977635f7d0bc80032dfad65 bd8a3fcb7407bd8c6551098fede9df9546c8b00e6904efb4e059b15dfb5a837b 23d5e8e21279279008956b15d627a8feef548abeb3425a58c2253270dcd64981 ba86a2ac78dfe6a40841b4097760a8d7d081fad8ea5b75439642b3c906ede5e0 c9efcbe14595986379db073716ced21999eb238222c49a87cc5964cee69eba6f 8557f49178f3c38b1d76133e74dfd7d08e670014d3988c9408dcf6d491c6f6e9 d93de46ab4c83b8ac550fb01bdf3379ba75e84e0b41820c708f0a7a225bf891d bb41ce43cdf45821bdf45e0a88bedffb9d83996c254987503e388a695dac9cc1 9f383b2d08e4eadcd173865c874ef8dd86fb64e23143e1917ab3daf1ea308300 5b949045a0db875a96f1f4c57feb2541c3d22dec95cc0e2d026790c48c2d06ac cc356845b200883043eddb7ede1c367f144a84abd3bcb152e0471eb03a782f3f 37ff33814ce1aed9ad5fac26a7c94765333daa50846789fea4fee91094359cb0 414bcb286e67fb9ccf2dfd4562267d193b261f69fd970e1f4bfbdcf34a664aca 0a4a609acbb334aa1ede8a44fb97dfc0fd439cebe088c5eaac3044add9829472 f3af2472c053485952a14f31b6bb879ee2f3e607468a89c5c727c748d79811d2 5f0c78b3cfb9d8f88becdc2fb59266a15d285b3ff9460424d8bf015670c00a74 e73c85be8eba62e325c9af6a9d5ee40cd84286ddfbbd4e93e88d8ed066159ce8 dbfec98f73064f591d4ce10537e87d09cb88839e982d6dfcf9290c8d99e17c9b 59dffc24a168e59c62fdb011883c490971ea8596865f8473e7ff32fa00707ef3 15798c1f2477edff74438e141c526c6c8ed74ea072b946a6cd3b17d406a84077 64b2682e6ab0a7279618b09b69e7d871acf9441130ee7bc1e50de66e3a7fd47b 111d22699279a7f6e2c696047c066ad775edb510432986393280e8650097c602 39bda8f1dbff38f6dfe07a9412e6081077c8628f99f46215f118496e191a77d1 e6b09655e2b61629ad39edf89232b3b664a600ba2460d6a17e8ad349053a570a bb79e56e98e2265b5eb0b831782d865d651809bd53c7143cab2c5939b5e0d962 e42a7b4b4e6c7a525e94607be742dd9fe32cd309d3d973f591589136a3395a13 e7c4ce650cac5added87c2e8efd49c739d67601f6f26a3a7181c677eea31716d 39cb9fa4600e8ed9d8d5b60d7b1ffcf442b96f715cac0268b22d5df5fe3f07c2 7c2c2d184782ec28e60e3229a9c9730d09ad63a299c58e9caf266530da4f2cd7 6fa4e2f22035b90328afa3231d8cefa85a71b6066fc668d571e21009a8c05a4c 848fc42d455955ccb1b61f5a74905a9abfa8cfcf2d60fba2f6f0f9c5bbdcd4b3 589c9c2797c0a99a367fb2ba933be22ec8888bdd1831d91ed74c9e092834679d e36a6a0d67ee60a8ab21306d564c15623f63671d1ee1326a244364658ee4460e c1be3c7c50945e4cdfcd1822c80002f6cb0f5e9837bd5dfa560c3a4936801d30 960c095e99129d0b917add7bf7aff7b14d81588e4b8705ce6dc457afbad7ef10 9f76ed67de4ee7e8f0b3e588a8c849a4d9f0b178f9e351e12cda855d9de197b7 888a1c6676fa4dc706441bb6b6f3179eb436856d883cd5153430c624fc384e3e 0e026e593d75c0663fbada14d42738f7ad0cebb32e5d06a4d4c104180e64c4f8 edf59835f8d6dbd0c892fcec5a07015b37483bc2d6f0329e7fb67352c8425283 080be0f2f777405d67a7d9a9d1b611acdf18eaee5e334f98d7c2f7a684a7a6c5 c75a7478b65fa88b489664a3c03eff62fe7be199c34c81d9c6c5312b4bc7940f b5a6500b40ab7b071e674460bbaa86fdc1d7ca4fc545b61106ca7b9ff1176ad4 e9d3cb2cd1b0a908f128f3250595a99d635538e92309d6f05aaa4cf041e27be8 8567429bd438de820e9fc13a4d3d8903f591ed6d0f7655f60e61a813949f4396 1468b53caae6cf1f20c23f1571e3ced4067a39b1757ccb9530ca7b2dde34c7c7 c39119a867f3864c8eda460ab5b71145a317d75f0d93ccbdfeb25268f9a16e34 8cf2973d945b47318be0ddae926fa41caa869df627b24a39269952a1d37364df 5c4f0348168fe7cc8765991f5d85ac6640d0a365b5b3d031d13d5178157a4c8a 19e031cdcd77d94c42a88ef96a8527c3ceeaa672bf44b9e9d5dd3d4e8990162e 231578d0579ce4b6aeaaa40067eb79d1d0101d4f96fa9f54665ce0171e1ec2c6 57fac4ef208302b304a3f6599bcda1ff2138b8354151853323acd72a0fbed456 7bc621607043d78488e8020ae16dfd9aefaee27de2d8cca2b25dcd854cc0795c d7da4bca466d0e788a681a629af9629e06eab04b21872fcda50c36b991095999 deb1e94fe7bdfb255ba2b8ba51fc039012f9d01ce9cd094981911b9b78eac667 0854dddc30f12786a5e775fa5e89f7f676556c8f90c3a6c7bf4694aff1a64b56 c69a7df8afbe9ec27feaa51d2dcc5f7fd7d5717f371f703943e882b64be3cafb 1ef76235174c9604034fe4e47b542e3400e3d69efa6cec534412bd69949dce99 0ce200bfced2ca934f2cabc7453652e9643f70b43127e3601d7fe676f2d68b21 c3b90bfb8352a82a4302eecf2842d66f87445c21a72ffb7d1d4f900f3aebd6b3 7501e921d807d3af404b6fe06b5fcf500ea00ecfaef42debd4f10b4564f43815 379dcdc3238e7acc492bbd9ed0f4fb5f129f9a70fcde2d39f2854628a6f77503 195747f25c84b2f4eb32f4e4b023ba6cdc713cfa064a6f2706fb08ad84c78104 e33458a43b4d2dd11e101e30ab929351de0b5f082d25ae4c16a505160592b35c 0966a02d1212a1baebfa3465e8ab99d4f532b5042f3506e10df981104c5c555d 0b11fb23bb898d7715253e829e65942fc232d7dfe9e4841d1fbcd10f32343586 f196a2020bac9d0466477d0c988e56cdcb0b3c3759552c6189891c1d6f82a15b ed5f9ef43f5e279417b4f8e30d489f8718c359afc5215ee3986bb059a764c571 4c1284543819437e8f82ad88acf7a22e4eb9c8c335bdcb152cc4bab3682593ea c57de822198d184d2e73001b9376f461f73d4aa45a91a4984c2b3497d14d577f 94918a4e9788b3018ab694678fe2d0473f3c22732b284379a1d25b9978d62948 276b05cbb6dec14153fa2904a6a02472c198f6f394f2b626d9f2d60774a7032b 9df70dac99a90c29abd0086c8055e0ba2a7c0eef7d6879fdc804ceec2e74d366 8b00ff8803ef289e885d724886938767ec98fa11e8e1d65b6d3521ef13539cf9 7b6dabd5b58160e8032d30b2157fc07077e59d3701a225024287386ccd7f0d8a b119c347dfd88891fc57d1cb2255d4cad260b4b7075f0d5411111c1a6fc5120c 531534552aae7e4f12ab22168a9f7074641f1c415a208f4c69cebd28372bd534 758387fb13c146096fb8f293d561797281371cc6c4d2b3b0e7f8c3720c316560 da1eda491041bb8e750db50d672eb1ee5858498bd4a0ac2f781f19423c291a3e bd78f49390f458036503c171361533203573cc5401b3b7481e908dca515f1d49 248c896f744b118517c21916f30ed6e2de85a41d5f32ccf89d40829731f55d8d 887b1d1d0307453566a02dcfb8b636fee9ec2a58e374288d026ff319e267486e dcc4de250c84eb7c46516eaca495d6604572539232ba911ab2334569efbe655d a81f12d2c59c648e29d1b92b41922175fd970699c0e413ece527f7c96da6d6a2 6a7d1cb9b8c1bc0533ee75398878f5be562a7311646c2646023440f4061f03a1 4dde777aaa78b7aaa468e9c57436852b0f78a82e4b67d46a701e655886c8052c 244b06548f82895180e985912d59425eb430e90f8fbe1cc8a4d98e57ef2c0603 fcbb9d39934dacb4fd551e13738989159e50c3121905692b57daadc02d8806e3 a629cb2fd2b3d23a5fc0b4b5d65525b3f95563a1adedf18077e5f93afe4c2e4b f85dc0ed19e5c6ce8094b7e290ea04720a2750c5a9a34d5d0920b131ccc0bc97 7455c8b70125caa21d923cea788fff2dd4fc0870706314d7da1124d27aa5e094 976d9dc900afab8780b807fed9c6fb6266c93006c79ee11d2ac35f4184773b8d a6e4f57c0f185d97520d57f0704d33db702dbdf29b2fd14925b3a926cda71aab 63684f9ee0cf221719d5d692d73c2f40bd59d3d89c5a1bfb923bffe78d84f371 d51827aa8e29334358508c1855699c88c3d025f34c32a44014206b77cbbb3ed9 8ff850f4cb967ef6b5ac621a35ff2e5db2260bbb0c0e70c1b583831987cb0439 e45f05962eadbde9147a9f9d79a90ec071d82e0c1badf01aacd2a3c569de993e 45c1fd2d4214a27b154d6d6c51df3c6d93aa49fe11aaed3204a79b7a1dd3ee4b 6cea9a9b75ee89cf6f11de5d7dd5b66f11fe4f016b29cc080171a852a87fd61c d0a429466cd97d73de992b953204292edce561f6a7bb7cb2f27d9617fb5f3f58 435ad3b21cac4a431f1d830b379c5e4cdd25b9a85edb7ce521ea8f750d8cacf8 7e6b4b115d0aa733666521e7570c001c9e4b8d5889f3f491262b620badd76d31 c93ba80379448e6c100c924e4b22e105a11a8dd7f96bb74f02137823f6510527 765e7d45fe2d7f551d4cbaf5c520d0b785c7a1bda4f190bb242d2d8145e3967b c78ae2dbb32e0b246271ad2054f2ecc22bf81b0c4a1089e0d165b91bf2920824 7ce707b6c5667e04ff374748a7c411b7c9170858dc4dca610f0546f5fead4512 4d4131f9038da305445d1712686fbca849d9934196a566839cff5c9ef7dc2aed de5d399315d2d2787fbaf3053fd89cc4dec93742e5322f7cf7e683725e2e675d 198b4cf9a79209398692368a41fafbff11e97fec3ef5cde96a17d143fe037fb0 e7acf890654c1ee1740d5308c5d8a7351f1e061390522686f02cba3ac5da21dd c36c20916ee8cb94a7d0e9aeadaf19bb45b74d3011d6777e48330899f6b239cd cf29dfa3a98a56f7732f81a8bfee34267b126baa260d3bef9335904fca37500e 9544ef485b636ea31a376f647ef8b47506c3b3fd89dc6649d85daf2b2cc5e82e ede4cd070b41c960c79a5e6e333ba89c75e3d35c83a9d67a2413630c8a3a2486 3e1a17e6a04ba85d400a41ad3bb645337e4ff05fc2cd6465a743f93a38cf3637 bf4bdbbd2524cf695087f225bc45062be0ba88def29e1dbee0e84338ccb9ee85 1ef07c0a97e37dbd73a0116a9bcaa53b867289c28a88f49ab7fb57a8393dd339 0749ab0e23173dd0e49253a884c037b64c9854d3712a3d694da32ea385b51c2c 4047f1f360a6a584a93beea5a1b8d63a7baec122d83cb44d87454f834e450210 41dbb83f7ddca07b2bc01627f19482fc9b8ec08bfc031321fddc48bd8b3dc203 403f9c751fc513587471419f06447432d05bb6f019f566a01f248b2fa1cce374 77db49aab7f562150c66abd8afb5f9f5de587cbcb037566c5c7803b23897bbc0 aa7f37bbdce4512e60aefb939a5ee85b981fd936e92a0c0d25453118928f9b35 9f1efdefb5d61a2fc2006564fe53dad086d60cf6c575019a9c245744af393083 ae7c9bd6b9c45e76a876d71da3ae4587a00948a58f8b55356a13d1be2aea9a3b 9d87cb8afa90c940266cb92318089747c1527513ec662eabe9fb6a0fae364df2 fc8597f3bf3015c600c3a5bfc5cdc72d44e040e46443ac9435460ca38dd9d2e2 62063c3d4ec431dc83521a4c57bd08bffb02926d5a26e1fc3c2f7ce457044b52 38e7385760e5091c6d118cb7f874d04836acb3da6a876bae79502714a94cec17 79d44af5fd7a0da2c35cee6f5c7b45f977935de6cb5eaf1c8500216f07e24378 0b3ae2d0d555a8285831ad835d31ac9e572304711e015c70965a9dd7d84e6b1a c07651733a370a892a7ae25ed238a776368bbf56f6ef720a1310bdead4879b4f 0827f18ce33012d3f08f3f24da49cd4fc8f1fe4cccc31838843f634a95eaa283 d97d73fbfefe87904299146c40ec3e0cdf4d98edbc0857751d94e748d892dc23 4d26d513c79f718c92b63800487d46b6e212c56328dc0e5912a44683e034adfb ad65505a9932365c2ae70a28aa6703c2b79bb79cc92cf3fcc5a4817ebaa03e20 fc1f7e0dae03f959278227a3d31a1e505ae5e1b902c61623cf6e42e617bea820 2154c81a5bf08cb887da9998332849ea2ba8ab7e8c4826364f55f8911e9c33e5 71e6acceffe23a9124ca88404365fb0da39ac681378a79fc6f13c46e9c52f20b fb47abe7544b635474bf0e6e6449b44b4ee83bab16ae53add3dc7a1f828f00db 0b8154c112c8aaa92f41c4a87fa4a25daa499127391cd0fb8c9e0cc3a0884588 c5ff9aedce8ce521a66f9a43201862fb6123bf6139a18e49ab9ac5dee08dd6bf 3be8a1a42934fe63b8a1d657bbaaf243753f6567672f622e828b6849a7068882 761f07e502e206da110aca4f11f8519897b3cfb57132403706ef62330013f5c6 7a713a0c07abea8d274523c9024764bfb0b2bf27e139ca12d5997d6c9e01bba4 b2f74d7728608c65f97e820f64f8685aac5415c06c9fe1176e120eef1694255c e5a5eb73a87876e8bf5d2c14e31e4b77f8fdd446d4bd4bfd3e87b073d6c566d0 8db6146a96d9024cf1a562616a5698063ef49a068000299df8a38bd319d44f09 ac7e38b22df4b5ee768d34512f5ea3dd42d32998ac209620c9183662e1e35177 fe9d264008d31715d342e682f7f856c21ecb1199c5f11ae311008886d6d0cf94 88f1741f90404a62be843529dad830cc3a6dd52eb374939d8a8710c142878dd5 958f16fb5359b7362533c60f2fa213d42d0b84531d44b4c64e86d3782bd58cb7 07c96cfb99f79253aef39526d74620a38d313d1b77adbe3d8df21fc95f0143ae 7264368e2383a61417da50dbace08ec23ff0506baa92b02e403229776d6fb085 31a8efd704eb995c4b97dad7f7bc339ffff4ee087d2bd1437563d3965a4fd368 ef528f29b673c25cb39e041245edcaf8a81bba9b9db147ca21828f0c617564a4 faf17774b41dcfd24042452f3610e7005b1462ab6c563e9e029b897723b21761 8f6f18c08a28017e8d95d131e86bc3ab2b65bf4553d51e8d609fde13ecbc68ac a675140679d6e8e8f8c539784b426aa1a3f8d9536c542a9a7f1bbd9df6261558 b29d3d610c2fd6a3906adac42bed87188da37d4a5f42a5d78eb1faf492d979b1 b7c6d26369c047169dee81c800ebabe3941dcd8be020f8124d3aed7a8b10ea21 e857e38b373c295b5101b51ed82cc4a1d0842d05ee141431c06f468babe7b47a 700f95a6cb5124f0d7e78e66fc3b9de8931261ad192a748867f30d1e795780bf d5b347d4c1ea5a63d86290393f6ded11fd6ea57b5ae03b1114895873c19faf50 4d0781056e2541885435ae803e5c4b29591a776e0b1b69bc40fc5afd7553f784 0ffdf3bb3dc5645635a430ca2121726462a56782506efc71cc8ac06ce083ee1c 64e7eed05604ef02b39e0e037c52c26d8d564385f2dffe449659a9799e573a54 85401f807b810f4c41df4b73ca08e28380a752958ed1022ce50a4e2d10c832dd 63ad551895a04e0393a74e8b04a8d276b3bab83e25eeffa8f1b86996a9f60b48 a877a995f0d8c3533599469000c8447987df300fe709e07ea069f028d7a4d1c5 d89bf51000a2745dc7c84781906d9ffcdae26bfc54bda2e0f0c1eb1fb3bed0ca 89d68862606594af3f400729a466f95cd3daff7ba79641b474286aa6ad475ca1 ad545c31811b9ccae716805eea430148e54e115b9cba66693f9cdb244bd5d6a6 4d986ce8e00043a197294b823f8f4700746ff361fd6f00c350a0c29b6dbdac03 308e7d9cb968aab45c1b1d4c39b2e84925dd7b6d379f0e2e52eefa2dbc04c567 6e0cf7ca1e381fe301887292591b8e868316f3fde98c41bb6adfc2391c2b8698 2b32cceda7303b7e269c9fef6d69f5976866e8dcc558a57ab65c8ceee295bed0 3cb59bb9051eb6cf04dc5545efcf27c52d21eabf669d584ad877f99a3f0a144e 94485653b7ccf2d037136108d4c7a2f865fde8ef2ed4b20aa8efb04e564380a9 1dedc559a7d071eac2c2d4dfb56b50f2686e97c51a27ea09acbd76a9f9cecaed c16606593309065b44af936a77d2b15da030d6bf053de02a8bbaad2690480ada d63b0d744ae12c971edeefdc7c73bfade214d7af4f32db23d5c9f391af796944 e613c8fb1e00df435f26c7a55c9805525da8403c1d62d651020c16b8756d5f38 22a6e17ab5a2cc0658ce5f3684142def7ed5cada03394cf4993a2833133496b0 b58fc97d80d1720cb8e01b95509f9421e89d9447ce78a239a1e00b4f4dbcf2b8 3551a756f01c8039efa834ef682147c4f51831d5bfd149a926ebc9ef8caa1613 1952f9a8ada0dec2b958293c460e6569380122d4ff96cd382f9990b1a9113d65 b31ce179f09a550691295947c6c4b8b5c5e0b2922a5fbaa7f5bd9683f5ab74d5 ac5cda8018f5706aa272e56e5053e0a3489abe29c793f95c55f71d440f387cfd c94c04243273146bec5b3e987dc66e1ac847fac6046e8fb0790908d5cf34f3e7 998226c1b9259ea024ba2ba74b8dfb87637a0092cc262f7f528c94523f52683e 16f3cd4966ab8f9ce63475e663c9e893c0a542be720581ce7431bc5f708a435e 37807e5fc03ca21d9b868f4066167aa920eaf08a15ba99a37265bfa009cefadc ee401175b8e99c631cb4d234c6e8b2052ec7428d3ffa7f3495885e4af33db138 a4d8e920c3c0afffaa954facd61c55c8b3b5f3e93766e914fad04f02fce81596 49325ada05a4c7b4d6841994c3b20a7b5bd37b7d0539958494d57a541b7c5993 2b167711741c2ec1a05bc856e8886b903af7f9f547c80438841f1719889bd2c3 55df0724c351f925ea2d1750b3408fc8fd3311f94c1f8678893f9c9e92798ceb 9985f9c82c070ec1267c0e0e13b0101f7995aad35a86a894928df48702e85e91 fa6d0168fc4c19153bbd69e2d52dd9f4bd9612c3811a23b1e5b9c0e5e0ce0e76 c3aeb9a3b33a335dc2f8b1203bcdaad16bd892736a4f0c5100173c525b73545f dde2aa94eb9e9d95bf26d58b2c79685bbf35a2221da9ba113d46e42bf4979568 e10886bb40fb691f7c09bf0a4ab8941ad9c032e24e243f5b475c2ba4186c59d3 ad1518f0aab70f985760c0c79322dc30e57bf73654f6a3c963ffb758b484891f 1bfd9ab757718a967827256835e0c0096be7821efdce326dfc574100a7818458 b66394bc8253948c93fca74d6477babeadf3f157e1ccffdcc30bd2a199d1527c 8ebb6e9304b04034ad45236327be3e57e8bc91cec162a41eaeacdff6d0198e19 83557406425b5344e6e764e61752d3768cd4156fef17cf955f19907634cac0b6 061a9ce6676639a1fac580576219718cce7aeb3c673eecbd0774b84ef8bb67ce 7e40ab40f12dc56d5d06e03be46859249d699be0cd89ed90e5fafc9dba862c81 c3156765adb36a96a8de94a46fbe67601f4aec75bc9182621be8dc484c759502 0513ab193f1e8d4a5ca277d09f9eeee851ed3839da3a3a82b27da1a14f73115f 23aa185377a01ef1addd43f45c561dc40b7e97a4681c6738881d9abc51ddd5aa aba8c6a9ea3f08846b78c7cb7ff4efac14a7b29d2fc774e02581eac971c03b5b 07125026c45b31b00c3b182c27ac97c3c55c2c4dacf709f848dbe8d92f764c30 9db919028e96247cc7a3ee59882610c96812f3bc7945949535a25f91a7c0606f 109a7250ce0a2a6addcb341da007a07ad06cb70dc5f93faa4141b7737bcecbf5 1cf79be4ca51d683da0c4776848e66948ffccbe296c169239be3eca2694b8838 aa77a28a76016d0cf845ba83bed7d7c432341c6b6966a338199eda5225065d4d 5111738723d7c50aa469f3bd7c6de3e14c14ced4faa534123bdcc05751704d8b 7ea0cad8f9ba4181d1039233d2a9bd4d14952458724b3d140a870af18b619881 80950e9fc264064b8b2c76db487d5c568100a74f9d6896b32775b39903144dd0 93f3fda9f2b715cb01e6fa87673e9bea2497cd87599a4436657908c97d904f85 9c99ea4a62f6c95f0f28c5497c7a1f2f1613159f55f1a27dd499494f8366e5fb 11fde2c7200987e9254f3ebcfa541b1648bd6bc56b23317bdea9b608805c3b4c 356ef495bc6638606093fbfd9e012e4896711a1d4806fd7e8f5fe5d57bd69c8d 2613134089d743aa37ffe93d0f6502ff9031290893a2a9338e8f743a53c4726d bd47901c352d495a0691b19951592b2171fc4eddb29bc4ee8dab4d9401496a8b ae33497dbce71bb16aed3da393673b3e0aff9fa8011d69628cd9d9d19c0d648f 3eec20ff3714ee302039b134c74e570af81240e75cd7b1fd1b41cc7bf5336728 bb30bb71e3d21dbeec757455f500d5853180a65742c58244da328ae9c0e9cde6 34b3dcd3a8f6f00565535967e343de022b120b8fadeae85993022fe7e25a4c42 1b176bbd1ca0e3bb38ca06e94f2aa5489ba238d8c32ea92ec415be3dd52999f8 d2b9ca9701d7672d29df29e739d30f9f13b19a578aa43e416f36a79de9ac1a32 57568b7d8af421e7545fd863ec22962efd30e060cc87f0ff82e1a4e53888cb43 6c6faefe04e2d41a539395298aa4d5ab68436a89485ff0e6a34ca5732268b5a4 36db9e5fd17c237ff7a4ee84d5cb06dead8d0e9eeb62a867f63299dd7dd919fa 807b7cda51541a80a70482e4fba12067fdebcd127c549a8e2f09bb98dfc8e337 8e7d1fd53b25513076ad8b1bfe6097cca071ec1a6c955061ca960f084a8913d1 6498ada1b42afafc68e96cec8bb42302099ba6f245f6a7258696fb6ce5bd41cb 8d5c8f0fec9c7f55b3a1fdfd086d39695f9d9a68a36ef50f530676965a4a2728 3ac309fc7796b2d5f265be3676ed4e954e665ca95895e858e42d9788f4df63d2 ecb015dca07fbf30ab8d47f24868dac9c0581fcb615cc25d07d8c37ea06767f8 a3408872145e07d320476096ec45595eadab8459a034754f54cef6ae6d1461ca 87cf4a664fa3c02056e11e7d388aaa2f4bff2d51eb9e36522d2f683ed9c1ae57 8412aaf5782ca9837ac92919ea05fb4fe84c2d90dc21b1e3a248b3972a7bf550 b2e93acb755c1f68bc44487669223b2f7b7eef5d43bc70ec83fe2e6297fbab3b 2a23baf4f9f6418b87ece32e292f5d8495713afa39a81dab0494fa5134acda3f 6a86f98231fa488e5ac9915433dc6246a155eb89b8b5299f72ee498b75fc1f3f 3f2f66cfd1718fd6d327a26636de809b74dcb3a6a7b9a9fafdad8b8cee167caf 279a56b14b1e4255035e21d71076a373ddfc5cd0290b2d9d3bcced439f357c14 7b80a69269c8f5f88e7b749ae42e3b6947c294c9123f008abe8b5820bcd59bec f64f3bbed7a50880db951b54dda093027393a1b8ef8df7e59ebc9bbbcb47a9c0 f3ebf56a334474ffe591b5a683ed4183f96335b471e710fb5a38685c649ae13d b1bcd3371a4d3ea9c8e5c1c4e243a96779297e924207da69e3dba58492912b31 bebafca228110a3db0d961af7088b6a075d194fb19976c8caa7e39698990ba02 1c2167e701d42305ee74e2a1074ade4ea7ff004fa1e681c23c3fb3834cb6783e 95496f8e9a90421587fe40046c9b9882243465da325d490a2f9f4bb2e6c532f5 d68a944e5c5bbec8cba73e6af8b66511a9ed9c06ea0ed3e9aa8d34aee50987db c3c6ca72e5520bccf170f81543f27428cdb1bd7faabce8ce11f3a41e4f867f27 fa308a189394370f2fd7157082d53655901f45ede445dd111998733a8e5a7272 ed00f66fc246539998b76121cbbf9bcf47b4735b0078bbc739a6d29ddc5ce5aa d54d12bed6076612678a628fd96e66030f82dc4262224355626b59cbcbc261cc b520f125f3da85c1df2d559badd49bdb1d2cf850bf8624019564122a42202de6 08d393939a613eb547f48ee7fa082c7c1d956b3f3a545181277bce02b4dfb9ca aa4705338ab973c306dfcd2b136bb8b2393bde2d12d2d9e82b099e1a8d17fa33 8783edf6faec2b9633298391228d1244b77c13def2a51f4c49b88fde5e1c42ea 37075da0aa8b1e11df201916fa558b41b13f25cfffffebfae9aacc74196d592b 62585e6444c3510fcc32bedf6c41e27e9bc00ec0bab5a8dea39e51189fc6bf02 f79a685c326be76777cc4afe42cc2303e64cf9db737aaca3b5011400935988c5 e920c0928c68195fe9706c50cf8cf3430ee5f94faebabbfcbf3b34fa55a769a8 1f45ed493152d36578129e519e68b5968807f96bc783eeb6bb0baaf3d13545ff 32bcfff817a373670568b72c4e37ed377be4d13e5f4dafc5a106997aa1626bce a1b282914b766b5fade9410be7a7fa04814306438e142705c9329ab5d7902149 3340776404cb836cd6d3911a729e33ef7e3126745007279c8a8948fb9c2018fa fa9b30c5c1a2f7f7f641ad954ff659e27f3cb857a04a947360d34ac84b02ca2e ad5c756abd51c49f31acddc905e56a3553ffb307f12ad387f2d73db135ff6d94 a0000ffbfa09452f6998f2c4ae280c7934ef051de534a2c0581c46d96130eb4d f5dab0a31d07bd7f2ad38961227ff9a76d12038ee6062def1c7c87527404204a bcecee1d1fe39edead4594e443e8602ed96c66ad27ef5efc907b2bd5c0925e64 1283e89fb60fa56a1c7c35c68a820d38b0c0697d608bfb711f9c786f60b9f9b5 cb85e9fa200425c5597c7705e3a47e448cfb08724d83a3a861dadf6900279ce3 627790dbbb5d630c352c9f6a9b39e81275b460695a56fb71a41a317052eec4d9 4c6d1ee723d40d024c5fad104f845aa9fdd3516c887f360e90f86d583064e493 23f92aa181d8f0c2293c6ad9dd8d9a4e6a0e52283a90dcd5734ba0f395ddaa25 5a7770d7c41059d497f8674ababb47ca51d81a84ad418231ca24227a592cb055 2dc71077b4ad02bc6df20ae7db16e2f3df5891af68603dde953df260fb40f55c 7b1d72e236fd870ffc59ac3cec47931a77d75b73f68e238bb4b62de6814bb42a f691b475e4730c26a779d29cfc1e7c595e2ae63308140eaeddc98ed3c9b708ba edee8c7a076abf646da7d0d8a6d87a5f63a897a31b3b3e87b5ed9666b09952d1 eca9eab3ecc7bee5745cbfe6cc06db7cafed3e2adfe9ffc969f9d52dba321d5d 4408ecf8ec057c9e3898631701209e0cbc21d50a2fad0b2167c9035352ed2685 890c62b7d18760b8f7cd25812e895345f53baaf7db06f9623e4bebda1eedf6b5 d0418370a9da951b6f70f4a410ccef079ae4bb64f8ba28d0dce9efb55584a4f7 4f26e9f87c71304eb203a00dc13fbdf961e48447f21d2157fb9c632583f06f25 f7f85074a09bee313a368b159fefc0a344480a1878b37136d8ebff4d13830c99 635a3fa92ddd54f67db8d83137726db8001f2f3b67b05e5902f2880fd969f591 b4450cb4335430f38887c975b69e3fb02a50e04e0e988b76f5c0e96b4e3431ad 4ab14f969913dd0e771881fbb7c8926b40ce945b66db9b9d7bbf7ea3052198bc 9c9cf60ffd3ea4504c787bd04b69636d894c3d6110acb9dd478b5a51464ecae9 65416d7b56342d76117c344cc97b0a9c771a6c0659cc103cb4573f8324bd9cd2 a865cdf40d38919f7c5ac7092cd25dbff60a15d8989ba9b3345090b59db6e137 de7a45cc861b42bcb6dffa599158f21ee190ea950df02051ced7334d075533a4 2c53bcfa715e17222c3fc277ba4c242d418434a7ed2dcc69cf4cdeb28170c2f6 a12f18cc492c0ba0dc42c9e06447e905aaa3aa9a67fdd10d3be777cdf000e848 6a1ee3069c2133e2b149ae06f20c6a9a2744a8707d49d3acf2405a28f3029bf8 25e2861568dd957125a0117bb746f5cdee1e3c35f89b6650f160e0cd44c0437c 541b7d17f16763c0e22d2c175d731e00e1f932a4896ae52f209e2a1c6f5bd070 82cf331dc739f052d8d1def1ec8ad88c4a899961e8d43f7f56aa402664839e08 08ba455d081a8427ebd7ac20811cd1e7b261b0c73b73f7ab225c17d115842cef 20e566b76fc465fed677c0f6e8ca1bff15fb55030ddbf98a94263bceebab90cb 25b6361ea65c8a514ecf80b728b7907f454bd33b3710157018f5f9ef2d7cf4a1 fade45f27e44161adee573d724a7f603925e34d443cf213dc7f86850a2eb52c3 5d2ade0074daff8fac8954a83f329d3a5c5eb5de7e1bade2236e85687f5fef6e 67f64bc1fcb23246a657d6766ac995a5c593848f458c280ddd8d81e4c4550a2b bb89e833ab1643e4c8a71b190195fd3b4fd01055814ecdcf33cb57b1d8e93f26 cafcab3995125421e6c796b0bbe8af33c08e32f310e08a50d45ee623634af7d1 68afdf7a9a0aa464b6f89360e3909b8e65ed0b2f430645bc52e747c09f22f0ee fc0f4681fb090879075ef9a0eeebfe9820b27eaa542b4870bed049129df30878 b39cf3e54ae86058b9b6e80ed61fb82d38f7c20c42a71a828ca6163473334abd 9b58c673129ca242b5dfa794a8d598f24d27b5546ad480e9397a238e5cfa02f0 3021751912e142ad751c3e3b1fd3f758cf52437585acf62633bbfe6bf4eb404d a0b9e680bb568b94753577312ddaeee444b74bc4cfe88832f6f01e15d67e4a2b 71c3b3a51bc71b385952532c2c727c42fa2c77c8883b1c5e2b0553c5a39bf8e2 b4684d5d4595d128ca4517e66038941676f46baa0b2e7a250591f65798d4e15c a3ee03141d20540fe95da7f30dac762def247ffa83e9e44e3159dd9a1bb6cfa4 c435bf49f201c92af4d23a6d06c4f339f0e93abfb17de722f2fc74a3d0a372eb 3522df0f028b4f668d0e546835e7631c20de5ebc27806b2d8640b599bdf60fc8 4472a6550c85328f43c4086437f44838e1f4765268dff05e7c8744155bb270f1 f016b78ce84bcbd54bd615d61e3e2089308051fd37c788bafd86c4d76996a52b 9b20c702fae90882665fd5d6b62b0b70e4304860fc461b3f7254255d17a90581 15ddb72e8f3a20f8b722ee6f4fc8f084e9eb43037b30ec55dd98dcd4fd69d73c 21253917c9117837d221b7f6b2af9ef2d021171d18f65c2bc09364988c16124b c12a0370773491ec191589c152c689a723793d8de5309979c976d38d895117da 484c15ee91b1464319e5ab26b7e82b02aaf4d5af00cb2554908e4292e187dcef f00d62438c12d59af9a03be0a0545462904c02588e6a7831896993f88a9e2dcf ad6d5be1c5f44e51984c41aacf8423aca81397174c8ad81ec06ecbfbb51c22e3 2e4bf569ca69fab8454e4bdea2920f3849b52c3b9ef1714de1f5547da2179782 714bfc14bd15a47600ca5ffa7d3e83312e0d0ef6bb7c26048a983b2ebb26c170 72ea29c9d5e13122770eef90362a04c750dfa66d3adff53e800fed5636ec7959 3e47a5357ea5b00b9bad6f01fba4528b0b1988ddb1dd8d12998808c6e9e830fb 7ebdb0efcc7a00d946a99c03fb11bd621af56d7d03f1f4f69130ee2cf694a76b 4b3e3c3b5cce86c00408c668c71ebbb6cb542e44e5ce70da2bfb1a08fe47d815 8afbb4edbf77a13ea1fdc92963611fc35f229974533f9266255be6486831ff83 eea62eccdff1da6c464f89bde5d4437bbf80e650cd6cdc3fdab3185a82d4a66e 31f4d8b77e1e4ef852fce4f6ce552a66cd0f063faa2219a2b349a99bdb82c01a b64615194a8716dcb0090d21e4f6e9d4010abd3062c917699291cf3a90ee33ad 67929a53b6079a682e1d3fbbd95a7ad95e313206620c9a6211c64fec52f707d5 af2360c260ecb5d34677b30c2653d89079a0d6253b8a6448339d7f8bca72fdc8 e8279280128723e76317f9c05923900bcfe213daaa7eaad5d35a86ed003366e2 4d08e6f4eca91fc4840491c3bec93be0a8eea69e95bce9b861ed69e397b2d142 d48acd892fe26b321e9c395cd4c3648e429750227207d87bda5116cb8fd9c6ea ad168c5460b8d51e727342d8baddff5d4f40600649afa3d93385b2a4ad4fe0ab 0c3f8bdb3addb086f3ea8e45a76b11c1c89270c86d5ac0193257905f35f48a9c 824f133dfb3a117497cb91fb824a1421b2c1d9f4c4a672e41f847ae8fe113e7d 1314e3ed87d699485a12a1e96ed9f2887e232b1515082732abf14bdc4b06d741 a0229bf063b43992550eaa90492343ccef1c929e29b0fbf0df7d784d682b58c6 e32991b467cd2af70cb58c60dfbb751850a310aec7f9bbe0370c80e9602c684f 3a623ec2aafd2fb1f10fae9c984b4650e02f3d64242effd5e5863000211cf27c 93f8777c6690a63fbb1d2b974a48f54a9bd41830b4739f6d6a9bcad8585e884c 759828fb8d7d7ced612028a8c73fe37f7cab820fa21ebc29f3ae3f73f36ef090 a9abd0fcc664542459839c5d86944f12156eeaa109676981cd50614cdb56edd8 57eb95f31d25d34c07d443eb1118775bb4a943fbfe582fca144927a3e01779f5 921176f1463b97f484269b746ca7c0c5142b3d2d6ba64be24040f070df9e8093 9e5ca2fc80355e8304a73f461ce86ab9d06bf25ac2429612d099e1d9c88331e6 2e586f7f943b8e451bc06fd1413d4977d80c190e382f692d520eb31cea64a643 de4ad9f01f4c5827315552f493a08499011ac7929747ccd1dba08e9cacd6a221 cafe3b4960784f1fc0278c6030af8955c2c5a802de19fbe79f002d1b3cc3ec38 a31bcb4ffd8d942d5e71b50ba9bc0b9f872129d5165833fa872e8df58f8d8cd0 84cbb9242a186cb7ca8582da52060aebf3a2e2ae37815b8193a3e45108c9fc7b 6a879e26eaf2628a12fc22055ba6166af43a93b0eadf2c41209df7d3dcb84a48 1e0687073d7316151a0aa43cb10ced8c17a64aa4bc17289d41922ae2e98b2e3b bea7d9c8598676fadec0cd2f8e3a64251f00bf2105d88b69e4e22f90e7d5b2d7 27293ce62b6ab5b070c9c7141966471b1855ea5c2762927eb36de6e79d12128d 595ebd5751d10f021a140dc23bd15846f4e157c595c76afe9a0853c06303a054 817efef816d88a416efbe10ae191b8af0c02ccafd4a912b2e0679a1fb45b6bf6 708ac0b8b9001a18e9f178c53e07df599389a538ede6c1b92ee1bafc50ee4211 90af173acb549504bde981ce635e4e8520128bbc4308c72fceb68ab7b12d8e47 d9183c4e0b7ef42da1bfba93476e636d50968c054dc5d0a6c5dfe7b0006e6908 0d9e95b350bf7337fb07dd804cc8173b8268e659f9b77e25f08d26cc4c3c018f 91fcd443797c4ef5d62fb8ea17558a4618c3b6569fad24a48477eb8d68321d24 208bdbdddb5951dfe24d9c1c3cfc48daef15f5deab1cfbeca3bf67fcf40d1436 39cd15d922ee014bd7f86cb1938b72f381efeca433d126cda4813bdb8fbea671 3f64a2bfc61d1c2f94f3152a212c70453a9e32ab55a77a39370909852c7aac7c a202310fc5e42be095f140e74b3bd6950541e520f6a9f20a3fed918aa54a7eda 296cb325127016f52cd7bc381ed95b14c72793d3eac1bfb8108568f58773eed6 4f841014fa8e2ec1761fd40a08825ffb984e7f7fc3d8ca65fd3024a36be95c67 56dd89dd22fe8ad46273126100cc30621154ba36af673d15644a1a525d90ad51 957e0b9e1943e79fb0ae176eb19ce925f214d0a603d0aaa02c95f1dc39c30809 b0e36582d3be69580bddc0fd1c6071f9f251e532b37b3075244b83a821e79f1c 5a542043a85697372e64a31743e9b885b84293d625836347607c6564684951a8 8733d4522bf71dc9936056e71e3a5cbb5368f8ad1a341123cbcaaec967e91e00 3f08e5cecdd563f9325e1c77bc9b5e3464be6fe1e7f1d1c1937d20324ab2b66e 042f84c3e601831f4b210861269f34bfa6e99ed9fe753907c87269695b962f34 d25ae070bab43a5a7fc420a22bbab879d50171d05db9856c3a5aaf50bdc22255 ff7838d862247314fe36d4f8680ad109f87c23458edf7445708c1e1f0d578309 324af13a77556245800063ace38c336fedd64ef9fdec95eb08af45ee3e8cb1ca ccff52747d6f00f4acc24ef4374e3dbd6413f99f14d3b9fde3b7d00ed3d5e65d 418f67ba896e0b99662243e5b460f13f5dead33ec530248a841e31c7552f77a1 588401af28db044a8443a63169deecc75bfdfdaccdce3f6491e7fe642e01bdd5 9812115b6c17b6ff44d0ad173206bce8eda2bb37e28beee31b714c8f3b5b4775 102d2464e81b0f837150db6a9ddb94e6a76f5109552427e1078cce431b93c19a f2844f7f29934f6e8a73940e3b65aa056a0964fb9687ca95d21c848f54a02013 c2c7d63221a0d356ef2d42c636ebac8e15b5199b96a19172d6c9bcda5db4a53c e79ae57a7260180c7b80a87348e334fbd230014c9ef4e4c43b6e1e66e07fa227 3ee080fb0929fda68bec31660e9d356b6c526c3a3a77a27f9cae24bcfa4ad4da 25309580734fd714ba88d24ba414a947c761d9e687561daf67e0d37a9737435a 71aacf09eaf96f4a0fdf7cf61fb644a5baa3b6ddce8d943eb83b46a1f2e9ce9d ca4a741b8e5d2e7c4ca80c14d1671b5a36cc1c341b65df43cb50e730c7230ced ab71bb8328cff79790ef5c970269ad411526f99e1bbc463e1aef87b62a74647e d6b2ee4cf5847499f58bb7220fc16b64aa553af9e2dd414537a213333016d868 ee4c45e78a82bdc7eb4e67081662f2e97b5d4b7202885125f65e9424b3b696ef bac5089455d20faab8ce48702d40843ac3ba7c762af95b135a2b9a5d82ae9712 86b31fdb92fcacf13d3bd5ad1381257bb2ac2223fcabcb5e22dbfebe521dcc4a 35071c372809d80b75b888826238ffbc31d98bb9b46439bc6054d86992cbae30 2f760221c8b2252e6c1d29db247704dce1c0f875ee703258831e9f9625151757 c59dee188c01e9ef65d866c09b4fd1b878370265d8e7c4a332dcaea44375b53d 65bf75d5f9020fb4b8d47401971a3f7c228f8d8096de344dea4281ec6bb59c97 9616589f21dcb633eded2289a1c8694fc9fad00df19886b06906ae90fb7542d0 19c6a7ffe598901b476142763cfca514786f2525c3319aaab150612031da0b3e 8a6f2d5e7215a8fc470c67167e862bb1d3b5c15ef798c40ca62f406bd9c904fa d6b3b32c1e9bdc15d595348ca0014fef6d3fd0cf4ab4f5ffcbf45edecd31a557 117db57b2cee7b9d151f57258cc3473d82b6d090838988524f5fc95fb4163cd8 8e539953c0de5669294451f4e77a34152eb582e5300ef4cd7fd0b694419ec4f5 463dd59da496b371a9858ac4b0e8213109a6c7379f273151aee032eae700343a 4ff740e972b5cb92238ddc1b2738e0248c212b2ff19e26a37a99bc6545ea6d8c 78a5ca5968894d147cf488964c36b140c80013e10ee70a78ac510d7219003da4 f96006d16263d8bfb327ea707cb0617ede5dcc66ab3959eda7a3739e97bf0919 be727d6af4878530b612baa6dd2bca6a88fab9a0fff97c4895cfa3d389174917 2efdcd09bfdba633db597e33ab58282aa4adef0ddd25f420817e1b93c2e20bd6 55d6540ca5cf46c860784bcaead1c83d30299f07d681ec6f876bdf100ef4f4ae 1ac9741a52f522870ba814d5db0d979a44aea57001c988a408f41abe5e4bcdfc 224868b248798ddec8ec553efdc12f337a9853c8abc8feca2d0fa79271979d47 74945739416135fb8b92b9b8b528fefaacb5dbce3d2ab6a33e560c5e537097e7 0252995e05c298a1ba035fb9b72d1c79817006cb0a8830485a316195434c3798 5cf76542b526eca407579a65c28efd6faeda3b466ac294e8f39c9919197405a2 16765ebb78dcaecc67efdbee69dd61f143bf5fda8eba29494f8571719570df56 176c22ba37e1525d279da07097f15b8db8d8d91122e6942c4a4e2e962ac7fd25 aeebf19dd43a2e88c7daebc0dd1bff8bc9aaddd30b476ca78ad47880398a45e8 497332bd262af96c0f233d61f71d9411b279b5f9721326721c78a8667423f571 a5dfee23b82ea2a311114b8657833120041a657a6aff25df8f8138d9cd95818c fdca7d3547f485f5a6266979766508ab610ffc46137c82498c75f5bc1c43acc7 ef910a41e76e35a5a0fdaa45a8bc260a8f484827042ccba0a9ccea1d3eefaf02 2168306dc59c65b235a5fb5b645b7091c7dcc941049811eae195cbda5214178a 2f94d7b5f48d3b2a994f2b6bb674aa7c45a30509faaccb918b21d6bd17be3edb 6b322d54c832fa9bc982551cc4137bb251c6c263b650ee710c676fa06554b362 d6d6c99309a2250246c1cbdbf6ca583f12dd0a3142d55b757fea3a6e02cb238c 5722859c92f15a5abadde0fd5dca9cdb8f962ae138e61445ca42ec01aa65fd23 aa113d032c49dd3bf9f1feb48d01e3192a422da4ecf67207d150a43be25b1635 1d8f9e4896f88847c619f6d4e3c902f19d3eb27faae0cb2a6c26ead8dffff44a 73a92dcab402444e0b1a3a5ab8f009ddff5ac89f50fe2871262153f57d27c802 b07b78a51f6a90abfb9a6329da84b851e8959368655b62b0f241fed524ee63e9 895463d9f58be52c49f63b84a562b26bbdc7b30fe1688d6628ba9b442519d630 c1d4661e843519df919ecfb94f6db0757874b7130670a39da320ac925537d0e2 0f87ebbd445cca52d8635f5c1a0af729c8700f0383016e70934a4b2aad5a5722 543ea672bfbc807edf155e97bf1f34f4b7db91f2bc601f941fd33f3fddb65169 f073bee1b134ae01a16c4dfd04b9590e85af0a084bd5674f65b25d33243923e6 875c5e939e149c16b831294213af24f4fad9909d0512b9c7b63c5765d6ba7f2e 8787065b491d237e10ddb3b71a5fa879fe25eed79e3152e8b23db6f319dd2f14 73a9ee018d328697041ea0ef79d62f13f0704727d79602a2d9fbd74509373b80 308d9a3e04f5e0cb6f3efcb42bc33a425bfb718f6cc30e220c07205b3d066e03 e9e3d3dd6950fe6891260d9dc52d3255781d9e47767651b3b9b6b1737ec33098 748b80f43eb3fd3509b48493bf419dc1ccb3b7115ddd948cd97cd23bf1577533 a4404ede210cbd7dda44b4337c4fb9fc5f09171adbbdbc6c1a4bc1325f25d737 c3f5b1538a796aedcd5d40d7dc5bdc0491dcbfe70a3a3c771b2671fa54bddd89 515b220c4ac83b8dd75750faefc45ae5d1b5157c2a81ad6a831f921d4f54be8e c27edf121c322593ec58d0a63ecc09778805e0b0572325c53ecf1d79a844c59d b2709e9737743f4c498a4dd3d482606490ed3385ff365e5f62b4be6fca9be6b6 2fb323c8ec9e350fb3f565a6e8a26a64e52dda85f21e2ccf900c68b2b1f00d13 29b0870c3918c20a7627db7d1cefa7827af44262d1d3eb4efa9951898861c408 dd521ed9394561083983eadc6dc213da8a4fceae52a82e34d8920eb3db83841a 57dcc3fd4d16b3c68c6d73a92e62079519fcbee3e1c998ebb121c56c2d79d88f bfafb66f58e71390f3e769af762bbc5d9ed20ad8ae314879357deb88e1b1b417 b8dd630b5bdff0591d42435fbdcd36909286e4fa153d5904f40af6c7635d0fb4 7c2439dcee6f4c680ca7119331ede57645f51dc5f7ff0daa97e34bef5328a256 b8637c29720f82d9264c8bf68248f65b1d6bb788a448828fdab76c09001d5ce0 cd055dff9bd988864745c46b2c3ddccb7fb3e121c356294a1f472e3512e1709f 9ee64087928d96f24e914e7b112a481ec96e863c72691af2508640a3dadd3557 0bb53fa47418ecbc116d3cbaaf165116ec8d16d807909bcd1a646da7c6e7f13d 83a5fd1444e648111342b0c4434b897f1b73296120ac43d1fb2160b4ec408560 f7f73e005d05efefd15d3c37d94a03ac26def99d58abf9285ffc7e3e613196c2 da4cea40e24b878ed8e35e7628844e88a8de81b38e3fda4fb12ae73cab110c4e e2a49fe8f6e49496367a7bd7ba72746e3e373fea260e9e772b4c124d6806baad ca6b3b831b65eb76c24c1f8316ec67091525d941561553acf59ae718a1756039 f31f4a8926833ff59b1a6d8765cdf60176d825134c347e64770813c77fb6b867 e6480b1cc71c6b8b4dac41aa43adf87d10901ae970d406c37495ddb30a5c30da 080872fe6a1f85e01b9557927c9e945fe6c46b9dc06581054996f24f2f52b07f ef71906661ecd36cbb17e92aed1137249ddfdfb36b4662f4facfc4dd4365e845 e310be617a0baebad5b309305b94cf53d10d335712f280c4fae3c331b24c8206 4241c8ef7a7770b14d9d0f69c5c72d876ebfa5bfe422b058a6b4690d3920804b cfa1d3b5923e9dc1ac9e7cdf633d8d43392bd92a5065217d33d01d697c088911 d954a9edcb881f311e0728f2b13578964201e3b2c5f2a8b48948f5123896554c 62b34f9f72422883bad24099fcf48996d59e111633e8e9336cbbc32c3f7fc8af 4be5d534c8e4bf62f71ffaa7c784806bb93788e98728997564ee4bfc8c96ac73 b3f1b6484533ecc1f36e011aac6a9ab7bb426a0c97c35de530c3f58d1128a2c4 db16947217a4c24c872b0dd7c7e0c4bf968b939e1875229aa9c9606ee6095e1f 96ac119be09fb43b59668292b46a2b1923dd1024805986ee2cf86cbee5dd1f9a 830511cde05db6d133877f556425d07627d7806e3dcf80c40579f2bf24d3076d bf859a16f6fc0106653fb551aec5c4bfa49a537e483d734c9b2d1cfc8f6efab0 8f3f5797ec050282c09d0c94ed14c962ef0996068804bb8543d68513fa6bd298 5df24bc5c17bbf8ab46741b049139de27dc2185c1fa86b7a77d2fb9b94025142 682646ad575405758d8025aede46ee8271f7f6c9c1aa02708de9234c86c23478 b30c726c045753901472b0ac4449b3e0e899fef0328bb9fdbab4dd65cc7b1e82 38603c49887932ab43ccb1b906c35a2467ec32542f10e8d9c1e41457f829340b f5e04eca1d8ebaca5cb6eb5dd7ed8e813f4f196558c2621039fddc837db20c47 85ced64b2d0f45c609b786057d6294978ecd7277114d848b6f46d4b5ab898d7b cfba5bf4ce2773a1cb102b2cbf3991fc994b8b74b43a54969149401653f54496 4da5eb99593ab33ebd43a87319fb8b39741e3cca5318e240de74ddbea1338371 7637f4ac1d6d04e8bba8fccec49835ca23ea7ec58e913d6f4fc75dfd4525b8b7 cb997dec64a828f32253b67b0408891034949848093c395e3ed5f6f584bca0aa ed4121165c3867539733a5bb7a90736e611aa253bc2cdf29363a01f22e79ab91 18e4b0f3a671277ce55ff9c06b755e84b02a89abf86eec3289d0f05296f425bf 2061ba0be601d7464efe12b5bdf95f7f0a5d0183617114512bf94e5ee75f012d d05aa6021f3360930cd4dd6acf0f7836cd51f2a06a1f3d1fcce92d60cce7a9d5 5106dc2681a42618522c87015d4085c0026cb1dcd7d159d60e86babce69159b2 1c9ef5b58943218c38304bf50a2c8359110582d2ba126e782804f48aa162fdfa ffc25d1f52878383df85350ea77fbe1cf9df1755aa5644e8f499fffee6516fa0 13b38c36f56e24abd86ae58c37dd8c39fda477aa7894c74a8ff1799290b063ad 7f42d3c7546b21b6ae19dc411cbb5c03d0f2f751db2b865d635425c58e8882f3 4cacfe7cda3bd1bc3532cd47c465f5dc0bee74427796d1ad8ab3fc21197f0fcf b8769ecb9d8e4dc1ed343b06d0811c53678376f842e40c072abb9888375b6959 d51936f9f19b19a4605968750a6024f10f3e03257760243f5ef4dce453ca37c9 d1bff38dbbe7a135f206739021ccafa369b97ff1c79b70ed2764f75159bcf43a 364b3c1ddad01f2ba58d9143c80542209a84788c08930ddfce5c17777326f165 7cb16820d64bd6077cde1233a61c64c36b21ec4e4b4237b3e888d5b6700192a2 43652622e0426bc0037708c7e82a770a174b97bb2b48447867dc330d061b9f70 a03ce9ed96dd53dd6bf0fae1c3a4b29f1a7ace81a9cd62d5cae9f081ffdaf045 9f8f3c95df90f1593d81aa083df5cb9e29fae2cf1de2b0a61297fe2ae8d4f008 d1d008298313d3a90f83d4e37a768414ce03e5fcd914fbe8f2c34a4146975c6e 9283c56aaa310692824dc3ee31d3c33e33296540d43555d371965cb06de700a7 15e057bad8660e25b2767b8b1c2f0af03773908ea2b29106c8ebeea2725060b9 d77a5331d12fbfc5c6533c0d9cbca595c27a62c66518a8aaf4b5f62c89862803 37629a0d71e80760e0fd5acb0015ec0c70384860c665dbf2e4e81e15b419c451 d6cfcb81c00891417ec11dbbce7a06c5a9dfa8167387372da7c2c802c2d405ad 63651d7a4c5784a9e16557f39fc99e19c5a6639a0206d8d89a329fd92a2d2e09 7ea15a5693d49b48c51eebb413b2f2c390e4783118edcdd4365bc762aba94c7d 73fbfefebd5c6c8e550d9e8f59b659d25f88aab55ae016c5f64e5cf1883421f6 040b885dc359d5c99ba045089c43b5413d94aed1b0db58de6bb4af86b7417a6a c45b048ce067666b94a03afddfe3947307ccf21eb6cb2d7a49e0b209dc5f9940 375ecc78cf302a2872cf7bfde46e43fcdebf49592a065a3f8dea0e1a6fb6cedd 0b614bce6de6285003af37313f30fa7b19d7566552eb0a366508d055d661204b e3b1f8b8a63bab018b0a33eca256c727574f20b94c420a2d89dee560eeb4b1d1 f6f1b8de4a068294ce447022154534c587f46f2af63628a0668f9ac05f299c3f 077ff3ec2f4ef7a2cdb84ffef6db8701a6a545e57462be793dee67f1e7152b5b ba4212f490a6731e246298f680f624a13d61a73eb0f5f87027904d3ea0dc21f7 0b37351f5039b2da5bcf4b25ffaaaed739e009b344471d5935e749b8405da827 653fd93a9cbed7da17ce3c15a4f03e56a8415d234a0545dff1e7793957035371 4d63c2b04a8731b2bb9532d8ecdcedccb09db27606a4cee17de7d1cb9d2e0c39 91e919216b6c07f14fbe3ba2989509783ee11810cd6ae39c26f10b4fc0f0ef6b eed5748a0ac9a4bce48c9b0b4f4083cbc671a3070d8eb50ef0e2e814a964f6ca 40250692f9acd5a56bbe62ae5912caf10cdd0d12feed4007c35320ed39459f47 a1094508043395bd191865bd9cb819e7d0b71362592acf58bae7d305b56c9a14 47ef7f2c1bf61c6486a3ca71702d97ce3a34b27973b6191e6c239bd50b8ae0a4 515c8da58dacdbbc7dd1f450e94ffd214b9b0203c23397d268512b61968e32e0 04a1cbf14e112c750c99be94c2395f5ee30ec3519fb35d86f8e84e62b0de46c3 e3c8987d7ea6677255638f5209dff5bba84811da47df587c5ff7aecf1fd6ca05 a4b3c31890a1e71f23560bb0d06876dfa767af96eb170805b987882412837a36 1ca79640be8be0483aa028847e4088d8139b4eb6fae227f09fdd79e0eb668039 26e2c514cdb5690e1ddcbb6aa2bcb3b29521a16001a7526ee843ca14d108ef84 c83ee40983d511e3a72ac6344977aab4cfe72da1d67e7cd7da34f98cbf6235d9 b5a8057478e65c4b00b23341734dc098b7c5493685376a08b5220c5eaa1a6342 2ad189eb5c2bd0cc7d86bb96dcb32ab24b62191ce41d104e920a38011dafa977 cbb79319234ce7be108d5d85607a48201701a45b00084e425f7e3d4a20c3654e c1c2adac8f193cb01ef06cdb26a3ae1b1eace94b791d5342a886e8ae59ed3d45 64b17661b66e3367c6666acd19ceadf15a1030453ad1f2898113d64184af492d 6ffe3c65b23153313001e8b52ead5b517e08c6a1ca98d0dbe78d78890f02bf4e 3df83dc181c05e8f129610ae74056d81887ab5688d67a12766dfc50b2e4a9189 2c78af12d742247e187b630e9e460102079e8746f357e69706aae4c429ffdd9a 117d962e3ce63e5e4f81415077cd1683844ebdcdaad52c30acbfbf87bb584dca 3c4f36ffd27d21e59134b8c9aba7150a5b519beb808361032fb8c24aded9d3a0 0cf92a5b6cd85df58f180c2854a323d9b6990f8e46d5936d075af8cbace3ecfa 1d4be477a72ec2d27bc006f9f184b5cb18d32f14ae9bce438d526812ddb2b25d 9cf0b82c2a9610a95b6fb0e4d1a718a1b34173bebc40e630084603abf0b44979 b07c6727cdcc18ae246ed199dcc09b5e56487b94a0fea1a546c0287255ab43d2 6511ff4341fbf44840f3fa47df6df8154f8dee8741a61e15fed5cdaa64d81062 76522b87f2799ef755224bef3f5d381736565e257ea269832fbcfadfc9f8a0a3 5bb7d27c3e64c7c2c7ce4747088895ff0974c89ca521a74f4bf5ee3fce81eaf8 01f9f237c10d09b16516ceda52baa7c071a8b6591a535a4113631362c9d19e63 8cb5b69ead3d51198c8cc561ed46ca0479a3e3b2b839c7a72faa53a4a4add403 3dac7dfb30e09a2612d9614fe4dcf8f57ba6ee3007b318a5ffbd71e0080f7f87 0713a790c712c1c3fc3c0d75c5e9136ed2e6277487cdcff5015d2651de9702bf 4d455e59a42e7559267bb6adffedf669da0a07bf71370560b453ab46b2ab7786 849b048989fee33ea1a8d4221964789970077788fa815fc7871aafe8fca9409c 008fa148730f7244ff38d53833e95cbb1c577c3ff1dfe84e24127c3ef14d2aca ace86023eafb39be4605d8b2c59b7f1a1007efcf67e799fe4e37be1d7b46f999 062c5836283b50e53e05556914254afbe367d2ca09387d8a59a782f0432411b2 5577db070b2553440ce3d39701b4723144f005d181a5eb55aa1a15dfe18bc3d7 14a5f44dc584b197891d154fda853a66bb8467e7b124f8ed28c00eebde8640c6 3f59902ec2952529a84b35cca822f34f49326db984720bb355b0221be5b9999b 02d292de493bb816624730dfb7569c4278c560df806b85fad9ec31b1fffa4ab8 b0f457dcb76d980320ba7b5bb05f35f5e9bda0875e638503d592ae405760ca2d 84d1609ee0731d85c28c26aab53827aa43f5331697003218354b2790fdb7b166 c98fef60c3ba6efc4d5fc5b338db20d603fa2ab6f9e7ec5a49eab6a78edd022d ee56fce94213b1a21c8b001ee3d6599104ce282071ce1166be13dd2b00c255af 26b3681d23a3ebc908dc65fc9073cbf13559db9c3590fea81e61bea071698477 4b4acf9a3fadf261e09c6a5328c8cbc3a420d150c04a406f56d5d3836cb75d17 1d6667d1982f140a020b64db21eefc4e013ceb57b5ae60025446d0ba4b6a274b 2b60e68d15854f7f806f35ec4f4928cd2e1ece3fe145e64e913d71334a75c5db 287fbc0edb5f0a25e746439bdf109db66a39c597438ef51fc60a98952461b268 e8893049a2c12bb88090fe8870ad578a03f93a0e70fed46cae5d1249b83f5929 720cd775cff62848ee2ba6b26b8e783ba348d78c45a25512af42acd5ba0b5647 387d3e0c84a1ae4987692edad21b11e509a2ce5939821a6beb44fdec918b3647 a901f1d3fbf909c287b8cc899bafc718d90886d1e443dc2c7f9977903833fd91 9e828edeed0b53103adf6f8e639537ec25cd9fc7b00223f0774316301aab5012 fef3460a62796a9f47231a5159b0a55633cf269a879f59ebbf5d346e2f7fcd96 8391589bdca0b10c7160f9aef53b7454fa26fa2a304acd2710489ba877d00c57 3ce61fc23aa78859b3097558566212bccba7a3ebbecfc0f958f038148f5b94f7 8885db55b6363705549244fac70d7270df44054d6e6d030dd0e489cc3bcf7c99 15e3b7dabbec4b4d2809503ed212d8ea978769a1456a98c5ac55b63aa87a37e1 fc6480f534f885402e60074ae0b8bc9d3503e3a42034d4d6ce89e380011d2a6d dadab8aebd804cc2d620fed306a7e49c9e8b5b186611e0056705e0903dab1267 3302e786c31b630be1ce35431704e33667de94a14bbafd63a192991694070337 4c39d2d6534ec3af90996ff6932aeb35766b2d5a12e2e34f35d9409e653e0288 5e6978ebd75b608865fed6a17ddd076e0e63f105af39559ba9179160b00a533a d2b33f20f3805d4eb37435e21fc2a77c51a699426c417326e87668960fc4542d dc4e8b8dfa2100479f5dba3c3dfbfb7b02011b84cb33541978b550c90629d413 edf1ab0f362a62234ad09b305d3776fd9fb1fc2f406903337b7360f3969b5f49 4f7149ddd69328970ed6bb92a1df837c12436739da4274b66b67adf4910226fd e9f17be93fccbd8f7ddb82e28f5d51e4dcbdbe28218bfd160eabbeb633fc2e5c 618b66069d62ab9bd87c0fe78af0295ffcfd421c806090414a7482403767ca2f f3a8c08c7ad59dc4ad61b8de48603aa3c9f5aa19d7ce46865f02914a2f71ed66 57dfd9c8474a24a678ba732570b25fc7f28429c2a6dc106ecdd2645efb803947 a770543c32da38e91807bab262ab20d2a1b46d826cb0580eac533793b9468184 24a545effedc4307ff90e4eabaffff5fdd175a090c9ebecb3d253cf5dac4b8d3 2ee34966aae2777ad98656c9c5f756fd13d0edb86a5e91037d4a4f5979ba2809 156cf741b918f852ab8d8770421dbc0b7b5261b48c791a736bc468c4459003a3 b8ffc95c9142f64e34b59ed42e231c010967faaeeea7f0861cd22343a8228d14 80afb795d4e70df03ec00c1c85b8b34681686e814bee84f7e89e7f1e7fdc655e 18bf79bbf56084a0dfdf5f60df3a92b6d216b7cdd5f4661932cc35cded19b8b5 52f7d4aa0245e99e0a7cd5f0ebdeef5e17e46caaa0a96e9251338492553bca58 74396abfe8664480c6a319f5e2f2c9c195fa93ffe3c0ba407a5d7d6e75b461f4 61aeafc1503389fc5f5cba017570be2eaa6c43dc737c6d678d7ccb53a1337b0a 26f17d9d7a4e351d9bc204a2417a0720dbce3bda8900a3e1edf615b24a4e778c 3ad244a19850c32f7959e1bd72d52aab945a3328e88c30da68caeb426840cef2 fbb4ffdc44ebbf58f237f28516dc91b298608cd514edc8bd44787f0d3e8a5f4d 86795974704e17adf23ec03b4c95c5682684aecb894c4496152bb4e6651aed51 789ab607002312454180fedd8103250f6ae97b429b1c48fe43b6e401252cc92f 9842cea08b555d511cba35c3a6f4b8186290f4253f5449a6343c68442aa77bbe 888bc7cbf91b3b290a3844c524d4fc16db82a4848029e95c96479158922d70c0 45b094f58fbccb4712740e5cb3ef5311001ea5a3614853e5c627c35d63f8df52 a8f12ae9ad658ca3ba1b36fbabcb0523a91f63679134d0aaaa6237c3ba0c164d 511cbaf3daeaa848e1f59c964c020e91ee017650bb15c5c48923c1179dc5bb30 cffe757f0d47dbc08a4042e58665cd15d9c33901f0d1f6349c0e47ff47c169dc 85d95d12430eb41191aa3c9338beb1b8d5b35ea71c2a0c188124f26ad15a6756 cda2aa9505fdcfec9184d101ecafe658715263a2519f74458afebc9295308a67 1070225e95a16ef9895785e8796679b6b716ac95552dab3efced7b3ce1f42a02 1d580f3fb5a6eb78670fb4409c25ae91fd43704de03728372ed05de27907fc4f 2abbddeef4af6076976d2b6f890fac0e84cb091ae8223d062c6e1c83fa543910 c92c65a4f105ea56650f56f4910688e4634643d5419cb435d90c53968ea456b6 c0e9facb06611d7d60533826cceac3d53c9206dd63df18f02363d1a9e8768ee3 c8a3b60bafe6637e9395589eed3b39922991d39881cd107bb04a20d4ffbbc9fd 35bb121d2ffa6664019e0265fdf061eae50e20a03aa7fcf6b017ab38a541dc06 e6e6ce83ee1d6def2c71ac3df568d6aabc732791185b73b9b0867a2a8bf87c6f 10e748f6e40757c62f830b494567d2c550e7e78ee2fdc043d296fbb7ca63704a a293f941ab5cbe4779b6d9dcd80d209d837d46eff645de90a1e6accc5efdda8a 9db1562c1cd95354350f449f2dc6b44d646f8875654dbfd912f072d2093bf853 cb577beae9f446aa54f2cf024ee6eb700f3aaecaf55afc97309485cfff746a53 7e35cfbe4438a2c6c473e1510d94b4a77570943ef54d92d557037da57abcb1fe a5fead4853271d602129ec523fc39a7f165f46153265ae43c826073e71f8d424 4ffff63e6338e25a82c061bdfae1b952e99757b802c47de960d7a439ec942660 5b42ef9db07a2b0309cd4723f828f59af890767c5a197990ac076b3f20dd2ce7 4a522b4d65a6e5fcc086e47c0a3b80704be8cc5c1a5f69dc0d6ad114f4e1755b a9fc615c8213c2a7d102a6826692a6940ec1fe467702f31109895627615abd86 8c570b858f43cfe1639df38e0a2484486bb3252ca4952e747479e8c2dfad7ba1 125bbc11cf44101bb8846302bb6634cca6131d9b01f67b703d12883ed1705f69 1a332d2757e99784cd8c78deb7e175a4ce9c99ac7d958b55d0b2b171c5443666 f71bcc4e2aab13d2896b73e075384a9eb54e2bcd3296dc7fdc1897f563bbc030 5c7b7a1a68d11fad1621eecdb4e36624c9250598b675a76e90b744a380b11d0d ae7bac7c16c692262571e2afdc6a480d1ffec29022a10834be17d4f5b4b983a7 6996ef8908aa16081db350c3c2eeb548b780facd34f8cd995cf648f7a085e47d 68471b52cd272ad7d0bee083944e732536f05119426a20e755b8e92fbc00b62f 9c73e105ed495c3d6e7863ffcf6af733cb18758b4e2e4030e3933c1377a72e1f 7ebafe10c391b05638a98cc99876038e5c75ea6e84c64e1d96deb68949cc56ed 56f208ebd0e6fc4580c816702001aa675e4c1acba58144265310f5de4ab76f2e f0ebc44936b4aa9f7b49f6b98a5c75bf229b1bfb30e8b2dd5f53819a58aacf8a 0d2642bd9139306b7b5f38915208e7ff351011208a258fa0a2f63e847380b3ce c3a67bf233d29c093070aea7aed1183f3ed17c06c5d42c89dbec49ec57290fc1 7976e523de3661e27abc03d950a64eded849b02b3f465bd4487aa13c5b5f6cc4 507309b29bbdad3fffbec5f88e8d3e9917c9ade8823e43c90526f905e313ccaf f104a2385f3281b6ef160d3bd7ec241f545aa1115b64fda32da8aaac4d921238 919deb3c4476c9cc1d6b152aa8c97ae5bc6998501c3ffd2a9752dd246af48637 bddb29909980fed5fb9f7a0287adf561db5e256b316a08f530528e2001cf0910 a385551e77af9eb97195b15057b78466322d1a13b7129aba21a110eb34828bd6 10e934bca19add73d92604ecf0a143c4f81fb48c59f8a5c7958300089155e41e 19e3f61b26f41511aa40d9790cb91faede1a114da7cfb75346876eef36d3ece2 5a3806d93e84015f8ae7c06be19b41ad00eccdd0d9b59bf93c7e8a42d5916dbd eab19de8c4671e950316550e5185e7aac48fe0b788d36fea1bd62faa907d2c75 4f74ff18c183ff19498f6ca582908004f65fdf97799f29312708c32c59915b29 d68ac31282112ec37482102d7cb78b4264cc04e165f384e3c72b0fb5262c3cb3 c51875092ffdda4d02ab88ea61c0efdd90383bd027d9a5411197903775b1a787 e4b1ac46b90a7e88868c5f2000fb2b579393d2595120ea63040f0556ab591a8c fea8121e866416e9e6c064baef81aa8b5ec97b77e8762f9b8d4f583778057e32 dd7a1cbd85bf2bb57f905803219f00bc3b7a2bc89445cbc87b6a2b21ed4c63fa 41460bce2067a673fecc1c370b8018c99075e9bfd483ca06c8050a2755cf845b 36476177f84ca20efe627002cbbf80301f45626acef350981ed62cbb03bff1b5 263005bbb801d107625f824db30bff44d2d83f85be9e4d35aa40f302177be1a8 30d0463332ab193eedc36a568751caa4592a7e2e66e31e3aceda73f86c190028 f03f348d76cf4298e7724e1c8e59958aa8bfd1746c88892cff42ce5d0ac4c8da f315f35a5867e499ce13ec2db00efb65b5b401e53e72cd43e9319daa26486355 cd3b04c1879b967b8961d2066eb62ab9ba2a712e7085d8157111c403a3c99d11 04b6fae367a3f88338afe3a3d15ab852f9cf4c7a718b763849c193e63041ad16 2b435ee966ad05e3374136a18bbf1e848b3e52d65f437409bb7ad97609bebd32 ce41e198e25547659b07982bac11832d676287f4004c27a71d6de84b307ae98c a895a86418e0810d314a106392ac50b0c899eea0ff505ef8f7134da22d62141f 55562c224b158e524cada32f36254f830d0766079d6a3dee9efd2ee53f989b39 d61cb2f04f9072dff0d49997d8129fa4c98674beecbfec71ed933f1b8573d7a1 7cb62cee911f22f41814db3db677ad8f94f5cdf2effb4d0e4035c0a9b244a78b d3100ead9a3b4d02748afe891d1edffe869dde37817579e0516895203b02489b 3d82d666acd3c8cfb8092c64a0e3d24e9331f02522bfc005b423d8f692c9813c 7cfde9f689065a77d34459cbd9dc090600dcda81ee723942aecf7b0cb19c79ed 251db52ba095eae2101292a2c197321cfb02432cd66264050b61f3e6d303576e 0206e451a5975cc0f244605b1472ab29bf39295875b08fa083ec97ebe64108d0 399b7e009cea07e7f4b1cc818ad78298f5d45da74de169d7a00a9304e10a03ae 12546157aea7b6b1f0fd644e58706165a3fcab70e66d1ab100db26ef30870d8c 8561e1090db5dc5c1287a89a9260e979706bcca28a3320b06b9137a439dd088a c791f220bd6c4fb757d152b8d693cb521d6b63f733830a3327c4674aa4d004e4 a73a9cf1bcba9e37e07519b8ae5366fc3dd79ef212ad6c561129c6132fcdc185 969d1d6a39d3e3903b2bd52d52b0423c4f44ae57b30b5d122db4eb4916e0f897 5ffb1949928a9cd0edf3e42eca0c3e6a4a195f984e1127cefd5f8c988fce7381 b97ba921b6f3d4843a273fdfb4c0f7d01e454d0f79c36b0a53ab636aa3dfa517 5e6e215cbe69f8a5be2e6817a43e0b8e0dda1d167e9212c03463ff4598fb03db f48f070f97ba56da2dae2f420984505fd9d41d8c2b515b25e9c93c814deb5f60 86369b21992ff6f3ac403a1e15bf97d9c680872f5d5b344661b8c9037a75f5cb 76933b513d0a47a9b4a4634001bd5927a65a27a85d641b545e1978c8eba4bc00 2f55fc07217f68f926a511b6ba76ef4e11b1e11a6c1415dc6a9ad7f4cba9eedb d39dee26cfca8e355f3e5a0f90a2cfacd1e42745ebdfb30aee8a63d2b8eafbf9 d541768d7d3813c981aa5bd91e44ea8ebbe4a6c48b4939fbd9950bd290e2f6b7 7fd096957a212faa43e961a3a3dc7f3d9fbdde47dce5ea1a1e224145f4beb63d 03995817db9248acd170c8859e1dd4e56121907e250d1f1be4372e120088a602 36da4137ca792dc92b071823230ca3130e9fceedeb4ae09a06943c6b4e612579 d9566d2c3f7772ed47fccd03c7fa1f2f3873335c24253036cfbf85cd8c8987ff 980cbac938d2b175e71ffe501b2e89526340ca0a2d7aff2c24ebcbf0fcfc37a8 1a0447a473806709c890b9d380d51f56fc06d807f3f441ea0bc6daeefac71102 866103cfa43d69f67d891185fe8f69b4c8c2b7ab8fa5f12a6a6bb06b51e0acbc e6bcbfae8c4b9c206fb300227d3aac83da92e83bb3468b8d0d63d496db912e9b 072ff1cf829c2d1b036be0d39b9e7acde3abb5853ee6feb1db87a5a3175025d1 3433aae2f090a6d0ece12701a7cb9493c8fd777e85b42d4dd2857314541ae9c9 d80b82760a5cfb6e8faa08c5af40268e3a8d06257cf5fd46bea0bcd6283da0ae 092c1f5243e69fc972a3e07fd8cd1a10d8f6618301bfdcf8aa67492b59133a1a abcaabffd6aabcb3917277fb8e36012f366f049562452dc312db3f9b472ddb05 f1776d97b825ac0bbbad70364cd715371cb18199080553aa3a9d749ae3347f3a 517afc45dc9b19cdc5df36d8fce777b6a493d7b593f8e2f846062eb82634a8b1 6792f743182228d1fcc5edb186247080e5ee3ad8b2e742a6f3427176f3591647 25bbf431961e04de2ef1ba2698c772b25df3787866068429f92db179d2bc2447 aac363bc5816fd098056b593bd20f9940dfa40f9164cb3749d997d9acf6bea4f 14f0002693b60758ad5b2cbf8de7c60ed7cb7059f54e0245c9824ea54fb62f50 1e43195f2a80b152cee1cf541a75bee261cb48a47049428c879129c9793655e2 cfa7d776749d6c9c4582bc4bbe42445f6e28cb211717cd5bbd0beea2a1f5f051 2b26a52d99278da9ae8edfec96bd8372fa580ae0f7ea355b7a7b6314c6732f53 ac574127a72f7bf7a89bd68875b9ab31f7629e5a228aa3c2eb39d365fc668db7 4f638cbff46f5d45378ec724e0bc501e451c0f7c4a70c4b1e570c3ca0f2a4fa2 d3829bff7c6f4f2cf54833d22286108e69df6322ec093dc464401415ed94ca84 e01c81802028d62a59740f8c58eb99f1c353716753d5339d3139a5de4d447e9e da75c1a0dd27925a9be1365b27aff00fc340839e0e025a7e580e9bab06ed8621 a88934bd7331b097dfd8c2b6d033c1a684dc4a70e50cdd9b6fd92ff1f44d62c3 c5cefcdf817b94f44056d9b42b8d5f39a7717ff9f923eca6f3fcf0e151b036ed 13165c9af5a50193b0856e89b9183086862e6f4af229eda3f4e0bd050d2f3a0f 6946897b19549dc52d2a1d20afbeff7ca538527fca33907785e0f0a6dbf22c26 8ea682e7651dcdd88665c84400f9bd1319ff7415ec7fd58b84073a77a8db2e99 cd103890d41618a5ef554cd2b8d9bbf29b4c844649cbad71a02a584e48178621 9c21222619812ce600b4d7b2e5e49d05bd2a7f19656801e6663e05cd4a6a8147 a7e3f27160f6af9f1a269dc20f614c95cc1b4519153f803d79d7b875e1321314 503cc02f8203991367219233eb33e5e59111423428cc3f914a11d11fa35071e1 37717899174f14bedbafbc4e46ed538e2387980626d8816118b5780985103416 b7c6467115b2a71e383ce7c2d308822a88ac27a5efd409bbbb81c6398bbdbae0 27462539045bf188cc304b5b460a259748ff702abb9aff274a6101957a32ddc7 3ce8b350685cc8e47107b779fa374c20a4e8b1aba428e7962ac9b5e0ae684ce8 1c1e54347895e6241d564d734372af169a4b06c290e65c187bba00ee4c902dd6 001319a5443069dffce68c09121d760d5c74d873735fb58649c99c5fcc3ea08a 26a84563936d9191775e7084f14699c7b19e4e1a08f86a8c049a180bfb8d3f4a 31383062e8cfe502ab7d1a5394e5dcdfd46d649072583a6c46f94560550c5b0e cd474973a0c832ecf50a74157848d1b646e3d53880d3f2913c501352390b3618 2fb291918d66f3343d083c903ca68d179f16d6640a45c61d667637a30f9f9228 f890605141eb650079395f57c86cef098a1d781c8e0e822e0032057be728fe80 9f6c929ab9690e01fba6878b68904aa0e2a62fed30f684d98e00d4995edfc4cb ea79a2f0495cc11fbdc10999b78cb943dbf56d87baab216db954718273296d22 c0f15570dc8841310bcd4faa72ba49bdbccbcfd44b4ccf910221e9d348a4be3d 18e6526e5255ea932660aabc5fb5c0b69be21c9b60070642ba20dbae2355d7d3 fb9c7297297c8ac72d9769d4edf7ba864b6292452cbb7d07e9c0bc1d12584070 c45e98e097f5dc983119eab940faef3faa000a832d4ece31d0c3030c1ba20bbf 059a854a6ebbf49850514da66aa00159e5379211eb55961f607b0d156d2eed03 ac7e1c5235042fabf9f939c662eb209f4db5b07a6ba1bb3a2f74c61a7b685d26 e934aff06ad40e524b739d7f1fc4d7f1d8d0de76ba634e21baee9976221427ae 07b7992a78939eebd1533ebb20a4aab5cf76fca2985518119754da6b56b6f378 b62bb5ecc5d27a859c7de07c798ff2ed5dd380d293c3ab50c376245c92219d53 3532227eb5980ce3b72d6438bfcafa1cb36fb30086c02852e8d7743a12a0a5db 9b3c69a3177e3e882eebd3edf289270e5c9152043dd427bf08d8f9df001e81c2 9600db572b8573ea51c2623a2ca792bcf626d94741349c42aafde6e36118fb07 008abcfe36468dbd04b3510ce159ceda794881be3ccd767f773266003831ea46 23afc4d55fae89c3b25e28d1d8e2808534ca784fbc73b772e68e618be6f8f3fe 2c1792505144c850fea22e6298e5e0ca9f5080b16a516457afafc5308d1c11c7 eb0ac49395762eb6482bd1c6c26d88fbbdbd0fc778133ceed25f67c96990d4fe 2bf441f2ba4c7b98f230b1c1c14194a6eaa6cc952125d2ae1cdfe65290e70ee2 54d49cdbf2ef70bff52f5366a388c0f2fb3efdcd43db813bd4829b02a5eba447 da87bd9af5a9cb0bd156ff81de4aba30a6ca951bcce3c907647b2670edfd78f4 6b88b8bc3aa97218a25b7225a188026fd0b92dcc683391788e446b3fb9162ce4 0e4fc4b0826bb82fab4787abb6c7400a8f17c38ca4f6d59bae49510a2550807c 6439b5c1fe264a1baeca31d7454dcd0198483691dab1b61571468015b37b28dc 9ce997b55700cb2c478d3bc25361856545b10064a8dc7c8453efd7a15cbbf19c 0f0e079e734d1cab29f0d965d6a8b009888578811bf7065790b805e75693ac42 77268c7f46e9d5157a012fb5e7f678ecb25541e13df9f68a23b43f54d948e242 0d89643cfec242ec7346364cb775ce5aa2b33f472390931952847c0ab08b3dcf af74253532f94e8702e314ca1b2f3e954f6857006ace3d7220b5c6b60f5f2009 4dbd54eec1032701c3cc3dd0c536d179ff60a5bf36efe3e703c0d14f83ca7ec5 80bc95741d8c33893d1ad01f4bc72f8ee12f45fa93a9b3b124c14978d4425af0 580b6aa55ce25b13da21c0b11c1c6947bc15f83b928353ad883bd2bf9e5dbf43 53ac13282f0e4b2fa7a3dd39a778261c7b30d52bb177453ed44bc92f595bd81e 11b884ae961601b5b144033c70af503f06db40e5e56c68035d26777a77788d7c ba873faecd2cdaf18b94b987596f5c3d78446decd3afe393471df3c4ad91f484 c649f69d895c3a74fb40f900f6004e9647f5487cc99bfeb6f089514a66fe149d c0b303d2803e6e6e0e5dad9d7607ff8b9686a6dff7db624987392876b80957df 4d7133ac4627e206470dcdbb69800cf950f6e20099f06c62bb4aab4daa8b7244 e427b9b7b51f46201126eee2cd701fa7dcddd16d6a2be0c00c50c9f6b7d41d13 f4d2bf9dc85bc02144481459caa0cd17938a838fe049b2e063171eb080858671 337ac019c751fdddf4eacfbfb7410c78f94c7b3515faac678a74c06493941a3e 685b3f876281c103f8d6e93016c01e522b54613d23c762b808056848e4e2ddc0 d39ff4b560b2d7362cb3100ed739349c7bb65a96072690907aa7ce36a998c9b9 4b09b0e3b0172917c654fc50716a8e5d9c4f70fb989735826ac065368079ae0f 2afba202839aa6bc02f617353741d274ca2b59f700fac265e5463d7c77d37af3 57461d10f53af6a302048ec071d23dca0424df0b22e4900e717e50d1b8104c50 4149292e99a45ebbeaeeeb42fcbb5429191542fcf57ac9180be9e4bd146c7d11 91e48fb3f5802cbe280e8f22bff8cbd2b33377f9b8a57e54c6b101780bd2d8b2 2205423bd6c33b7475cd77523552d3c2245e79a17d1fd58c2c98719f3bf64e76 de4ceffafbb6b250b2814e5e609c123dbf0f8ff49b7a64fbfd7141471f2e85df f6680ef228e42ca5ca3ee45c4cecc08b43f679020e8717127878af24fe621166 e526a421cdbabf3225d960ccf1d2edaf2552e2c4cb00d97239c1cef404e3bd5c 161c03f89f7a92feec21b5166257a693269888955dda5e94e6947a49df9dc94c b5885a189e98f3a12e94a6a7bb99762226a51d215b663543e7b2afd930964957 0fc7cefe22b36cc1abf3497ec36f1f7a5a86b16ae9621b5d342b0db6cf94a512 5f498f59dc4d7237d50e3f4a4f7272f122871f24816c73bd4c9e2c457b9a08c4 d8738d1bcf179dbc06e4208fb598899261b83d22a20cc8abd7834f05fb644831 31bca5fa0553d95fb2931a8de3e36c233d5197558ce359e94206f4165cd0d065 43c34beda1ead85a95b999d8c5643260cd6f5871262bae3a02aa0e60420f766c 7e448b39f7195a309ea4eeeea294dcccac3058ec740eae3be8a072f8f7f4d506 a6ebbced8cfa40fea831aa9af0e258b8cc5b8fababcfa22d900f0428e1661a84 ca869c53f466bd56354eab3f28f20d0c129616a2fddfc1b70ea7118c4a87b51e 850c23f8ea8559b7a0c1a5d5a9dea7aa4f90c2e2a1e6b0bcf00d8ddb1e8c8b04 36bde8cc1a96151e69e2b52e39d85f60202335c4ff7241c930f1605e18697625 9ec74a95ef0ca4ec834db4f1365f537c2d3ecc8a068eacd9f96078e0faed0ab8 3c5cb58fbbbe97bffd95776db65481b396085f854074dd2a756316eaf4277df2 318b32cfe10b3b7da4d4096f80b4b5261d3fb87c071adcb91bc50b491588dde6 bd125407357860b5c26f38dc7daf0e5da4341da941dc9d93a7cce3e394978798 58da592569a278a365b91a99727eaf3d19292314f5aa43ebfdf5b5bcabacdc5e ceae0e9177edfcbd4c45b403fee01a0d4cc182a7a11e8d0fa7e8a7f2b62cd2e4 9acdacdd35f7c2722f922256c0e1849b13af751b66c188f243f3743ad3fa0e5c e5336928a111d544f0430a68c509bb256bd8ff13abc36b36005496486b027140 854a5a3d68261194b6f732c3dfb085c17b45b8cd0e02a4f6a9e829284cb7519c 1872065f788d502977dbafdc937056e9f1afacaa0147e9d01ddd3703486810ef 3253c38008f57a51de45006c21b3ce8c3050866ed3e7fd1906611f133d659bc0 e48f56e8d94cda3c4b30f7eaf14e846297760a60c4bcc568ca17c1243cf65674 495d8a0a3d2b77f60cf3986bf145bbec4d880bce951cb8605b3abcf8fbcb0747 c500c387c6ac9eed9ff4b65d03f3ab7aafa9e82986a3eae62ca3ac9332fa55da b3eeb61317b39cacff1bc942fae33997c8c466157b5a3baea360c965a4d4a0c7 5dbb5c7ba3b47e75e7964b1afaa4bf296a65e6c0cfd9da120b4e45405c9db16b bb2a7a3a256280b4084c77548aa8b4f831d909394c91b11b570769ce55bc2666 1a1cbb3a376b735d77e3cdc98d13f368e5847c318f817204a08d3b122028e3a2 2317ffd970d8685e9077b84bf8a1c2f0659e691f4849fa8111bfa9f37d576ece b2d20f34384758d70e2f549ad04855881692f4682cdb345c69e2849022d0a826 d35dad6f687b0ddd30558c3b50e64c4cc421d2d5a57eadce74de7a7b27bbc72f 2bac8ef966bb4282b4a709e4c83fc9d6a45397ad29ae69ef3c32a6dcf2ad83ff b43a866b43af0abd88ec9e3313e2bc13e4ab6bc57189a841baea4be912d49e75 8480a85bd13c36ed361fda578bacc985575fc0283b366cd76d4322383f1a3742 be2d68ae08d536f8896dc7d7b2986a4ca5b00b109160df694c4dce27ebc62c51 1d7f70ea38e300f9bf1734c9b282b788c26324a61ba8ac0f3e1d09f8d7d8e9eb 9d5e86ad85e62a2c2e3fddb73638bc57e7461bd2ebadb67669d671575cef175b a775bb3b5ef5cb7307d70210d4350a97340a39824fc5e04ef96c114444f995dd c8026774b18edb75842b43bac9937ded7a4fa5e74cc58171a643b189246c6073 5e9bc91c17bd0ca046f8886dac2a6b666f7233ef2027242fc5f7bf235f08a5de d650b3e0b4da91d56e438f501c42417507eebd75d91f23fa8d919814d102874a 5a1d10fc06c6fd7d26c2e6bd7a04d5954093d971dc53750fa9f7f0e14ca8a32c 547e07e19e256fbf5a31054f2423ba9ebcb0458daad82654413e13cde7f00057 feff927dee1ae48afd1529e7622d4361e6a0fdd429d0d996f66193cd9724deb4 8563eb82117987e28c9ddc2563fd229c43e356aa94c5c9fb03b593b9b3e1fdff 82741488eb5bd3c0b793189a42f351c0a5e6acadc29749287a588955dfce413e 9a176922532e73d416762ba47becb90a1a43cf107cd716da1486c34bc9dca4e2 d51c8191a20d99ecb0402f66517efa977c90f357a60d5c69ff461bb67cbab9a6 67beb0b7eec6af602f01f7d41382ea347f00c34598b64581b748a59daa1061ef c3c69b332d4784be851575ee15bb39d2191609f341a637ce0af76f7135a4fa73 168e08dda6356f3481dcb2b72730fdc30bcac857c8028fad73db0734c2e81a62 5e6020910508afbc804c10203cb8ea1dccee82b95a5b578ae5ac4ea731efc0cb 732ac63b65580612b3b2ec34793e436d2edb4c5563734d5b9436d0eb25bd79a1 d5b295b6152c0be537dfdddb9e50cd9f54c12d8d0b9dc2a05bfce0e5f41b0732 26250751126360d8e2ceefe1b022a09a826569ac794a802ac30ab58aedd89e7e 2003b7efa8754a4e4544105f4a8156f4f7974920ba5557394a1b7dcd60358812 e6ce685eb8286b0947cad18240c1e72f33bbbbd2d9fb1afc88d4ed369d44bf17 44de93e2f9407f5945e41d754290d56edcada4854cc8703802d66012a8598eee 64b224ff4b07c3996324fe55cfc540cf0afe829f819b1ba1d7fd905be63ddff7 54e7e79bf0c16b340500902c613129295afe4f467ddcfb278f4f28d44667f8fb 70b8fea28f45a922ef42e313293dc1dcef1593042cb6a1e54ff7b416ac7fa82b feaf9f02a2b4d3089c64a26de99acaa2e100f8265a59c6b9f778c00657113152 4ec0aedefe8e64be51d6d6dfae4f1d87dda9d6f1c2fd81c2653d889e3beec24d 9d6d5e01ca56de31f7f8c8b0239b19cbbe2663a5c0dd8c75afa5b9a1fd35a242 79520260481a490224ba0ae1373155c3aa7a517ff27917d25e60d91a826388f5 ab82a5280f8a799cd87521db693482db964bd84b5f19f023b6d25f770c361fd2 6f3a3ae1921d1539fbfe4a978baebbea28ae48dee3e5bc62b3785a72f0a07cbb 9ffc67b7191989b2a3b6ae0ab47c61e9d7684d7004f020a63b1b424a58cc254f 23c04f9c351aa486e1711b6f3cb0cefb3582eb51807789d853c4856d39cd6340 4c23eaba2e2b090dae5934c8aac743ca4e7338895478161a373db7d3bc342916 7f7eba83a16281ea095a187847b37b6ffac66aab753cd9dcfab528c0e00e039c 074a5d11796ab11aeb3a11b9a02d017bd4c7fcafcff2999a198b3dc708390f12 39dc360965b5ef3b19bb23c102e587fcad7af287eb1aa70b3eb05789ff90b715 04e739364c87b9b8f6b72970868c5415dc5038762c1cd2d184ad55614aabff8f e291c301f7fd8d23981e7c7b2baf783f9a7a5a30f61ebb24978137934e61a3c7 80dd1f087215b589145743f7f854079c2cdf8e8104624784a881723cfbae0dd6 dd54b2c0477c76516109586df63e50e4f12007b32e34e5ef1028bc3cd0a4516d 07dad53479bc303d38815e69c8700154654c099f7b64b9de11dc7ce042227492 8748f3f12f558c55c46cbfd6a1be512ae90a469f90ef4804b795aff0095bce65 81a4e406663bbf6e75fa0802b86804f9caa39eca1f6f5697b06f8295f76721b3 8957905cc21df69b2f7eed6c77e1403bf2f6d29770ca6b308159d2c1c4eae10b 208e1131b748475896d5a7a16084d20e30c1737d9e4ce863f081d6bf79124fae aa5e9412d3630938a6765549ec888051ad28b6b0997f7a85bf2bc1e2346dd43d 0ac49b5dbdf268f74300e7033eae5559058060ae9167310c572169095797bd06 0671cc83b99ee1c4901257cbbc61dd6e82a83922aa7099dd9a6bdd855d53d707 56bcd5c3919049148a385d8aeafb552f1818d11827ef8b43139e97b6e87402af 812441b133d243200d6b3c5503a186ff8b833700bf409bc07a97c43a742b5227 9c49ae15494fe980da2b4c06e676afde929b75122d5488c4cf6521cf1a6765f8 c02a0e9a953a800b4d20a706326db04ab52f27ff957211580f25d1ec4e16e99e 892a0238272557fcbd74b859e6bd0f1ef2866f05014f1e124f1e0f53549f13b0 b127786e2988d670b4bbf2c9b2aace5e912a0c972c6c462ace58aec41e4f47a3 03e949f00710b4768406ff2e65ee3a8184ba9200e6a26f820584487ed8f7807a 5c51c998c11dcdb2d90d105da024eee08259e484c07dc9360679f53aec147814 d659de6fc9c14291c16518cf5ed85a4bafbb15495181546eeea63b10fca10669 34889ae159c1d015ca15c893f79bcd89c4d27245a64ad3b44aae584cd2f4c416 631affc8da5d3057d44e9cf96e7f32d209b45f1f4016f874f9576cacb78a28cf 51508ad1afc21e93e262bcd9eab054c2625e1c0c85d26e67d9a580adfdd2dad5 51af5d3ee8e7a083d44c28ab3325974961212c873c16a15ad6c3bc1a7cc62449 23edc1b6a163817942cfb0c1fec5e880ab4104438c50892046c746cf23f76e76 ff0ec2dc94e3b99e03dbf01bf538960565f720c8df4ea0a305582d6b85c35b6b 155148560e925243ddff5875ea1fcf464b5d23e628028adebd2f06a737be4bd1 2f36ec3876365af809938e31d95ead171376b8fbc147082f7e10c7821410b3ba 4f8ffc1ad2f7709c88cff35f9f20199cb892e3d323e0197a3ca67c99300946a9 4213faca651d7c333781f98c62cb3a893e53402af782800cec9996d7d9640bd3 c25813866d135be32ce2fadb654d4355c51eb22be3300bd4c41b184b855d204d a73c552ed1d3a150a9d9d89732374904172dee8794f46f8c9bf74e65ac13a4bf fb375e523c21b0638c5e0541f6663719bde017828b44828833d6342f27ae6b15 47864f74de59cd058f78ed5d55afad0b8da454560e8db80dd259397e81d7cc41 2ff7e8eabdbb9404e79dd9fe360c7d7694057b7cc4b4196ed34443ba073b25e7 b818a926acaf76259150964729ee1e6ed2d2aa0f374ee73206eee7b327592174 642912eeed48eefeac2f3c2c6ee46eeef0ce1be063d8f80b62e724c6ff9ea147 e68ef621ba8a1ca7a5ab4dc6348b7473717bd8656fab3d64075b9cb70f129c40 0d6d7da0af04eafaf173bf85cd3e5a70baa210a7fda2ab6bcd5692624d83462e d2bb6b2ba7eab4ce010199e05ff28363a32158840e7a11e607a820bc3a486b5b 460cd942e21e23586198ecd37cc814ec95fdafecece620fa01c73f0a400fe04e f4727a352680f0cb01584ab4449e528bdb02789ca75b0b41fc42c273debf4f49 e2ed4ee7f35ff5cf60f4de401b13bdabb26541469b541a6cfe0f5deb97a672be 0e5f0bbaf1877bfc5007859e01021ef0b8fdb1ce9f158fc65180d9d3a5c55907 0be8195ce2d1e6eaf85a310b7c5712d908034246cf29879b2ab0925f6eb600fd 026437c46ce46f2f3a77f0542ca453108c76afae6029f75f0d99d39f6b02e495 5ad97561e0a0e323872b19aa0af4777f59b90b4e3149a6abfa2f8c4ff8e169f5 6c5ad7b56f316dc0f7d614701de56eed5f286e2c0b086a2f61531a081c29fd37 4427cb9ba60fe50ca112a519abf86757b8dd09517aede0672981f6340d7151ee 4ef35098eb57c2e7176330b4a5fd28ab0cb497b125a7d7f334818858e7d86a3c 7ee8a9db2450244a7361351bbcd8304900122e9f7a5104412ec492541859ba8a 8c8823bc6b264cc4a8affa9a7e7299d7c53e6b493830260ba3d0aaa2e3a9de8a 93640a3d1eb0784720e8368cb2281c52af23dcef948f17b2d32e228c788871d0 6485c727cea636efa1c6b5bcbca823226a48fdfa9027f15211cbcb3e88bbc6da 4217813c7d166a1723896362ddf1725b8cb125a7f681cca9c16976f8cf254a17 e71c24376e92c0fc9fcbe3d7938c2fdcf1d5213c0ddc3eeb2b67f66bfb304b94 7a0d7eb7015a44177d3e7fb3801acbfd51a47eefcea3946923edf71808b27474 cb278cafa996c079f1367be9aae03a6e199bf25678c2ffeef32ba4a037c02c2a b23a6d8411ac32a1d08294feb96f370f49efb7dc66437278d2aac44b772c9ef0 e66eb0f6dee033b2528c193f72b5974ac93d18d040c1deac33cc03c92e70b499 fd9851a22e63679151d12abe8c077f9a29b187cdd8ed3c3d24516c09dc1103db df7c25a31bd87418f9ff86b82c2e6b4c2fbb04b17c9974d4e680075244641023 e3e9b583c5734c6baf903e67e4e223335ace8f9dc6d2ba2121ead210f92355cc 821e092e7338cf9e5e45ba054dae3afea2816b4bfc3b39d512b8ab056a79e7a3 1d9bb6466c7603e858bfbb08c0782ecbf0fdd61219b2a252248718816c3590e3 534cc1155a423506cbc4f6b4f09a34fc1c7ac513b485633cff3030ce09f0c2dd 848455ba5176d7bf422a0f7436fff9d6bde7c1b4b2f0c071f39959cd4ac72667 dc77cf7bab51e6cca12080fa9f0be68d47fbef64f676422e8c279ec93ddf6d8d f7d51c80cce6c13fb6fc17d4859c509146acfe1a97022d17d4063fbb3dad1027 96e16f2da9b74551ae61e97dae9b7adf8163758c930905f42549a3bc1ddcebe3 d08d73902c9380235cf48205deb16872d8448467b6f328e7e2a1924a121ca893 a9c6204c0b45ff7028807e9bed996817e476d2cbf3d17d102e1797b2acceee2c 436c13902fc001c23b7a4efbe72dd42298eab028734ddb5723741548ff94f6ea 196a1dd5c4a7efef10992d01b13cdcd31fb2cb199005222a750563d12124de84 e5dc26c454a876599fdfba9e9dccd11277faaa6f1d4714e6b6e593d91e346b1c 15928fa73d7d3c8ab23cf26af16116c09173cca7718fb5ebd5f62244df741f6f ae0be7e4a8b1a126f20af574d14dc455acaa7aa47a7138ac0bbdd8968693a76a 55b2ffd1a84dee82e6b7ea7fa2d1b25f047f59f989aa5b567dcc45f306092b4d 21d2926312955598b790d970d08db1fd098b3900b66a0ebc278123a83566f3a1 6d0f4bc4fbd5cff2ab44736e2b1273aec26f75e8526185a72d929f8f8b6dddb5 ab3d7cd6582f42b9e57fc9dc256759d5e4256076bd1ebed5da6756f120c93936 9704e6a49bbce66e13f4deac1e29c0b5a6fe48dee64b314e4267f5eafb06440d 60630dd7a5810009d92bd97045dbc903965b3a6c958412ede44cd0e460d3cb94 f65356f3167392f31273941adb1ad6e774c5c973a6c3abaccb4616b9c5dbfa72 f458e0d0649ffc509bcf9043c1484e4268e2471e8211e4188986f95744a0c77f caa44649a5dfd24ec9e4cf9aef5815cc36ecc27cf78e392f415da04521a24301 85a37e738b98148ecb2833f0ab3476f7fce92ff07f0e46d2c39c151421c82e26 0f04c44056eb58c3d2288d2c40a55a8f9d3dd949503d8474caa9c60c2864ac24 3bc4cff0f919607f5cb038b089185956db73f411b908e89a0bb92a41a3f3e5c3 c1f473db103c7a9ac74ad70e27e1d7365683f0f9a5e309728ac54a38c7189f9d 7446f1b436fa23f1fed7fb9c5f82e9369678f31eba5913868e965ae0d1027830 030d2b06c01e3a06770901188b78e1a4dd73b1a7e59063888a5c993d24263614 7186e85254806a302a9f7ea3aff602c9744690d662dd2d1c87f9b84d108118cf e9e3c39c847ae1602af1ec43c174b67075fe1b63c674788721b0b48d87ea02cf 5fc613f60730ca096b012ab30bf3cb7dca998ab68e87cc685839f1596213f0d1 9afaefaa590319de1df2334768b50f2c607a4e11f754103515ca846d961fc07e 6fd444ec64aaf3221416a929639eac2b064f5958e065e425326abeb186cdccc5 d4bd8c6ddbe3383430dc5fbe66546789e7c189e218ce130b0e34063f586818cf ac4965aa222339f3fce6f7abaad151dc7f7f529052a5ec867de0d04b13b6f41e c616d8e8e448bc5f60ebc06f4df7697aa330d28665840ac38dad2eaca41eb3a3 160869dc7fc8a6e4d0c2490e7b312f65f74a9ebc92cd19958be9331ada1b96da 4e0d67d5c1cfc52db84085e81c5d36442dac1925ff5cbf59e719ffaf5b715889 f9dce6dddf06f2974de443f74da30a2b35352f2ea86649c4358a24ec4754545e 5f5f28ec62af92aa634f69afce12900ed1d60301217474493e39c1c2dd6c8394 7de1c379ad2dc2e026e718d47b5599e6da4633f3facdbc4561137a3921347131 21c6b9a2120b74b22bc5d0208975329ddb15b1805123112d59900e3b88fa149c 0b9f34bd1ed128b75dab750b52871b33197767dd21cfffa76700d2cad8a173ca ed046ea16155b68e5a4a0213ca859b1928c26b2058e69d6ae1bf4f252db44643 474738ab1fe4c709d25ac35981b579dbd5d6722e72698112862cd3974ac58bb8 6bf3c7c7c6c88da4595ea2d821c40d265b7c38cddcbbd63c1ebb6dcb39635f9e 73f4be6eb1ae495ac40bd087d7d0b4afb7850874bba11bfd11ceefbd36fc9e26 8c4d4cd7f51d3f38bcb0846b2b47bfee3d63317b8c7db554dac6833db156228c a1e88dc65edd21228e71b5e93f5a037bbcc2e3032e2906ec452035120c2d69bc d633dfc8b8afb6b0e28e54daf0a6334327b11c325f953a2a5434007a44b68b25 0f67af098a96b69beaa87e2c17798b4c87a95eb4022b51228ac957d4ec3b1c70 7f7c6044225db5be308e760c5e1ddc9a46415b51e8fa7e0cdbe23981cbd68f8e 0de9f3743c6556ba1468e723606aa4df0649331499761266ab8b1e5ac1166582 8f4ef9abab1b53a8ef51f96a37a25aac6897f0f2465241a51c6724358d220f41 a37c51b91ed02807c0b5f65d0bed027d0ba4279546e670ecdef864de09ac4312 8214589ef44ddaf1cb03545e8cf24af631e1f26109780f56259ba317bfdd696a 90c28e21e08bf88c777b6dbdaeec0e46137e006bce9fd1f81403d3d75f523f7e 65248492903417203c5799e79c56ab85a8a2c6f67d3f253caab00f5e8c08e959 2793d74dd84ee45e4d3ee405566c98133bbb20c83a94f148f85a4fdb8a52e43d 97a7d0348b149a34f4d677316694d2a70a9bef012bda9c7fc014a31516eedb44 8e6a20c9ec2ae8788e2ce7ad8a5c257c22bc6d83ae5228eb664ec4546c0f7fe0 ee7e434b6a428e3dcacd516b46ec0777953e6a7d1637c8793c7a70a383f6337b b358db7323a317bf26257b0a185abbb24b309da45bae6d2c45ef213e0272a3cc 2ee957cbb29694b3dba20ff97d32b68d3d7e02a70cd0a107a9da3f0b6c5b538f 571807e9240236fbdfb92d7860462197a19a19944b437b142644346e648be33c a6184f599597ae3b4b913dc29c8984a810d450f1f1416c3686718ee6e6b306a6 3a28354acd88a4fa87d6b6b4295b782e9c39c31473230c49299a2781d11b0427 c4c4c525178a5d63d8b80d1de4b0d16e821cafa55117a8c91784fc3455b9bc53 65fd99c4e94087141b411cc00fbf7cb76ec2e61d88a6b1407c0ba7cfedf67747 2f53a84bdcae063bc0cd717fa7594685764993d556f9b97367a150cda71f18dd dbf660cef280f58f47ad5758f9e8d422e6640668a4d07f59135eee5266fb87be dd56bfaa19e2de59b11dca789fec8678910f67fb38217555691416100aaf9b8e 55bf0dc6616ed931bcfb249ac39388838b7e06a1597ace6f466c8bc09ceb7214 2ecf6a7c24ccda0c9d6a1574aab79e0386d2ef0242c23d31be03d4cfffbd4ee0 6018041ed75f1ab1354ebe9dccc6e5c9a1bddfb77a569a59af7ae394cb9a895d 5b86d0fa06a81f3d33b860ef2e77e52e2a602d7d0be993aabacab3dc4ed76069 c18d25071db60fd48f2b3f49529ff080b6a7680726b50995bb3110746e1dd462 c9d4689f2a4ca8fc8950f92028363862ba02a12ce681a5447abe9ba567be64bd 778ac7942dcf66ce64aa03686b82f981b074670acbfbdd9cbac835fb25af8002 b7cb99f3dca2d0457dd1f9bf33ee3455c90fc7d32333db9af6d6e7ff7186ddad 738bd0b5f8e22905a5eb2d5bc34a4fe6c6bccdef95f7c8d166795299d8aa1e02 cbf586402fb1143843ad214b8fa2d7424a5ea30c1c0995ba05d84141b58ce693 09b7b06c1265a5c89353a3bc4cacb26d1f888feecb946a17c31763138c9f93f4 baa54bf8ca0807452952912a73b425e3496440b041573d5075f9c87dddbbb9a5 138a78430ee47ee1af2b40bb5c2ec67b9522a01520c55551afbc52d6713a71c3 0a066d4d9df44601a3cd694dd84d7f743f97fca6cfdf4cd781220145a61a2b04 c7709d3c6de1d0665780b32943f9b1f10423ca73f2627cc48787668ac26e5d68 be7df86a7a7751510c1be4543328a033315f57ee05f9a96e12c57f6d8c6e12f9 6dfd3b3c6af97d088d87137a62ebd906b431a888d474f5921e23b2875503821c 137558226f3b9f20983597d0219234473f072258ec2b7408a1a0a9342c0852f9 0a86446a5c860f0e6b018ddf6ccd76972e229635de692ca2d8878ebcd57fae3f 1d954e023a7eff28281a4c19921e2f966334274c8afaecb91e0bc748ad24c368 4dd75f6203fe6e9871c77deaeb61ba866008e25054cb4add093f19425a3a9c7c 709620647df71004103568d54e1b12d95aee16a0ccc0d34b015003420a39afe7 d8e5322189ad5d6112dbfda6b8fbecb0b0800e34eb542e85de5479cfacc99b69 5563aa95b21d03cbd94a018c173bfcd7ee4ed535eca04dc415243e840f2bbff2 25d48834e85ae5b93780d1af82c93b5cd628ff7983e2d51ddea50bfbdf10f869 60baac28b614d67c426162684be2d2142dc44f67e746356707756e9ab19a9278 ca88d1dc3a0e92a9bfdf3a5833343c28771c08fdcfa40b37f36fa11fca94130a 91e7d8dcc55bdc25b746f3987f17787b388baf92428f7503741160814fd90868 240edf67ad2fd4f1fe866ca04e200ee104cb2405f7531d31df1bfdd2ef632224 ed3d2c90436ea49f44995acdecdf610ee8924079f247fe67c841805b4a3777e5 0c58409ca3d79eb4c73f165bd65e9d07fa458184311ab4d733884aa31456be6a b03a12920d54a0e4b718cdd796674fc3b3f53122aef40d8959aac75023826669 7886789162d0b8afef75af0eea969aa765d72991685b401014802208a23bd093 465923d4321018cf3c3fd9a0626a314abf24c894842013ef81afbe3d2c3f04e4 43017dd73196b59c3c0bc61cbb73311fe979a6dc2e6fa072216007598de168a3 0c2ec368497cdcc5ccccf642a2fbc529d2a6e64f4d479a267d127477b34d8689 e78ec2670aa3c95d071272a384f69afc5383b890e0935c3fdf2932d764d441e5 325923f56390ecda16d53e3bb3c531ec1e992c833d241f5304611dfce59b0602 d5b6c141426565cd31695891f5bb037f45d485d229c16205b003525e39622f6a a3fdcf1cb4485832a1dff39e84e007806d23de8dd9ab17e18ec4698badb21166 8664653d52c4a37f2876eadc9c110590d44199e74875c44af8962b7a2feadbdd 71115529e8c9f2a6adbcb0713c60412abf74d6d59eb1b88c4263e7584ddc4299 1ee07c7c6282887409ac6976b045df5292aac30d83f1c19e4dedd4dd90e93661 0691b0788f29d59aec16f147710e7526d9564976efa332891de220be64af2c81 49b050a4e834adc8d59299ffc7d4837623dddfbab445089b4785dfbb5ed1f47b f97dca589b6d43a2c1ec098ae536564d0f930af6532dde652c129b4c5acdc843 b4c0d0bed839d37bcff0347b1383e95afdb71ca61acc749eeb2f166ea348276f 78bacfecb211da74c9b1c20ae8a97060fe0e4b90f3457bb93349c6fada1b0a71 ec950b54ecb575b76c352326b3462e8ce84e2570e83c6bd407bd589a84aafb87 56d217a4a8ddaced4bb1fbd734563701ef7bd4586cbd29b923e4bf4dcd08d7df f2680dc02f2474d75a467d7862ed2382e8e97b811df74d1c0e588af02ba99774 d7613ebb17b5a4172027b52d88b96b37c632f3211d52840921b9157af132d408 3a140f532b5348bce144791115cf2fda93eb1d086a9c134ecf46b7f089bba313 b11f66f6a681fc7426aabfebd28b9dccaf4174bf8f6c49a8cdeec6abdeced9ff 42fb8f63490e06ee3758db2c134449cebcb20efceb49e20c46df224302f75213 fc138321ba5a99f0efcb4d3bb6a772c57f8b40218355c655ef198041398e6be4 7b6594d8a1647186b6a3398b7dbe166caabec965f6efbf6658643ef852a217bd 7ae4504e3c3339ad3991830818b8b1fab87855232b8cfd9b38da2a5d22a197d9 5d88600fd6147340baa98fc5ba07b60c913cd444fd0d4faf24f5895a6f2f747c cbd57106c11aadc7673c048876d6743a6f7f1ce6edf75d602487c56b6755262f 3db8752d1f354d7eef54a2d5c80506b1c6eeedf20f9732acef228caea5e2096b 45e39439f8f8aafd06d89ce071c38b16ca6360ce7f44e1fbf45656d9726328f5 8b1812f2b933c1da5f5769a1977562bc4c4e5922e83f43b9a3b21b10e7f037e3 ebea76b72b6437ccba2c8bb1a24020b5b54329921b09ab8bb08b6e6d39db9eee d93783d0f146d480009e5caf01f7db43da2a03d38e2b5f6ff8f987f859c53dcc 7232662b995e9dce961133a640997b383c493caff36fb56af22c4418a499d479 fc6618fedbaa8e8cf54bd1d1be9d29a789d0eb119ceaa5aa98a26d2a852a6aaa 2def9f49ac2be0a25176b04c4b13628da5c1c3aac8562e9b653332c1c000d57c e2fd0349a73eb126116e0deb546a0c582829e03b5ec01132dd4a85de18bd7b28 0b44b3fea78da4aabe95d00d2258b6e71fc09c5423beea4407646aa4cda86233 254c66a19ed9dd921ba4ce11c7e02f8fd475919865887d624cd9206dd56561af af3bb93bd91900bf9dd8dd8ea738c4c4519c0c1451fad84b1029c7e5a955a212 3a583f231cffb9c0e76d3f8e2eefb3a5e60494389ed656907e2c6f02a7951f59 14bf6f379c75c5cfd1b380518839883d8e5f3585722098bd1abf37692d14dce5 84ca97cad81b9fe834daea590241fdc94abf973094186de4cb70c9f08ec92d51 43f4ca684f848f7db80e80ffa661e68bb1d2c2fd7cf0a2adad234896bca8b25e 680bd6d26c189f8f8e2e0a84d556277745a8936323a73a89978c396681442514 e905207b1e35ca7663a08c827933fceba994d7c7e37728c0442b9340734f5c52 d6ff465ba039d6bbc846dd14d52b9095df753d252f19ff35d4ab222ec0937fca bdb9c386c5d52605ed35b936c575c9f49120b5b657b422c9ae7da87d21fa407e e53791e30630b876c5213589314daace5c70d1a8bef0dad54fe00146acfcb92c 5106fe25c6b7232ae2606eb8481728bdf66f9c5f178009a679dc199967313993 3b67a24ef54bea396a01613114970967277027f2a5e8a9c1b87dec9c2f0c1d81 6638f3b5c4a1f236728cd2b8ae782958df6631c5f37003f585e002516cc8ecba 1193b7a2eb55daa6fe629132656d168059cc29ab5b0a366188d13aa7ec0759e3 1e1af8739bd56ced859b5dff0c09ca204b2a9657d440f76b670bca203ad18a6a b27bb6b3b2359367834d32ddddbbe4a23795961e67da050f5db8f975d53ded99 16cb5a45b8fa3c3912bd464b680fcb85c978efc3a1ec9eee10d7c897b04036d7 522163c8f01299ce0aadb7b940a8bea7fc84b07b4f39472697325c3755bea8b5 0cd5b22d611912906151173a2427001b6fc2ae1931729a33e654d06b6985f778 140cc1c21c3f4ca3e5353eb509f3309cbf78344d77e3dc4743677b08540db4a8 8232aed9e3c89596e9713ee744899a38f03ff759362334de4de9b446f286a43c b80d783ee925906aca537c2d30b6d8a459bf198417e687abea747aa4f2f9ef3b f2a05829f907b58a861b3b5a84c3a749ed1bc5dd3793722977c96477f17e1d12 61c3691dbbf8e36437fb00fdc9e5bb04e15f17be4d3ab71059e1d1509627b0d4 05091ed6bc78d088cc017c623788322c31cdd83214b1e0d0a02be3a97e59b607 2646785984d294ef902a1d3026c027ac2fd4e9ed20d45f54257dc98fdffeae95 ec02cd768dd9d9a5ba809b9460e3ac8411faa8ade6d189d64874a81ad150f7a7 ba507b82dbe856a07da988e867a273318483fa8518052b0d6c270091b0722b1a c57bb9ce24446c9ca0b914f8512d51ddba4149909610fc7008837cbfb4c87f82 0e2cdf8a97a1464ac6ef9694e7c4d10ff073e5e6c73e26149c4f54df3021e6f3 481aaa64c90b7465c4585598fe883a27a7d430b293975cbd9203ed018ce45e39 a57913c84c04ba6ff65e7b3715e40aa41d47d24a80dc14d48204b797e43f2c48 d65a61ffaa0a6a5edd78130bf978d251766e12130381bc7f4b49e7d2d6143ae8 364406f26d728080ee32900ff6d3d872f660b79c7550045112d3a1c3423ac6aa e1f6915d71bab21afdda23c2bcfc188967eb07974b2555764af1e63d907f5638 436792938f54a2d66c211771ae132395bf49d05f99ba64c39367713d0cfb0b39 2ff2e2af68684dcb7d527424e87a56a7aa4fda6e35a986454b471a9fea6399ae 40de20844e9203732faa37c98590f0808e272c31bbb0b293bcce28c30618e5fe 16d52c3e3089a8e554323dccb08dad82b14db40d0e56c327c6e0906fdc3ed41e 2f592b39af6e25efa81f1d9205da1480af9c4cc34c22de4b61847ab0e92b16a3 c4fe08477419429ba18699e5e60fd76bd838616a3c4c7d568a27adf543878a99 01bcccb352ed4310fa9590873f66d1238013d1867ea7ed3cba2dc68c5624ee08 a525c2d1145a4e5643086c1079e93ad798df17bcba1525ac953779085f12566b ac91a0c801504fd43b5a7d181cb5ba9ef160c7878f669de7c0b13f6c090f6bf6 e55555e369cb8b90abc6db22dd994738fd92d892f9ff4e729f7934372013bbca 14b37563a73ec29a1fb4e443e7d814b3a008bb4207a02860d9acb71e667f4e83 ae0420b693e272517486bce77c60ec63f0afb005077ae993690af2c58fc67cf9 434777a236dea7c76fc10637f94d7833eddafde391c5071902c4d25986095b8d 43332fd41c1c7c23fc477c783a8f8369db13b36ae792e115e096e36cca8a383c 64071f8b8c3ca4b489c5096ca20764b03ecb011ca3d627e8933f247efc58e236 4f9a0278375735b138ee1ac40bca5724aaf1ab3e1d0b878642b785a71d493e9a b23c58919a5fd7a4dfb051b181f0a470396b87b6d2b1ad2e3651b0ef7de1e70b 6b3d8894fbf17c126305f037c325843f2b79e43e40491eb874798fd139efe754 c2cbcee3edd0383fcf7e990570d7daf79a5c169187ff32256fcb0e2bfb9d530f 7408c3c9d74740412c9db845b738439de8c1494b1bd3d10a7976fa49c227463e 3a6ed51e8f8922d30cfa4af9eaebbf49f765b684fe433b04eb3cade09d26059d 2651e61797b185fb783e495da71badfff845b6be634240bd964b921c4d732f24 bef244a8257431e0e4ba83d2bd436f6e82ab17b5e965ab54502a5d90ab7a6e29 6a751324a15acf9002a11de98d02c1cd3b39ae37b5bb47b755311b9a1efe1580 77511d55640cac5addad1f538e4dc7a7a918c8d646bfacdc8e116e8cb4c46870 7cbbd55062e7df4ccbe92fb13fb2a900562b178f594377b26a2f8c4776e9c192 a4ba44d58b6030cb17aec59fd59ad94e9c6f40a0d1715e064c20768639c0f11c 6c95b6a24a446452343b5ee7c061ce6f34f78491874e4222033af4124ddfc641 cc9b6d13c3bf85302a5ce11064cade150f9a052d89c0feccb904d39375a41318 b368bcd6e750de7f60b29b18fff4cad40f3245f90ece94193fc4844c0dd07eca aedb8f58a064c0e7a2dfa0f0d6e38aaceef2454f19953a23b51c88273a7b6ace 64c3965f146b2923804b3fd3d53096ee3da3b188b53395c6a59e4dba2d3ad540 d4578a31c81eb6e36dba3e5e18dffe15e7d4b71b47052029b7b6a97dfd615eaf edaa67a2dcb6be354c94a3412389a387c6b6dbaaa0f59ee9772e836cf3da2487 636b6fbb279235debd8642b25f87f38992a930330b8208d92eb7b71f87e82a83 3c22934d15c2bc759e96d506bdc3e7bd95d54d88ce77a7b89656bb3f1ce47f8f d9af598644c91662621fb50fc1f85691af870c0cd8cdddb563cc7b192298918b b4ea0522e5a29fb0865949b75220b0f2cda3367d0a042414ba6981fee4798db7 c6c5f9994bf6f1225c4e503bd2f4986cad4a340e4c81d94f18da4724a0fd50c1 277c8529bfe25e835f3ae06b1298a5df0b02abdb7a5cf9b936e2b9ebda9018eb b39bfa30ad33d0695c1f5227edf2a8b2820b3708dc0878eceb583c4e5867ca69 c9d2f6bfc69000ccd21f56523157425f56e82c20a4040a1ba02875cc1a5362b6 ad86275615f7e9854ca5c6c32d027a3e7f934539b167a8567f6b72897c90682f 782e955dbe5cbc9759b8aa8ec9b2dd2c6d89e33774fa652a9cff5a282e45df2d ccf5722aecbc5dc545e850946b47c4b7b9ee4a03a44fd4f49a6fc69ff6795a31 362f01364893350e165c8ea5aacb3a4bde3cc5a6170a06d0fa0ef2f81665565e 873b418323ff1496b2f5026cb32859b8afdaad425d718cc38e5826412fc0b9f7 27c402a72ccdab51d1d97467802a94b4b6f045dc04a3098a970f07bb794191ba 53c42066d3b91c594432900eaf877f89d4cb881a325150a4611d6f2082a40903 182dd07e3b8a67c9bc47a6a8de369f39d2dba5d8ae37da7bf8a0bb728a7eb331 eb4cc322e6490925d4c766787509f5073d3e66231499429482948e60f247f084 e754816f7a7681ea75846c12ad2ceed86be873e3ec17102e7d3b99e5fb65f21f 2d29d99e11bbca86255dd1615ba2afe476b08083fe754027e85462cedd66f522 5414a1fb3935887d0da9ddf4ee025d3c07f5b261ab469c816b706f0c2401314d 7c30b742b041b8bcb1fc415c9c4ba1c3d400d6b156bea3ec266a02b8b24128b5 a9ff6e8e9675756816c13158563165ec9f33574495ed05dba7a601056b0fc19c 15a1e54c7876ab55f2a1a2bc6783fec3062bfcb74f41f6dc7239c444073221e4 e25fd7aa1b951cd6c65b16feea627404418091422b4880deaebf1b1b74a928e5 e087d37360ac44d97414827a9b7b6c060bf46505003607d2ab6d0198b1b9624f d34a6449649c711dfe5e1f9ab702f6e2e612bb49dd82a01874bd5b1322bf0ac1 9c142b3ca7df3224ce2e0c2d2d7ac0e82667a7d110df02a44e7a65217ddaa31e ecdbffe237b85e71bb6373806704690b595dddb07d84123380ea9caa5d10faa8 4d61f868724a6bea3e7a37846f485236f74f5cf10d280c836119ac9a94e4bad8 fb979b977b329540b96478ad6300b300ba275f25ced597eaa817980dbf20b72c c2d687efb04cb2b3a6637e8fa39c617d347ac8153d760f2f59bf111d8f933f38 01b7cb6d2de2c033f104ae6beac9efdef563baa6ca9512d109a2436fdc2fd623 2d922942d3617eaf92ea78fd6596ba536910c61fe87a57a5a8c6b93513d4fb92 246d42d6f77734aeb549ec9a667d931c00afc7cc693bd5fda7d4d953fa37536b 17a5f026680db05f2d4dd6a3627e5708dbc1afc2050da701b1ec81f1bfdb68b9 65ffdeafbca999f45f47b40be18f3e7c58ec15c5cff4bbfdcd190a1889e27a8c 1b9e80e126f83fdd520861fda30182d767d7a65c75be325fdad27105b3fa1f6e b0f6e947a8d687c8e752119346db98b9f4664d7ef7b34bca3c72288a94818db2 3ff5f75c5659a201c6369210b8fcf8ea0f6bf54ed3ad3a2732639ba75153364b a239e6257150dec80d9d40c9ca632dfcbdf2285960e9f17859c65c2e5c5847e0 8c8994b47c13759f70a74a77d13858e427fc0906beded3c34aea59c9c7400271 c96eedb95edd77c96cd87077dd4fdf62efe201a6210e6029586ea6a3496c9a20 5c150656383fab2391c4f3a19b15962779be5075fccba9202194ae8bb0f95544 54955a0d371402c1ed77da343e05f1ac135e983a8355b540d254ff464a154f11 441152501d822e9aa41e2798d6237e03b12fd80e1472f9cd58a3de175c8e693d 45b5c43de816226210deec44f64e65b241ce2903a09b50d6f045222619f8aadb c73e87b7c319573321f8417d62b8c457a50c532d17d5e1f71d38077a3c373529 658d40ade13f684288f198ce7ca6d42bebd7a416e41ed78c354fca753240d53e a9ca7ced5a297c33530575e3b78253d4b22b56b5ec1c57bea8f5bed0c669a350 8f7b5df06b8118ced9bc25f8c74c9d88852c9697c1003b888f140ee3d53f22be 7b8d2e99635bf5a07c91851dcdff31aa69e014f56bc9b4468110cbdc55cd5989 560cd4e0e4c951c1cee26a8ddf8c020fe76540ce13991dba10e0e348622c8c09 d731c4d463a1e113b1a5f256079b1d9eda03efc92a75f81d1089538dc95a3e09 97c012e2aaa5ebb4c2bd033b4f687bdb07dfc9cec72bb0ad85f7843cae817071 f1a1c18746a5204a9c21ef3e08f65815a82196225b907e62f1803a30875d05f0 d977687926040d26da1be397c53a3b894e2f2a42574180b900b94b130d18171c 4d38ab462bd23198bb7dacb76e2c2206429e50b637d2c367104f23462485c0c3 37bf1b71bf87271b5db1c4ca5d50fdd0a8d57f1b781182131a58ef8ff9141aa1 a532067e9f7434860b3b7267b08125e38099e1788932f375075e4b3b42b18472 92550607d20d40b5fb705d7e57b0406a0eb9a2eeb4d25d1e24efc0091870d9d1 c20fae0f60b2b01f46fbe082bd066a504ba734f65891aa180198e508e25e80e3 66ce6c1e59133c154187a12b259d32bff4c44fc929ba231c3dbace69e2d34bbd 8885be7993c6917e86e88aa9e747dd4c920c7260e32a22ab49fa6abcb1ce38f8 d375f7d3987ed59038cea4efc577b4bef8e07ae3e81537a3196f6ed559f53cc3 e540708d58b13ebfc1964c2d01d123a2cc274f3d16c08b04ed6ac7324c7fd0cb 5ffdceda8d2967dd5f05a7b6c95ef0c98ebefc8db716ed0fe5ada2f8fe5a9f5a 22682c5a97e5bab24b5ff1f50afe9c7e6adde9428f2b56c0f24cdf6e90c13bdf 3942a08687625a9da70425af766382b23ec95918c64075ef102010130c230e12 7ca8a2dc3ea560b78d023cffc12571866a43a8daff7e65f61c75477d4a4fd9fc b39621ed831266454f1ca53dad3c49f1d1311ba828f0639f64204bc3309b58bb 761ebf69ec88a8b520667f158636afe49c3fb3ae2884bd3cfd244f089800d871 ec6875e31c01558945155906ebb64d0c30742c81ce1c42980f8a3388281d340d 8b6a6eceb75f314035e6a63ae3a2815890890374d41761c565fcfb1bac53f2b0 71b9d7bd56a346f118da924882ea753ff91cfb76ed522d4f24b10e228e9a1170 b1bb88b4643e1c9882ea8255b326c2a708488e164ef8fbe9eb03372b75ea4f2c 91c9955ca4cc9eac844af836dab12c56c7a6b822b6c4316357ec806e3f57ef5d 93b2b66a18af6409c97ffe0f14d7bf0e88e891008e2c587e14a1f1b24ae215f9 709c9650263e04753d988012c4c4a9d5b2ccdc76e57e0ab0047449a97ecc1c38 08868c5376faa09e2cf59e9650c14502a29b0bc1de5b9e4b0078fa036836a174 dd75f87b1c22c76e3e4448f148da27838f65f5ff96a492ce07b0e2e4852d8ea7 bb2fb628f604ff2088d68f746c037df4cf107e5b078fa44299c082d001ddf56d a6077e70e4f873076d5947477f262b59f8c24fbf6d785552f1a05ce7c25664e2 32a0469c62e4d3e379a56072979bb16b362cba978699e5a8ec43d2aeb6565366 1e405a6184f8a556fd3c1740155bbb4363939255c52c1b12c0decb4da302a34b b36432bd647595f785972b3258e1849e4adea00461f525b72a363e59e13c146a 5f49cd7b16262ec251119bdc9dbc7e2f34eb0a3aac906991f81f8f770cfbd4b1 b7e47622bd49ce453cf2f643016b6026d86a5266d49f7d3eacfb39b8390e1d6a 145a5ace0bda59ab2e896ed74948edc581b0dc012c21e0d468ef1c0f0d819fb1 8642d76add68c26b71b4f5a2a4fbb8cb340319f862771b92efbefa2194008d96 b38b8538f1cc0afdf9cb2e15fb9eaf96751888b77d81421e4b9158b8ea132088 7e39572f465ebf71a4f1990c454280f15b472ec492ea9ae57504503ae1c9b3bf 5c2529283e2989da4d524635416dc50f62f44d45dc279016fb3bd65e59fb542c 1589040d4bd9a28acb27b117a4e9bffe0452507eeabde439ac4e868ec92e626f 6651daae7d73dbbac45732b689aa7cd66275f75ac9971d9b9317239cb387428d 1f5b46ccc46f1d1c1616e1c65b727ff74ec859dc01959d9586cc2b4d80dc367b 140eb4a0207943d080066c51c2df443161194ba39d43dc57e8da88e1a3ec9379 4ac8000f46514299fbafe43c7212db107174a0f006cc3a97d8de9018d3573622 43106f14d82267aabb9adf299c119d0ecf326106c19fedd7860ea9556a7b8506 69300d0e1cc2d8bc2fc2656d2bb138eee763fa879600e8bfa5e509efcd7d0c7d 200351ad074dc9499408e8b6787efc5a08d1b6a1412718baf5f1fd9ade0cc361 cc64b5e0adede4a60d14e6718ed261e65df61e9f9b67091598a59680c1274519 f763b4bdaa6d53f9de8aa121ac66ec7d886367e977d10046c42055c7ae460de2 d3f13b8840db0dbc7f7572599d47251458b5032e953b4796f720419db1b6f64b b59f5fba87d6e410d1e70c108964e2c97ee915920cbc1760a91e9564f49c323a 3fd9a657451e327f123e502d0af085a472382bd64e6e1218cd5def031f57bf3d 26ff59cf7ffa41aed1942dfc9ade078590f0a4df37584b416e8a9d95e67eac76 bf7dd5a3fb1dba0335e83ce2319ecbb5899a9b1a983c079696950d962df5eba3 b445be5e3281d8b1f6f9adc026449e5ce635c6482d888ab77f750c44b5f40e43 ad06f005da5e8ec062b3e8759a709c51c31ee99c710d881d85f5babd582e50ea bcd845f3cf6d8b003f65c3166525d933b13a204333c70a7ceb7118d07e28b204 d98461e4fc469cecbbc3c2c5ab3867e948b268ab22c0f6d85f40981da7e776a2 903fb243a7e7e189fcc2245bbc206daadd300829642125a14003e5590e4b37c8 dc3c74f844566def7db8e6d3b36d3e938d5b2ef20b954dbc7ec5d5d995c5f745 c944b5fd77e9044598a5a37dc03a4ebf6068cf18b0a286b537b7758f3a98ff49 4b8153daabf36a968847c294bb72131f46fe43e315f101f8d39994f17f18c14c c4bb285eb77bf0caaaa6c0e9a6dd1e13a76150d37514a783b43307c54be1b8e8 8e4bdbbe27a936612c2d2ffabe24ab5985eb4c327528bb1f806ce00ff0bd6e28 813346a0140f31491a8f6e110e3bf41bbfa3a4eccb84c3955d2f153a10b8c54e 9307f5a33d3d076f10602c7e3a2b400109c59a557cc4c8eddc05accb7f276414 7af2c6b5c4da69863d1741cae10be0fe0f0a9f4a7b91623465a2c213cb131116 bfc57a60265bc6b9aec3c3a0f734d342139e2bdd765b6fdbc5e50494b8f9e1f2 5d555559987fd9db823c1c09e282afbaec148ab42bbd82d708470be7707382fd 8d89c4fe9ad754befb2580be9e838985f69cdb4324f3c083518b4c894371d8de 9dab61f107ea1950a96f6f6d1870c44804d617ea948e58258fef096a0c9282f6 530cd6930c8fde8a39fd7f04abf987b8c264106d89e9e81942c9192e7bb95b1e fcd3435fdd1c32f6251eb41ec12dd5f8da0c93d06b4179d0d698000ee44e92ba 33cc2e46b2789d522f96b4ea50dcaea2bc4b71005bc062b5ccc844116da5351d 17fecaca30e0f61301fd3a42e1f488fc63402e2247d7256c25abc623448ee1d9 2cea7dec7e08776b47fdf7c5293092abdbffb27963e5e2ba7f7d9c124d14aaa8 3937826d7efbe57ac975712473993a96201d29cfc0c05bec3235f15e6cf73d59 9ad26bfeb53cf47de827fa7de09bf740db9912660f674363c3be5b7633167223 ee231a9e6b1cd214809c2191528bc8e6b38220a83c269924139fb27057984ca6 0aa024ce96786c781361a1cb27890107de268e1200633edaadc040371bea7c95 50aa649d296e439268fea231723c8c68674e35057e6b55a4e7362f52a593078e a8715213618243e44f11adcdb8b85c239b66cf07ae46fce5285f258b44666533 afa9f3ec2114d4f00ed582f05d88cf1b4d2aa0b774dc91ffbe6e0a2404126928 cfc82975cf290449bbb84a87712030f84659f36ef60564c236570e67017e7bd4 db059afa2ce6ae83671dc0e696a445180d964cb90f19f7d5e95c93d59eb8c445 a4b12fc26f9252c95070fed56a8c1be89e178a1489545187a10596e050d55bec fc199592b819b487fc88b5aac2006e49246127ec04859b6c25b7c1451e598b55 2de33f70b669a26741262c594bd0e0db45a7d194fafde946f7c7cc78653ce925 58d2ded27c719c49bd954f377fd07aef6c8311f6d30129ae2b72fb99af54594e 95ad4f441f44142204688b72d00a02c5094aa68856badaa6cca2a2beb7efb394 795d8dcfc0bd64d7375d1979411383365a49a7460ba7857ed4e0b1183b433f7d 32051e93885476f2f07cbacb5b77ac6c823898db24dee267407d3e91b17f13c9 5786778e498c6ba8344e4e65bf65dbf0e333082e5d149640f2e602afa454fd51 60d222c5a45c308143a99b10d1e78b83217b25eb122cf5e3af4472118dff034e 5ded3d83c69d223d2fd5adc8e8b87f034dc7ca5d59442e28855d617296bc8aaf d4f1cc9e72579c076723696ed84c02c203acfb359843e156ffd1601645d505c5 331849605573e7bb104d432af91a54f2701fa6ad99e8bea8fed85a082e114dc4 962c109f1c17f21f02dfcc67197f366089aa0b5253046d9ed63618b038469784 ce0770c4feb342ac6179418963e7bb2b3213846a9a6e0b532d69abf3571a2d90 134c4718ccbd600e89b3038570e64c20f9c9b1738b9c9d9d841033b3ac634b9b 6586f4adcfea701745a4be223bec1c87e455b5155379030b1358f60c266135b7 bdc2bc334de79ec59882f2017fd46e831626c6b7df49a916cc52effcfe0effcc 59dbd668d82d7bc7e94ac2b61f5acc796923249ad445818288754d469df93df3 88b74dfdbd32dc2648c327ffde29d0baf30c0cb1b3673d112898e25b97a27a09 15499e1f4c0000a2f9676f163aa63d7baa622876e81b41e46ec095fc309d022e 7c912f71310bddaf215bb6827e2f1ae04e9467e648d79a9acd4577c9bbf5c648 46e1f20b455ceebb5945ae3e6cbc6a0259d3857031318af426efc9e648e28f48 bd1864e0533c49701f4607cefe80cc20b67c90a6bbfa2b553cdee2a6bbd92b22 41639cf69411f5551c1c0f8d22cc64496d126abcef1a5b3ff2998e1377c70373 c44e4082688b73ab4c87ef408ef6aac23e137917dbed8c31577b3aec21c1f365 6115b95e84b12b237c3f10fffefc5f08dbf91c0fc3820ad674e5ec69ffaee780 0673acc88e60b5b59d637fb36b7acc545e670a1184a4f73d87818a894dd27c68 83ef8afe34fb4c1ebd143ae81efd17c7953942d80a7ed69646da518060cc6481 cfd281ac6d3e760f68e108921a417bda8266d59761e4cad22a49712d79205cd7 db35b3b8f46ca409ccc27579758cc59de8b31b31e671c1fd9dd57e0c5358d68e e587c4a31456c9cef60c546e693c703935b5906925279de926a23e8c08b7605c 562ad12f9fabee529efb2fe3ce37ebf6389eda1f6f16252c527a7dc26d4f4795 9f44f7dd17eef4346f66ded7d48d6e7fb54e0599cb130b3b130cae9098b6c8f8 cb439e585538a8479bcaa2aa232b67b5f8eaec118f5327cf75bf3d85823c040c 978f59e58660d4cb8c7e0d08dabbea16e9d0a2ba3ad893f260740f533e8d3271 759637e4a935839cf0b77e754ffee8054181053e3d419ea9d1f1218b56835e8a 09815f5531fd8061323228f7a47e374753920db6eeab3247732f6f86a95f29d7 863de4a5f2855cfa2b6ea04775f8e72992ec6a992f340dd71f99dbbea8375aa3 2dbf68bfb67626aa332c1a52f494ba7a290f1f6058728bf317f19614925683a5 a087ff31b3418ba39d5c3758ab66957f5f8765e6bb2aef6050e7a29d61f158a1 9bdb8ab5896aa22465dbe5ba044b5774abce906cd3fa7e6d9c0fde9c728cb8c2 ec79b50330db677d8f6f6d91e05f40b5a0f9b2070d839379866e84a4600867d6 68198681684f9cb178fc833a4b2df4a21e1a076fa7f1a91175e8ab37672652ce 5bcdc3c49c916470991ddfd54a50ee4fba654da350df67afb63a212355c78833 e575cb6c3df2dd21cb983dbde6ae99bd907a36c75baf3541037be7670622d091 8c91762928ee2da2b38bbf8ad521081446a63c492bf57ee91562b1d40969707b 18ddc920bbd57a1779eb1294da9c643b6c33ed5977abd4882328cdc3026def37 e38c19981161d8f2d5debd2ec05658bbaac1b012d9d4eac4af9bb470b06546e7 accaae09aaed4ca145aebc5d2ae250415e96955741d81b38bd520bb5f6901419 d1296afeb278b33223f6292835d43a1234117fc78a311ed96164876eae2ad1d7 e919499a720503c65bfad25f82e2aff6d57bf76f6eb14ed648574b7fd8eecbc5 97533030a634ee514acf43b7f4e797f27bbdb007a9731cfca3e601dbfe9ca2c1 5fdef289a5694dbe0247c360e07ebb4ccc544813781900fa358145683b69ae4e a4a0cdf52f2f8df2ef48982056abb85341bb0e066b3d326c7b63f87fb9298f18 be889f841ac541608887d3265c2063b6c6ee8895d66a9239df06ab1999abce0f e3c748e070b03b5ee91523630e91a554db671d44ea0b02c3e96eae243fba6536 4394b38c32889eb603fa9702f4b7c9f94673f4e8773e16b43a1939b37bc4e591 5ea2b1bf9038aae992491d09e6ea7ce5eac48324409d63001dbda115ee0aba8c 21356a6cbc33d5bafb98a0adb813f6f17c11d0ab92c46a29748d0c186bf945ea 4f0bae00b028b98eefe51ec1ba9cb4ae0681d4818a5ad689b19f7786872dc264 c2fece1500de1618e77f04d5aab9a2a56c4151a6c92ab5e2d15c02e440b8a2b8 02d36bf7e889a16cf13054bf93cd0c6d4e2906939e6f0aedeba3e9def6ed1d7e c9d07ef5759dc37dc020f5ede56c8985083682c8f72d1ae79e9837a904e3999d 2ae8d87780d655d8cb28afb72fa2b67351b0cb001fb3f044ffe98e801018fc9e c0f858e900a11ecb372347a9c6c34e4b9f3941ea7a0ea1354d1b7e5243063308 b944e35c31ef29ca34efb845d63d13d597e9b6f4d0337679bdfb060ee8ef38e4 01cf9f8a1e2f1735b9a62f0d19a7f95a3d9b21086c6b227184d99b0b2ad96e0b 26e34e3abadcf3ef258d9f7f79dac9f267f23346a1a1bfadb9215c1754f6b4c0 c67ddb1e3ad46f038a950ab42bbcc90f5893aad29c9ca02930f9aadb61037486 7414e6eae997e111c5519b36777ae0ca24bd7d98835c1bc6895408f2d0165519 3114b6866c0223529134a04d40394689613dae85fc09d812e1e701fc74c67a2a a7424aa0967c6007afeda0193172272a869abde1e1ecdb8c8cda8353ced8a3b7 235332a342579fa8445036b2326ab1c3d6b5fa887bada7a93dcdc4d53a16f003 6b03dcc9689e2ab5f59d40a1c6724f1b7f8819d4d19f74201503e39a3be0f356 ec67c8971a02221ff69261223ae95282cac6b433fb7e53b53616aa175ea608bb 8b837f7c289b4f63e827f6de86ddcb00fa91310f7d053a4eeab015c54dac021d 8ac35e41ac1ac8254375dd5069213572adbcf14c0c4be164c5e85383f76dd57c e0f523e2e89b1024baa02af6bcdd045143e11e5f0f9c4d03126a3ef690374d54 60a3fd3b09f07b3b1bbfa0e46fe2e4a8358065a9dd3fc19041e213b72789a4b2 77f14b90cea3114e3f157c60582fded05fb4a8902b043d51a3bc9a32db25104b f4c67ffb389173a4bd30aa0af0a9558d12d260e52303bde2403e83de3e0fab5e bcdf3922b6c7d1fe4fbb5cf8f45924689df4a603a13086da38298882e5885518 0388ecc1ba6fadfdb5be768dc6a30765ade6c198b94425cbbffda2f3562825b0 03161dd7929e304c00875ca943875be0d53e104428d06d1d57b337d25853bf49 89675f835781d198ad338b7206e24c24aba0cbc4825dadde676ee330e770ce5f 1203a1c402227c368064d94ecf11ac2daa688fda5069661c53199e364e906240 f9c479c5667b33c8a0f36aefe16c034ec340cff4da1ceff9bbe51b0e19d091eb eb66d7888e74016f34c7ade755eabcc8956a8cfb8b69cd33b45c4bedd659a0e1 d6498eaddae5606f92c4281a904d9d7c2fde8da2bd59df229ae2dc72c480485e 3c06e4c3c04a3182cee79b2256cb0bff951954f0e89ba7aa9d0b4d66aa66c8a9 6bb56ade0bf04c11b13d3e39f3e99ac839f840b28ccb5448baf642b283dc24ee 113e29a194936561c0ca197e6c0f1448e95a62e2972c103205d3d2c6608fdf75 1455f36459fdd6394288c18437801105a789f7ce46a666dbacaee88875e83b89 419eaef4c339b273fb237afd9ee6accb2d364f0049c7f653769456e729a0f2d1 acbdcfe6a21f760060213c45c7db5320bf70d85db73b168a87b08bfbf58cd50a 8324ada4025f783ad3bccf435681d1c3fe0aca5b4cfbc8bab604b85a85e2424d da62282d8e91eabce67343850c056a622df06e1418d04777a957e12c85faaf2f b6b7dfdd51d68a5c541f58be68398575dcb1f3f2dbff1a876cb599717f94ffca 03c17b4489de845c818090d999fd46dab1abb7bde1659fdad03b7b1a25084633 6e5e9e3ad9028caf55f34eca23749c17a2db9e0c2cbd76640957efb019aa3015 dad16218adf7e64912c9861867fa2b291b563401ab3e9f3cf7a802a845da0718 800448e7836dfdb564e1f3341ef1e065da22ffe112485078689d199b4a384df3 439a6228ffbe081911ccd1466656027ab87f8779e0edeb039354b2e2f9f97da4 f9bdefc18893c1d98f11c8d804213bb7e45bfa4fd2332566a398ef7edb47a7aa 71d4b80abdad1578f8fcf83dd38ca889e99103eb1548aae6f7a6afd262b025d1 18f83b57ad587ff192642cb8ba727a49cfdd696447d2809815908f2c85aadb45 8d27ea481af2983c11f22c8982096ea0c183f50e72e5b5bc086b3e49df519b0e 1fa34462641f4f450fb54a7d7ed40c11559b75a3266ac4e5fc1699daa5468502 38dba29523a5d0c58004806dea0724b3eab360833de767c80e835ef316f0f794 8d8c2be263e08598038c1a2acd899cf8f1461f7f0627c4c727e1a5fab9dbbe3c 5007eebd1c3cce4dad67fddcb8c7591b9e619789bb549d58739873244856b0a5 ae80c3d639a013fece615089d9d47d67272d62ad67b1f9a9fbc265b3fa6effb4 25213c205675f15992449c52a1b71dd85d9350bd3e6b0d4d0580ed513b870715 c01573556884b924d51d33bfa5a096f7bd953e9fa690fc56e84c99b975532af8 0541e5d4fc7939e118fc168b6cffed7bb1b6f41919640fa214c1cf0ac104e8e5 1b032c26e2955941d4a65d75ed55a252aade89b5457a0950923914e4082ae1bd 0c8693d511718db4f8bbe94a40f9336bcdbb19f405ead1a0cdea300527400d46 7526d08617ecae73ab6a66c6ac375e4270cbd24c2ba7d3224b3729c0035cc86f 7a569b5976b2ba105ff9756cfacaea78cba8b888de706771fad6ce3caede35d9 ef0811ac445cb0a1c145b3775f0af783e2e4c49a8908e132d078409af795925d b81d26c37cb3100627810bd2d9b03417e05d279999553390e558ccd8079e43ce 47e16d1b5ff1b2477d201466e0727bc4fec955dff42bf1ac7827b161358e9bee 993270ec0d00cc3a694c7b706f2b1f015c2ebaf288aeb08561120cc32fa21d07 15b3a5b6215fbd0ec87409d8f8db1f9d81c7e07498f77d14efd86ae3ab6220eb f78ff4db6ab28ce4da11dd8b29c52b2af0a7a2d1482a2e9e2457a3268e0b2992 d8a6f8209c9922e0bb1bffad5253be2051cf1e13205ea1f8f25dbd1cd830fc2e e99229fde8ebad74e92b0df1dca5cc2f7128571e8e18ff35df4c4bfdcc1f049f f7fecc7849271ead0ce67ea5dac98082d9ed93456a63005b962bb1c688d06f8b 7aa9cdecb9fe5edebb341b058725b6af4884cc3cfc25ac6776e79f342723d08d 72923e834e403f90e6fc6f3b719ecf60785f606c589d17404de35851529de275 95cf1b7f364886d7a83d27209e6cc1f87f4b51ba5d6a45a78680f60a78dc8397 e1b72a2924e4b4224fdafca3d8864ff4ff1f0aaa4256617f540e8aad26368c4e 9373fdce450a009fa866112c226f75637ecf8f16da55ba944b1c91468e80f8ce 3eecf856b13df798bff749a1d1d186d4040c18f85278cf9b0d3461a724b8cfc3 410601429cbf0f40cbed5bc31afcaa12186c57c82a49f22890f3acd986290de7 7fc31403e74f22c3488d822d3e3ff1168ffe68d1256c4e784128ea4b95894372 d438bedd9a325ae64dc081fb3736908589a05c9d51c3b3db683abb9d37ded72e e5dd2a080d941c672323ba4d3de0de7f862ccff51c8f5670fa2e5f9822326dfc 2aede661d2e275689d14f9c48f382c9a747e8a17753b74550ea843a9b3e11559 c4245cf24d80d64f00e5bbbff633c02050b113c05943bbc948c6218e1a4e2d50 65504c31c2f1b07846a635c44c30c10de6f86749382b7dd478f4c203aa5eff99 fb5795158c14d79f534b071e1634b13b26fd5ff47f37f730b62797018c5f1bbe fc87ad9cc8b31c3bfe9a2a7c0b9bc61a40b38baaac699fa944e81bf1e4be1623 14db805a7c709b7236b54448358c84527453b1f52ed6b77099c9e28cd7d1c9ff ce8ece27484aa600d6e6cd8cee328febc483cbf87704b8c5fbd7d92f7ead01f3 09d999dbae2d97b0b250f9e78d9614754dc980550f44f503a71a06506c1c265f cc49d055c71cdb06b67232482b216e36e9368ea9c8388a08106731703e2e1e75 b20706610e4f73c27d0a20311971a443dd7a95eee4a26a6d184e35604dcbf7a6 1380099b9ee40d5efe0784b3684370c091ce551c2e73b75c9042b89a8cc506ad 862eb60cf398977882cb0400df7fd9bf0c375b7cd5d1710544b4247106c93c2d 3fd7e35a88f76a75818c42aa046c842c2e80bcdbec3cb25603ab21bbead234cf fe8ddcc1d41b11f30825fb348671bc8a6e0f9b475869fb594a15134d1e9156c1 c9ac8226966413554ca913a00df5aa585956e31f7c364eb89d99ba010fe33634 c3141c1c6504b18f7af1bb7cfad199c1b605715d409b17cdbf82cdd056d9fd11 bc7dc50fbecf8869e76ca701b46f380a4b897c9b5cca75233d814236470281cf e93df05ca09cf24b4909d9ad74497163d6af3ed0ca59ba67f0ce48f7606fbfcb fef9c53be3dbc9dc34bfccb2367f236e2a83c9e36b1b458dc4a3b10b2c8686ab 878cc627e12b3adf9eb3c858a210d0826c6cd152280c0919260e8765d40597b6 cd9ebfaacee564a9efcb4d3bb6b3af701f4d4ee1d88edf75c9221e3ca6944a67 7b1a61e357cb7f04a08b4bfe3b306104b61f73f9104e9f01d831107607b834e6 fc9ec0de55f29c220edbe0117feb41ef03125761021e7642d02056203a0907a3 7106ab63be349624bd1ea61cea20973ca8d18d277d24b154b263735e50c1d04d fedf3cbb107a3eb30be8faa7c3074646ba5a5bc26d31ff74f0075aac135f54b9 a5df7eab5f03d48f0504aa80b188e30d7edfa5e373e0b8cef1afaa8cb4c6d77a 7a62476cde9c972a5784ec402dcebd7dac9c99aaf80ad47d0a4a62d6b3dd5a4f 36aa4df1f0dbae7a892f67d2b2774884dff5e9a25a4316f34b9c03f4c0ff4310 b5c30ae1e33d13472352abba8e53b773688fab84cab7c5bc0b99c80da96d3f66 b1a76b1aca76c2128bfc08761b863f65215d793529934b7d4fa2e9aad4dc5c6f 78e4021a78605e2483218829392cffa6e3f3a34fcbb770f7d33c48371a9054e4 1f768addc2a515274be63550d02098796e7395311a394043c3dd630a545b0930 2de48f4ab2bfe388d725b20245b379b946570d823670f4115d3a78a30a13f747 4ab650bf1a1eb5b86cedaa32bb50af69925ea6d5b180e161dbd854fced30ace5 f6b0dd6cd28b7400125fd99a4e8c8b6526e2c15faa6f5865cd879edf56ac688c 50c348654399097b87b1af330ad7667b8f4f50cb325bd6f22d01742ead076bd2 b14ff83802a52c2e20605391cc3e11e9a44c6548b9201b9297e2f6224d05435c 45654185b392c4479a8be88961a0fd9b0fda49f0c672a118d6b2010a6a2b8c8f 5029cfd4c4ea7764344a607b3c7213e10168bf34153d6862b731856ac57bd258 20a815dd59a193b2373c83be75d61d081f6105f24b4944114c445ea22196174d 6be1af82afa7d51e82c4a968501a9ebf7863af6090a9a4a62a62abe8ff7ac17d b1cdedc1d4b1edd60cc2627ad4ae67a62a303b89597f1f3e22d559ff494692b3 ff62be5c38a0a198cb6c96fb08c4d8cb79488cbadd45f2f29a869f43eea2f90b dc003661e149aec71a2a8ef62a9a346c8a3b1ec718384a0f1093898236f75631 d88fe33cb4b4162f6bca05de2e05d63f9f2adbed77fb400016c89dd135ffa1c5 8027518a72dd15d544995f9052cddb9a71c90d37408dc0daf19f760c613a2a46 4a71e0d394a5ec613855383108ded16846f9e9bbf1c60c6fb05e17476462e557 dc3f990c1f35f9501f5d87c75b1f366acdf9d2e92aa2ba873cbf4323a6dc3c20 c6f87e70c80964398803a5c14f19e317cd2aceeaee22f980599bc9709110c725 5c914249c7e8cfef6cf8660fe7fddf0c6dff29f066dbcb28119c8f6655f55cde 811f6075ebbb75b1adc8339c34afa7747478898c92551847f4d368252d1b46e7 ec745c57a0fe0f22558b8bd0883d9b9a1d07b184a42a69d81bbd0349726fc39c 4c37302b32d59f4dc6c133c8347e40235e41c89a018d698897bc0b3548108d0f e45abec7817d4bbb2f21a31cdddd7445711669450f411878a7c938552e31013d 8a5db7fd82a12369058c93ecbd82a299ae092c3dfb29ed48c33f113cec22f074 3524787f3f826d378db1d53d0b0d0857fd91f88715023ddb1ac32c8d86b2faea 55705fad82f591b6ef78fcf6174070db68043e7d7d923e2f93bc328e7357b9c8 009f75794fb38ea55a7f16a96450e2b10a96352911668d5d347e2994f07b12c6 6b57b70dda27aae06de6572c27aadbc9b6541bda0e04c334907b1c9b57b6814f 8ed214313550b8dee34cc1d555fdfa50f7cc4533cdb1c8328790159354cbd25e 957efa5e8694482365ed4f3350c5c8c6bdbbc7cdcb718ce9bffb852f8132f396 5d8f3a81558ac60399ccf498c79cc847c98744b765b3820b8750840bdf2bbc0e 17fe0441fa4bee6e767e84cee0e362e45900a895c855b78e8af06f87902d5113 effad327bf5ebae0b926622c06f2139f4c54aeb8d411c95047cdb1adf2ff2430 3176417de4a5435aeed9c73bd982b3095e492ae6c79c8b0d77de6b2897246ea0 3249f60438591a674e787961fab482e27ced68b6a8de7ebffabc89964a7c7f3a 7c6827667f1ee4c68cc3d6859e44aed39f11a0e6a4bc5812ef98afa3935be243 57c81452ccc2be2dc3e38489def9592238d9d89ad4b45770a455f8f6e84f710e 8a1fc2341444c1ed0d6acd5e2eb34f47abcc27d799834e695d5a23e08c4c6bec 7a0f81cb52be8884790828e01487a65ea841faeae24a7f47e7d9995bd5d091ca 335724bef474ce2d5b0616d686bb1915e49ce54a5967a15b488652e0b20f411a 02b7469c603678b04bd3e4116286540680415455896af8d4b4858396971075c7 b1f605e5a3d06b9e0acc34613256f80e5205ac16372da82cb4136aa59463729b a67dfc5282a9ee1b5513843defc1a433eec5f7170c1d03851aaaac6f34a0ff3a a4236ee75cb540289f8f5e72c3624c55874ff0a3b56d00401a021499d4130bed 193a0c978702e0587d1ce1415de89db40afb001d546caf5c9104a27ba3bf7052 13c7d6a8e57735c48c773bd67e3c84a2e5b37603c3ccbff5aa0b09c2e677ceb7 a1389c1f361667848d8a79bf859942ccbe5398a944428dfae5e90f3f15b9cf8f e47f268a6ac60c988cf77549ce50805b947ea9813b0b12dabff0479e3814dd30 f0a2e0daf302445b3412977f14121ce83ce060d16042ad8888688dd4b7d030cf 841e1c8e71c8e14145a0bd67a24b47df4517f477dd81d68ef2e1f7ed699eda79 4df9288b058a69d7a426ceb90d2f624f0d29aaafe8a725b8844b1ac8c140d8db 523d09b9eac8936916659794e8f1d35e0da7b30616b2582fd57020ad3b643066 120effa260662aca609902c52773d61981820135097768fb95ee5c4f7c8a607d ad2bd1509b0cb60a88cbaf02c224e26d7f256fe8fa7c6a77df55b9dc01c7e7a6 87cb7f8b3896309937fe50d2dfba307333e8b7bd761b7e5243063308b944e321 eb149ae3906e27aeb8bae05d2b896af111944033548e58f900310327bde7aedc 47a4e82dc92764a96013d0123f781c0b387cab23130aa6e19d15d30fb4104fad 919eeed5cd8c0a9126b6b058c892a9d50bac2ec8df3a4de83050ca500caa8032 a49a9aff3f9485bdaf1aa4b7377676ccb97451d3da19e2429bc0832e6005d83c 6f311c1b37f206a67a6ab70bf7e1ae3cece591bec9b0713e1b81a7b9f2c81cf0 10065b7f56a1c1c27ab4cff304fbaaafd9ac741fb311f5d9fae8b5533b64cd9c 8f5f9bfb8aab2131b6d6d024bec84a69ee53e729acf6a1f288cbe25390e35f10 4e5013f6bbf260bd03dadbbbb5a3caa1bcb7e5ccfe91c4d5904d1b11683e25ab 33df2258d3b0438528a691c737d2134ba0ab9e55154a95b1bd0ab925e839933f 0a6605281c9c037c0bcf1945ad0a9fa46005762a4b12e9835ed5cfe6115c42dd 9b35c1654c2ab62fbed7838477a1586e7c217716f60dba76c8e148731539d57e fd35836782451a9810e6d52d19af4adbd7c0a144332069d27a30140f3c50bfdf 9d5715509aa457cfa870e039c379d54bf840e0e2b18fcfa5cb16b653e96c432a b5195735f092d4333d26dcd16aa003b4ec3d18817b12e2ae12bba2964725298d b1db5b9fba1c5801c7e48b0577123e0fd41c1eeb51c9a670e795f86b36ae7a55 82d4fbbeba84f532b371ed78aba85a2c2d8a451422de486a012d30004ed0f009 cb9bae7cce52db06ab28005347fdf1989b3abab971a85a15ceb8d3f193d77e2f 58d18e5760920d5ebc44cda6c144dbf9ec2bc596f5c2d090621426d425c3f9f8 4caaadfa088999f8b93c28498180dc3b191c948a2b646b0d466ea4501cae397e c0abbc0e2c46848fc385ceeb1e963c27458679dbfd5e57da61e1019d19e0f5a4 effdf0c92749cbbf9da7d7ca8e85adf68a0681a0321f189dc49786b7da565bfb ed066783fde5241f2622a3195cbf632e2f72c9347e51f0c6ae7d8557bddb9a4d afecdd11b0fc3abd02cdfd924ea3aacf47028fb783c61f2db63cc0fa87f00730 93f9ff44ba0a7d409e080c567d0fa97b5370bf7cbba2ba71f0c857b992665d1a 8fabc57ef7ac2caa46614ce18b335a96edfc8f57cc6942c4fd6a14b6c2f69f7d 0eb59019bf045728833e048d6a819b9496861f97f61104ad7eb7b1e49fb05fde 6b28fde18347ba253fed3e5c5969811eddf424614818e1273f56b00b3ca0351e 7fcd4300cee18a9e5aa1c532526425c9bed71c1b95a5fc0b392a3a5e1df06219 c931d896a6f4c9867b3106b83ddae5f01454c421dfc58b69fb0f29a954f04308 ebb9d0f86a1bf0e879fdc064a6e96bdbaaa050b8cc88edab1a1a7caab404d9fe 26146014024fa244f5685661e9873ba3b500a83cd75047ff0677b145db98317f cd34daa29b32f79e8b3d6eed5c7c27d5e5ca50776f0703c4f58e87d80c7a9267 2c0860b62437d01efbc5b8309cf7650a6641dbf817955a9e7287051d1cd4980e 5c2412f10caa2c62e87cbb8f8e9fda04ffc47e952b6488f8c8f1843a74eda6bb 6d0946765c231e68f9ba4d3d396ade78acf2f6bdf84ae5e4f73b30dceb464e61 5dc70e61a31194360f0d23479cf402bea2a12d418cb7a0b50e8e07c1d090ed5c 192bfbb4efc752f24fe30211121aabfd942817b72d0dea1c2654a7639079ef25 5c3688779a002eb79e19d8ef1d7703d381b30eb2b0e934ec07878a8c87de8b52 29ce0457c32bd9afd47e8943133f31d80f1f2da658c67040bc5fb2c1ee44ca99 dc2a78e6384a7c4df3933ff491dcf69a724506957c2a7b0e9562b5bc1fb4c230 e031de8ae4049004a45a17886c49774a4f01ea12e69ccdc90f55bc81b7936793 0c1e698b13510bdf6dc8e83aab16dec9d6efdd386b3c15c4c8cee9ca1b19babb 7670abec2d117ca028bbfce1d636201028e186a94e83e23f0c06730cab41aeac e940a8571e330423d40ff72024abbb32b3d3b62f4e8717e8978a79e5f7774e90 a06ca6090ac071832858794af5f4a352c031031c4bd35aecf879c118c599e338 cbfaab9a51928e228ab829112bf43a407523bf99149c9f69c3c5bd3adf1cfa99 fb7bea5004be8763f946ecb75bdaaa628779387ed303021f0b0a0ede56c61237 58b51aef609b9f7ca960c6c1dd1895826966d3efa640faf4f49340701324587e 1170e8f9db2e71b01981a94065cee7e6927ca434a01f84877703eeead94335cf ca8a16a8d01e649267bc04d9633529d5092dfc3b2ceee1761f1de49df20b4b80 d79003eb1b84b3c1090201c3b549a1d2c9b977ed5d7e314e2b44a630983229e3 eb539c598139d737c9693d0f3a6c5237fe4bb98b1ac06ccf4f3473f9bab0421a 2f15e8b82a5ea2b8a6eb4dd74db9d1db80ff725d8732b9560bd48bb85889922b 515f9039b2d7c0c5a344ddfbba80e6c82b4c6fea6240022216cafe664ae88dc8 6ef59bd408dcb78a53bc87b44361cf3db6e462bd33ec32d003e14c5bdbb7477d 5aa84be29e9781a6676eb71798be47710a73461c8c462f709761842a5301c947 5dd8eb060acd5ff1ce8c7ab02cea58db8dea7ca2cf82d6334acd23a8e2e23476 c015c857c2a7563e402ad4e78981bdcd094ee6695fd934d2aa0fc18befc8547d 855adfd241e61f3d14f6e65fb49616c900760f877fa4edbddc1520e9a8cacdfb efdd15d4b05803b45dbd92a3a5b8b4c1c7acfe5dce70cb05770f415c45458756 d217a4bcb2400d457a1365f90c5e68eb26666c49382ece94f4e99f98e9c84e1b a13c0e48a1f944ba73f8c02a8602ab9f047108274de9b0095328a17b7557c904 972281863f35038ec9b9502ab63c107ff7cfe31b56b7cfe4577fbfc49391ed0b bd2237542493b05c0fc9a3e244f5fb9dc47072fc73216d4d5bb96844e11c532f efb73b96c08104d288017fb1be416264eae67ed584b7732d2cf6e82818607c2e 1739a5f33355b36dd66be78476f4fb4a7ca1940148d34de4b1de0c9451152352 a965aefd49f30f1535aaeec61bf50163c983cfa46ff7a20566cd089db32e03ec 56fb16bc08cf61cc37a05f0f6489a5922cd7926b4a872678595ae98ae0c61845 ad39b1ae450a5aa0dec60616f4c77062eb81bea935363a9318baac9bf1f463ed 5d035c0467f2539b7cd6b9c290688cc2a4499d7837ff9d8a8e383d52c87c98bd 9c3f11b0564e36b079cdf2c9c7d2430eed16e99d5f0a785600d9654b56c91343 056076fb5509fe43fed70b0f0718fca6e0dc92f40932999fe4f4243354ac5e08 16251b0446837a3b3b3d68ad3bf518cd7306fe11541eafe27c076f1ba91723fc a596558a6856ba256fffd15d73c473fa003b263212decb8fb6a573c08849cc56 edcc163dc2db27907a74caf2297833e242480b4358d74607c2205aaaa89e038c 4a186a5996e878d16b8b4279ee6a12cc16258eb01171b1c7c3b96a53b4bda037 bbd6c03bf4f7dd6ec0a0c6a6747c550dd5fdb44cff453eb65d91a0e43ac484d8 518f861b346b78e488a3503a6b1d8bdb3bf9bceedb795e8d7a9776692c5bb417 f181f1ee706755fda585a157002a45360bce7e38823ff5bea7cdb8de8ba1e2b5 d058ed185cf11bcbf11e9266e6e588bc3d58840f81a5787a7934a2ad2abf9a87 4956e0e72bb2c10b35bf461b0bccf26bda3b4675307970ea628b5a082af98b4a 6b4128d8913814fe8e5600e23d262a6f291dda41d4e08b8bf1a6a88f2375a7e6 01bfc77cca7a2a1fdfdaef0b40f592d15376219ea02607cb02b41bb1a0bd1676 26026e2118b7e6b4070f82e56c9cc4900eebac437da74dad4801a047f1b5cde3 50a2f35f96504be63ca33e6caa4f784a1a5d713b321f49f29bcf21aff9e3e1c4 a419672ad3529f9d83b80190a348e27b0a0cfbcb4c12f0495326db2fa40364c4 46daebcc762e5fad9e68e3109bd59e8997eb5662514a7a4a8312d09834583614 b6085ed34969ad50d7b5e4ee8af4d26d76f8d9340ac69f31b18a7390429c959f 4b5d606e509633eec832c487a86be4ab9b866bbd38565ee9e9a89b8cac43d71e f72afa481b8e557c27666a8de756a216d2ed2b073f58de029c9db5670f46e678 a9a5ea2e8c4662bdf51584679640a3384348defb4b67420d2d749737ca8e8397 3643203177e6af6b571ccb623d1484e26af5f9f2ae73c36f37f45662f493e684 a49f6e85c85222c517cfd25512348d536f3edc409822ca8a5ac71790f60e1a4b c3662b96e56020f9fec581b7a7fb0eeac8ccc2dc8ad4447e07231f46b5691fe2 927d71ad15602937abb32b771d2b3d40be3eab6a5c4e3c06816e6ab546f1a913 03bda9504247aef4ee2efecac7795480df51a73ee7cf0334f0c557d0bd21e26d 88eb5b610bccaf73a804cedd646e0596c5b5c14de562b7be3a47d3f94368075e 7c1b144f87f0bc3cb73919e61459e816e31edd26d733e97e34ebf175131ff872 20c8cab192481cd1c7598f12d41cc56596659c578b194f05029e5215efaab6ae 66ad63fa6e19f18e71e9edc39bbec3f00c3159eb0333e4f06db1a0542f8714e1 44aa32a2546a85f1a890b10e2af0e997707479e10db3dee2e0e985a0bdb27b95 8e88e1610a95e861ca3033ee56b4470b542fb251a6fcac44fb8beea97716c5a9 51325c1f89e11921bd910c218173353c043b4b161bcf652586980f571a8b1051 31f389b231733fb4fe98a59fd57d77399ab3c44eaf98afced7b1a2db18b05dbe 2900d5955d9181b54e736f0e627e700ef19c29a21bc3736de73527ce291572b5 6a4124c249efc17b721f5dbc136e1b5cc73a53f583debbd236f7752a1ff753b4 e6c5314599959f93ba983ae93f7f94eabe3c677985a76c3e7cbdfbfe0e9fb8b1 57594485961a27a11ac4cd6e198b40da9a03083b4997f764c04503d5b2bde1dd 308da5ab7ee43919252c726e23923cbe22eda2184ab675529ef6f25119b2e7f1 6051d8fa175c39868d2d766e9657bb22663349dc32f78f89ff6a262423a74a85 b51e0bf812e5ebb99c035414ca9895fdf7ae2cc3547e5be2aa7957b30ac7279c 29ccdd7d54ae65f34d043f7154f88a249197f883df5d8d6b404f13ccb24b47c9 28429d704d8f5c5a5986b6a54e72c4b22d86352dd3281446c9d685d01e664cbd 694b87339e280482ef708f5d31ee02639940406f4a2c0d1f941f3c645c45afd0 e76e952e358d853fb9c87add69b46787519b4cd7e7441c5dc4d174e780e47c74 9250e3d2d82e4893308a2de57bb3cfcd0a431e1f9325cb10edec21b5d7ce92c4 5cc371159f50d362535f765cad7bb6e86cc791b976ad580ba7568a14f403c514 af0919343742c66f96d35e80613d0ae6cb9931801df4d47618221d85c652c0ae 1d8d0b6ecae4e2935c3dde52e941bd94035d601e94a1838f399d90faeb31b683 3fd0dc7fcf65851ef192d0b5db7e3ae1a29c0651b14644c03298e17e619fb3ef c536c42a98b31938d0074ed9b28208d3aca22fe7440ad4d09d36d7deeab9d490 7c668a72c29bffacdac7dd7d7cbe501338830a9817f3fff96cd6043c9c982dd5 e56fd6973485b62cd697a4e27528590929f209bc2f29c19c547ff7d6bab3fd13 b189dc7eb1e61f155910cc6f94a64d08b4c65dd39e43d9fac18287795edfe6c1 933730b7597c36982eb84233ad009b446508ef770679807cfdc839089aecdfea 23322744142818faa6616808b42f36b2d4139bb7a523d9855e7a2bea7129b5df 086bfdbf31cc3e50714f53e9ac376d2f0e87ed04f8e61e053b77bf913342db67 e478de62e130a079771b8a7520b50a87d6b9364779b6d1130e0abc4eda0dafeb 9cc9a5a826b9a38dd9f9de6d3b65f8eaa79e83d755e98ea169c6630df6d8e2ff de4efd107985807e8ba37292ca64017e80f8279c64fe6d4bf31e2e85e4de3077 cb2e5031b06efbf2e865fde8bd002e1d25acb9291f4ccd83c518cf22308277c5 60a77c28fead8b7ccb5561597984c25139cbfe49c5bb96dcb358dbf6d2630859 a4703f4e29500ab6ccb4eb3ac6b42822775623d93c8e7c7e4227e40a68e184f3 ec64f885dae9fe27200d40c7a2b03d6492fd0daca0d339cc9c948dff8f089386 3909999acd7dc57fccd535567ab24631133ceba92051b640c4c04ce906a62df3 948a5c127cd391b0cfeeae15d2d3a46c148a0452791281b52d89f6535e900e71 e8a4caecd74186724dc7cdc1faa564928a4bcdb7e40400830bd0d4ee269bbd0f 00c4b3b3949587be7cec9c1fa00e58c48993c193d8e990f6d1706473f7970bf9 37aba8747f7fcf05dda1bd9243d72fdcc0ef69c71b3d1809b2817929c9271246 7e08724acaf126c106cbc0d507c3d5c33e0ca14eaf5bfac556d71f1cd9ca14d5 24542d091402d0db8f697ed6f7c82724844690a4f4a233837a2b9c3e04e6b3e0 48578185746e01b57c0654fe1578d5958997d3b3db2f402884d59d5784cf90bd ae96cd82ee1d164f088acd93ee3f2a8eab9c87ee191ce1b1f09e23853277488e 6a41eaae057ff7eef583461a2e7a82544a74cf654e59d4bd6cb4cb5486d4e5a5 dbe5ea44d0ed9ca59122e9e25a9b9b08e7eb8a8ecce1da2c06642a647c6b66ac b5e88d5eb5e0cf10c30083e9afaf61c5aacab62d17801c6dbade2dd7b084210f 234b4a63d9b02db72ffa4640b487b94e34d682ab18602d3c969ae997b91fac3c d90b2f8b1d63fe38adfbc09e246642e4abe656966a20f082eeb189eddc966b7d bb50dd653fa99bcd4f258f870eaa0ccfb2b3ed1cd04d118f96b5e62c68f0a5b2 b68281cfc94c916b1674573fcc07ddfc79b7591024fb79444ce4d33b2c9a38dd 2239bc5efd6587936cdf45c7da89bf6ffabeb52b2237c62e98b8a8a193ee77cc f7241f699a09d90f26edd997d8cd14d75af3a78bb3f4e0dcf5d33acece6bedfa 3ed75e33565a4bd4ef5d4d4c351df0b43929e2814c3deb989689fb98aa3e7a16 00cf1477c66c2851b81e316ae3689f37548033a5b170c2a90ec382a09d4f5181 ce3e12c089ed50f5034360889e4d90768b6da3eb9e9a2837960f9381a8a85283 fb84c649f699e197b53f97a16cd921751a04ef9df24f103e194b3cdc2ad30692 edec18a803d5c3747c6893b203b5b32335ad379c86750deac68293341ff6be82 03f842f49b3b9b2c6a1f3fe074a7fb0c4e1d89cfe1fbe8a5d8aaedea86363532 46cbaddec1be06797e2dba59b024b0cbe2c07b5ebb096c818eb6170ecbac35ee 3656be91774ab25a6e57ed46e67410c60728679acb4cf1a0680f8bf685c7d648 288dcb2693a6ec9b16ce82c2204730bb66c1ad3be2797db30dfeaaedd52ce3d2 4978ae0e0e0fe797db6819112ab8d3a6157f15204215282f50013c2b292d637d 69c9a55798df18283515c3ed16d425676f59a31e454fe190e9341b27411cb62d 48d542fd539aad42d9ab73d8d2c462968cbc306b9e0015d50a3e76c4c712a0b5 57f4eae3259eea89d914d1f9d1c80a28d72b5076774d1f3a1f953ce2e4403e74 b8b72c3fe03f8ccac93ddb14cc4558fc44997bc0d6a5058a9bfbd7bf5ea8e9dc eb10256b8ba1f000c7138cb31a23b3059055cf941ce10786646e5a75a4d185da dee17b866f2f0b8473bac0dc49024cf7ca96edfc6d21ce460345052929404ab9 a8a7971b9f85eed55f1a3338825573c48c6d3dc49528d13d8537a0849ffd347a 7f38d89799df18c65116f4f9ae416d9140d3f8ce7c2d3ac74478e8fdb6c045cc 25f8a1ddcf2d12743e4e24d096e7e2ce57e6a865832754ee8dc7803c00c0fa1e 5b97c79e20cbe4f88405bfb0702bd9aa3f5c6db8baed608ef1a55b63cb787059 ee42d85cba9244f3c1716f7154c9d7b6f0b6f8dab011d61b75ebfe0b70c66ea9 8325cbcdf74b84380466f40043f07b50aab86224a1fa16f859a96fddf453b6c0 5dcfb0b253ca3d23fca60ff7453be9bee22565fcafa4c0753231887f9b32f3d6 918de274c97dd4f4cb7b8fbc4dd45f1061ac2de0d534e8c7a622dbab77eb2415 d9507b6e3cea3be90fc0b69e97119e131d15ebc976a834ae8bcdbc9c7e4fbb9f 891f46d65ff694a18e3524ab69d9deb219dec2e608ab6018a5788bf4f9887832 19f69e79d7d5037dde8a4fe822311298bb410ec9cc718b056ad3c49941c14e9b a89aef435b4c0cfa26f53cc00507dcfbddf912222072a83e990acdbe6ed09935 9c072709b5da135b77fd1a6a7f58abd4dc3147ccfd8fd47b6ab03a8d29706936 f681f25474ed35433277cccad10f1115a728218f87690d321f1cde2c554ca5aa 0d0480a6a105bbc403b0476818094ee8b1f670ec2c5ec37d8ce4781f71dd5d50 eb4b0e2fb5ab32ad28e3563d1b6543a9b7cd665bbcabf37dd68696fdbe1dbcc1 3eeb411375dc41a23c09624830b33ac29de60fc1fc4b6d83a21a40c872f28904 33ca34892110deb783d926bed6a3327074939f82e53ca97dcb11de715f3f87f2 aefd0f31c63f838611b04e7c394df9e2a197d0183ceb5f86885e488e0d4244ae 06e82bf5ce39acc10f9e7cfebf403b582ee68b8e213e9b891d48fdaf510f4bfe 79151998db43d908a87bca1ad52f0d208cf4c80c59c7221ed1d285eaac051e34 15197b560501aca4423fa66fc46c429423c4f079b2b26e1e7273c5f2b2a2371e b416e6aa100bfdb99043d6c489ffb0fcec3b6f2395c688b45725eb92f2b4eaee 35c0a0a09d50a3b510f7f8b10bb60304d98fdd915ff1a7afd51104cc80e59add 9ccfae1dc568bd1f1e86e85cb13c45613156495845238dd0facb547b52c6b443 3b08ab06f62ce13fb52b929d9e7f6f9f909e9f31310de6cda8be55bf59a0529d f68460e9a65c943a070198d8b73aeb1d4386178921e9eeb6efe7366cf1bfd4a9 efb27799b34bbdd9f9fb25133b03885213127a98d2744b8b3e777b37cc7d0453 c7b342ba06f76a742ac9d1ac8bf54da57937207e6b10cba21b2aaf58947c8c36 158a8e00b0574546dd97f818f8b7228caf76a63a739e06425991f495a52cfb01 f50d8878c94ddd59320ca93fdc5f6e7d70fb3ce15a233bbc218c2934d5432b44 a6cad3b978ab02ae4903d072b9753adb4d7ac16e9c0f5a67e62372f9e16766f6 7fd50073be2e8416c153fbcf3a7ee7295687c74dfe26bef89af8b3d7fa5aa80f be3e9dc27fba1bd5de4e9e1296f2824deee021af326c8b5ffd871b34dd34def4 ad51eb0f15ab0abb5840ef7b4f79a65180f518869d890c2d65f842153de6a777 23948e4ccdf58bc1fd97fcd6eda9180ec6f030ee32adb5b439951cdbb28e87b0 534a6a5920a143d8bcc4de9fe62ac8b93573b3829d05098aa9b4100a5c8baae7 35134a337dfc3b9c1f13ce823ce3f02848b728b5adeaa2271aae398b4c914d57 a76283193e8617c037c2b83a6f14c610f52d0b97821abf7e45c17134f694775e f904a012f158772a17b6bfaa03fc2f8d9005db88d2aae21651fbd677a4d91b58 ccf5a95d6d0c5ba3abec47fca5f1efa951c468b9d4c1a1922b1c4210d7c1e7ef fd677583b5f5d0a1d1b5c8c156887fcc1d883a3bb3cf0dc6004554512db1bd75 9403fa2f4a9f1d5608b68eb9edcf23b4df2f7dda73b1733b7fc8ff68916ca6d1 622fb84b76b532b6ac834f6c4623dc2810523697d91e2760349bf666eb552410 ee5c8c97a95c51c3ba81507f52fe7b1a5855279e55f64dfa1a7b86692c305df1 4940a0ae033afe87c61f59b189d59954450dadbc3e38c13e261e0cae9648093d f93010839a2ba819d199116b7f9a422cc1a4f36410ab7c22b206838c85b5a562 68dbff1a605ba49ce700d008e938b79c722803dd938fe21cab7f9514c598f98a a020cd04956c3b0b2f5167231b3d793db1a678bf4a45cb46d758fa3142c62086 8d3c1f6180c750ea9a0a61a728e915865036315ff902bb6aca7d620218eab802 cc91152952f4ff7c297169d6486a967a4e3de00e265c831cc201ce42832367fa 9cc88d0701db1d64b81a722bfdcf43447bcd536988783d2a496821af475a0e40 3f9451ee1cd973428e8fd71c8f6552e5a4087166f96e152cdfe8f6038d174993 8d394b3b297bbf4263242865a0fa2b7bea46d051eddbb64ff376ee0f743cf3cc 8b3a44e415f460d774ea4eba84d8260d1c032d52420ff992ea8752b126b7b721 740e22f8df22333850c5cf943dc531f866d19dbabbc7c0791ea966c4ef93a337 da80b958699d45c7fb46ff7b2f0967d9d6e9ada7b97a3457ca134adb9aad0a45 40742d93c91305aac65a51e368b0c091b4e7f37ca13501119cc9c0f7c718686b 640c86afe4be68961a038f1b32e122955e5826c3c830b1a0c84dd29eaa88e6f6 11ab9398888d0045585dc2b5ff9c2a1022b7cc5ce25539fe4791fd5acec180bb 1307f9ae654b56d9004e3f03c316a6e205df3da0a8ac7b6878e7529c1daabdce 0e4462f7eeda1fc1671fc81d2ab0931709aeae5c0255b1129f7cfca1e8247f12 8149751008e13452bb1e0144e435f58c6fc45bf4e2d48372fbd840bcd5528f55 059c09fd2e0e3d90dce183f8ea66a3901bed7898f5e189b73040937a08a39feb d6b0d50275cbd4bafad676fc3857df5c5f391ba19a9085219d420d7ff42706c5 d4a1c178b40cd7cb8d6d3faf66290fc780ad52670202a10eef1552f5cb07e814 0358787abc6fe03ba0c951b024d8b2b48cf626ca4b909bb7298bc05f2ef9916a 0271182b3624c5ca271c6428370bc66c37f9adb7deb6d3da89fa8f0951ba0d3d 52518018764b09ef08c98b9344351fee3f9635e64f3a2c5bb00fabd4c33a3967 fd242ba882873205860d35fb7d95f3ac23c9d8071afaf72eb5d443907e93aed2 dc09a3d04d887847e2a736cd64b3217c27c64ec92b3a321017c6d398c78cfb16 36da6d065365c2676e0530d0e54b00852b58831e29c9c669c03971639fe8fd19 f72583a55a61d00f3bf5ae143536f2d6697436dfba850fb91c0a705574320c5b 5d12c0a32ceb6e1f00d32dcc4041ca0d90147521469abe4534538038f96af670 418272d04f987ac87a95fc170842e44c295fb6f2265be207024e6ff039121f94 0af093470554dc7f319eea8cc7f2e1c6a18ec86c90b2d20bbfacf5ae30f7f66e bacba58b4511507be97660221b3f29703d4dc03ac615fe4db96b2ee3777391f2 d37a6e0e94e49628041584939fa7b659c1391b59ab1f1332f1fc45af2323dc02 0b60ca2527adaf61b432947639fdb177118d9ace3c0ecd9c7881ad01522e4df4 fdf4d67321405ca67952dc78d2f9d48148e00048c4849305dc0072eb0d2d854b 7d97bcbb23636d1fd96f4400ce3ba06f838f4a3f31fbce3da04cf1789b99f6a6 f614135a85d3243a2c3600feb4226368c6c57c2e6ae1bff6c244963c1d7cfbf8 c21c40308e98c03cbbc8aa95960978fdd73c50fc7b77b8267f5e952691bb8b1e 94a358941a8706b6377e43dafd0656d0a78edc65a6aeff97c4c6e672a255a67d dfca1c2983564b194c87ca542850d96b574ab1950d354e44482fb73cfa933fa2 fc3bec249203c6cd723d35471a49d940c2dead7baecb33d702e2348e95896010 bdb7fa21500072f7aab45de041511e7ed4e8a7306120efe5a9c21c96432cb8be e2a48094fb3365e4518f2cbb186228c221c9ee10c8f7f771dac7f04181a89d41 b4d1daabc6db1846bc493215578cd50a97bb99163087fda379a0f47667c898da fd5e07d2e692e4e9847c5f12c07a840c8eda9a4f6b6d6386192393c69569a3bd c7d74fcb4f08e4a77c382ce1d950bcf3ca6905b023d4664e3978fd5980f2d7dd 43517cd493684fc6f207d3bef18b919025adbc9ca736f840589696065092d617 2741e864ea603ced165bbda09f273c332839359246a72288640bfb76352ee47b 4a13c6fb93624e2cdaf8ec1c9006c6d9273f1e635e2ca71cf49260b54ceb81f4 fad87d7dd893cdda5cc356b1f17bb4b29273b4e257bf763f19a9f60dcc30e003 a56185ad222894b58d42591dae1f0220991686af39f7095bb0446ade99ec4bd0 80b26936493b74c25b0ffdb8fe063df1ba932b5f0edc0f75a3a29091e0538bb3 7723b0158194f0658e748ad2feb97181af7fcc3f1c8f5162427a80abcffcb684 f7bf04f1fdec4b334a68f4d928a9e67b3feb1ae5a66ada3bff21048f34708c80 36b7e2f7a01b56ceaf592f5f42155330eb849a7a110f6884a23cb65e4da953c5 702aef908369e769bb978f8e21522d0ecb18925342e60c7b4d02332557f66335 b6b461ea700046b3f8382fafee7977150f9ef8ceb03d0acf33ad702bda6ccade 1333ea6ec14f972f1b72906ba13d0887fca8f71dbfbc2e8b7bc8fafc067d3e0a 1a0f924780c0e8d9618986fee8cea0164f1f84321d964a32500213117d1bd754 0b412bfa3c3e6236c9e8c78e5b32ae330cb716779b63e19eab501b1c5cfc9e95 fc6d0634a18da3f38d12450e256701a48273f57e68cae118eb3e9a455c840a9a 4f989d2580e26ea76a7e50a690631fcf61c471b913ec7bbd5c2cc57c5dfcfd26 b794c53a8624bef037286c20a3d6ce2dbff657047415817fa2cdfdb4ca1ac9b3 7aa7b641bf987896dd1d567c1e1b7111cbcc46619594b4be0f324d228e2b83c6 c9402a46811495206657036c8e2fb3730934138145bf46d8676687b90f46aae2 ef2b4741d66d97ca126d9b5c8a6e4960475c77109d2059634a2ccb29ed117acb ca79a16b3bfca39a6172fdd259c0b785b8cd7ba68be54f8f64f7d17543dfaab6 18330084429d35e9634030b3200334c64ca6f9bd5c91e21fc03dba52561eaf9b 63413191443a0217c8a73a07ebbe752a39455b76651499dcd69dec1272583bf5 e23d9be1b403d24dba7bb772f5853b77a369fc1a41e018ee181f75b27e081716 089668f94360d97b7d068a2cf7a6d748c332cfc1dc3c793654c99e98d7a4d41a 1de9e56dc3304615d8f253c1ac2da2e4ccf6ef68c34470c859f1da002c5bdba6 02799c7310492ffab3f58c4125d651d2434659a79ee8d18d84260accfd4203f9 14fe3844a77f1a66980f339afb08aec5c6319c6e0673d393e946d7e34747fec5 27109ceb2c3091e5c4421e41d2ec18e060c2cf228a75560c1aa3d37644fc1d6b 66230a8dfc644a9a217cc5470549bf69710d3a27b22c34bcbf01e1c5e8e5e041 bf3f19c8226b6b809940b6bb574e208cf6832e51b63249eaadc76880cf3f9158 16cc98a9a6e62a4c5180e99b0a9fc3cdb3950d59daadd6c8bf29076b9cfd7ed8 3f7273502f6681d91bae7c6d46dedf15184aebbd6e2dc04126783edcb88e0800 5374719e45139c4fde99085b1b7834a75a4804754d7512b988772cc29d165cdd 5443436f0505b528778cdad6d8c0e9750f7af1efd886e82296c03a823bc05b67 2197d9221fb40fa880145e69f271dca81b1870516b1b0a93c8c17c4687431b23 c6bdc960219adefa3143fcfdbc8e44208fd37ed35d1644e813ac227ec87a2f65 f67ed0eac190f2dbeca6f7c805d4fd4a00df8a175e84eaed45a8e42920145a4a cc3b2b1fdc1b5d02e01762a386d42e5cd483b65b22d62aaacd1d0f75720638f2 41e853574c8578351851558008f9a1f38884f5243523af2b0ea16b45d908f9a1 7fe50c6202a440fedf8899d726ed0abb246cbbed576220fa3a55c09414f36cdc 383dbd0a0d59e49726298403ac032c04372a372d4046a5d227213c33b71b8719 4f440e2fb44f4a174e99424fa1af9cdf1f33ee4f792c24bef934a22a02a696b1 1b13a898c0dce31ebbbdeb644cce5d686165466c5d908668dc8f131b47d82f6e 1087037add47efe0f24a9146c4bef27e7b9d66aac8cfa62072bbcdb408e7d1d9 cf33574f5166fe681c280f283f7ee01ce25acd01d4114540d3969b0ae9319c0c 278ed90aa1a32502818e131d47f544ffb117b52815eafc597866fcfdba79e857 b742a20f2df77b27d0a768e8b0eedef76aaf9503687f461ef774915528dfe51f bd15a86bfa96c5b0dd2a24d2fa95d7a9ee02b44f1e784425ad06567b938f128a 82898ff8cde68eeb82f600ce27f7bf55669d2c04225903b5a6f1096e8a59e2cc 2c14798e119d5e32db357c62940c6a0b2ed16a0dfa9cbc7cefa3f73c9e16ef19 a30c073f090467e41b5a6d560f3aa550fe759643b49e0d7c0c417aadb70d8735 b91bb5c647c0fd2bd245723bdbbaffb94624a5aba5205b687b402b75d8d46c25 752f89b1d5028486ca4610040e13faca2e4db8e1894303a20a7e5e8be9aa355b 27a841a64781216979f462b487628f05410b5ea8b043f424823fbe90d3e8b1c1 bb07f5916ed431193629b0500fa32b52a942c4bb9d94a4ed8811f81bce2ade64 02e94c74575fb0a554e6d6e35f00065aab5c3186850cd7bf120f045a194755ec d42a094bf29f8cc1d4ab60a9eb28e8c5de55b5228952051ff5e31ec0fc06dfc1 eccbe65740a12c4e162a635ec23d030b7f33c09292495f50bdafb78c1c4b3ff6 eab0c36d47c02f885dfbb7b49b46015e1fc224f6666e619f7536f6ef760e6f31 7a4bf73e690a4a705dc8cedb1b25b30a0a4d4b3b7961cd2a4dcee41f4c60d899 efe93663c8d707025bd9aa9a416d6f1275892176e815f700b1cd549ba932a2cb 366697eab5d423ab87bb3afceecc085748f80ba5679e97ae582b2b6c8bbd6bab 0aecd5edf9d1482f18daebbf0122b12182fe0323e2139f54e9d531c215d21cea 63d989094c6535412ae0573be3e13728ba9a4dfb50ed3f48ea9bad32eff06531 ff69b110a571f0ca5eb073537a8b2ef5b16942e724f7d726fe3344aa31afd59d 66d0578b035542efa968e040a0bc62bd4d7f917cd1e290f69fef284a78116b1a 88c1b3c5f773d49b38d91ed6b3394dd130754971680829c935ffd39d5e04e1da f4c865e0f0c7fe77bb3c537c077cfe4121217bbf8dd8e98bc50b2547d1b558c0 156df955f750691155393e8635578b79205e260627e59107ad3a5ecc5686329d 6de9a642b1af59d3d4a252877830df326bdeebbcd792d50a059d3f15b2f825e9 8f226d2b59fcb3dd97b26e2311a33f668788b8d1b89df9d5c003744d3180ba51 806d511052fb9a3c7c70491aec97c4864131807b48684030c5946cc39e58132a 3bebee45faecbf976516766243eb348ec8f26e13dceb912464fdee32f48aeaf4 3385246788949d5588bb02d4d8a87659b2a58bb378f8c367f7ecd27f04ce0d13 5b68afddf5de79ea2ceb98c72a6ae009419f78582d31f0f347748e3a1b23ec1e 2acd0f43df6d4a304431d133cebf2adc82be9794be92bce37f4f08b2d9e40373 fb2eb085c331c8c9957f810bbf7a20f3a7902caa11ff47645634839368778bd2 f760fc3fddac2ecb4c0de360fcde6208fd07fb3bd26da3684f21622db406bce6 f1d20729532cdc2d316ce9f0c8d729e00da1958719a1e5a509b329168c4bd7b1 71934f05993fb81cd7a0a4e42c9b26d6eff26b09bb54db4009d2eb390838339f cb20d2d263614d3a3cb2b9e1fdf59cdabb87e096784e32d9d97c41f8d4d246c3 2d6eaa124a8d30aee80372461cf8e36693684f022da6740c911e7fdd53c8662a fb1ba15c8421310574700f3a4e71d60e5cfc3c4b3ed898b48b62c02d5fd22332 65400dbb38368cb2281c48de4db63c58fe091cfdc1e8895e20c3336b5b63d9a8 9ee1611c5e67ff1652e2478652e52dbf637353845cadebaf49062c4db09a872e 9e75b79a6e4751083e3c49d36fe6a97a2f69344159abf18835640bb8e3536207 02e7bdf3c4935fcc3d60c845627a5581fa3c5d34c90af7cb746845fcefcf9f58 644504662f90a5a5ebf39294c125db4c78a23662de5d4b3cf473b2549e4420bd b17561e5740eb83a790de5148c86feb217abfe743ed77bd75e503efcd54e5283 37a05c14e349053f38a611d533b81b25f622fe2a5fa9ea98e5ed26082109e84b 04245162ea0cb386109b21671d1396ef93bf16e533e813f6331fd03cb4acc2d8 92840e0227b7fb5541f731d4d445b74abbc82dc562b758ba6c8ff0bdff3ffca7 4c4701c203060f7af5d96129807dc67a68fbc7ddcd1fccb7b13763e63bf416fc e0b90a694e8c33edff59ac4b85a39e233274fab0a6d3c8f3c9a396a5d16b5d43 44f35115d81ca9c047bb7c2e507d8195408ac75d5bb6c2c142d17b4c5406687e cc8f7e04d79a9060450d5a71122ae8eab7f4db7a11ed70dec6119d99a771e1ce e023631b0df02712383b01093b54e34e638b83e2c774a1f5419c0eda34aec8d6 3df20dca283fdb30bc4a1e91be07079d2e0c39b46a8eb848f3c02e3c39b9a86b 08d8d53625c417f2c184debb1e82da691395cbc296413ecedba512b166ec44b8 0f8c50da1f565d5c0a5bfea26b60302783ac3b3b804cbd46565fb0d83097c1df ae3bd64aeb7db347cca719b37125c36e69fc79c5e105fa73bb5246048f61887d 574306b5cec710d03820329e8bc8feb6df497fc696b624394e4acf07377a29ba 5c5a10c93e61bd3832bcf1c693b494280fa8ac3d88208809d82e1c32a6ed8f88 9d2aff1431ca82319532648a2a0d1291b22fd96cea71a4ae60aa63a23b2dff08 eab75dcd96d4c4b03c016f16e320218fbdf06ae4e0643ea7e98afbc3fa05f11a 77cfa038c394d24cce7c96500570943a5b40f27a4eb4fc7c6403addf1a60a850 1df3e271554e4e29430138cd7431a5cd87a47e5cacd6ddf71a793b219887320e 5e58b23623aa56a2852e325d5cfbf43bb8f502be5cc1b18685fd56ab5e856470 1dfd2be84cb2ba94836e3e685caeaddadbdd34757aa125a4bc9b07ea1b8a39ff 27a1a8991a4da7da6716373bc4ff01dfd581e9af36a7d24d725e6d5c44654646 eb361de309325f679387c4af79dcff9a3c243e3558f595eeaeffde0ddf8df815 8188c0d38f4c90942217e4304a56174fa0dbd8052276bd8b6d382a188d369789 3fd31505503f4cb05db4f3de5e8b6b1912a57d4488c0fc196958ecccf1c7d664 eb18d39d6fc9ebef612b9bdd04907a49040612837dce5dbcaeb93e213205c2b8 08878584bfc70403f8ddf96e9fafa5b740b44d739443b2a350c2ac92736c4df8 14da1468d8946f685d88e3790cf8bbc0f629b93b6339dadbddb25ccea9fa956e f79d58c2f0aea80a43a0a1cafc9899784807bbb170f0c066b25820b54ce09626 0072fd957fa7f3ff404acd69322e53f0866a2fa0fe095b316213020cf8a70ada 6d925837b9ab7cab7fa7180256a3ee95adba1719cefd97f7d99ac76760f88bdf b8e293e1bec28e365b68f6f5de0d49f2e136bfd678c56687c2203eec88e4793b a4698895ce9c87633ad852eff4e8bcf9f876a8702f27532274ded9cb691f71cb 034fae4542256b85e2e9623fe03908afaf0716dd9ccaf0307a78015cdf54a466 3e3170b08d8baf390289040885dc7327edbb2d6ade4945c2a1e6f76b21cfa36c e8be186666bd551514872da346eaa3cc35c8c50337454664245a93e585e65091 5e209cfa5c4adc2a1fbbae4000cbf517c2ec78490ca1c3feaf1682d9b5a67788 01936f9cad1bdfa8b5a48059b8cc8b8a20780fb4d0697d0e34795515668c3e36 c07346af174f1cdb17db0238a344ac53d611ac8efdb3c8869883766671ffdfeb 00c8c856d6fead3dc125aa7214ba96af1741c09e50dd6ae812a90f1ce1198a84 94a096064892a05681dda93b158cb01ae06f0d53adbfcacb113eefac113a003c 62dc417d93124280b485f4a328de4fbb9abb173d52650e0e92e19b9e076edc28 8e441be2b517bd4a708b272476c1db8f5b900539ba6de212369d3888b55e5200 5b4a94dfa8cc8758fbbd94c5050c1d3d49e37dabedc5a57231d74d54d15c854e 7e4bb499ce56621559d031e96633b27f9c1650d2c87a69090d7642b27b75950b 3d4d13159007c1844f58fd06dfe72e4c39dff4870c47af15fc51b8dada7fbde0 6ee37d17b3f1faa69cbdfd05c30403170aa9d70360312a10ca9835a55d6ad08c 3c7426c175ef6c9578b3d77eedbdc7c0a75e2780d10af0aaf022545007102ca0 38b96c242668697cd23b6fede03554362210968194d5244d8258e0619691dc6c 1017eb4f4182dfc165eb14a509c032835db55f1f19f84390b929e79864af0f73 29151e30319e3716e18a9e35019c1cf5582c6b6eef1834d199762019b7f55243 a5d3634de4290adc09f371f737b7eea1485a760ccfb249ecac70e7badc1a364f fe27a2b48c7fbb04b31d4e57e92ef1f659bb3332c60983fcdf65e9b1ec8f5afd ca93716b23579ef5c6a19514de83a51d5683f8935560d21a1198ddea5994e0c4 c639c7e3ed57215f116cde52dcb8af3d1bc4b0396c70d726ec284ffe53ef16e7 f53a187b2dccf23842a031cb851b7feed4f3b1faee9a33e31584ce9eeb2703e6 7d0864be288e2be57e87012264c9ba0d951182cc56fa42524ad0590c2da5d12b 3e35cfc13c9b79ad38301793206b0582cd2f58c70a50d1c6d215b9a181011eec b61fadee6c7ee05a5da7d9c406c0a50038f1d3350f9a4bfa2f91b1628a80c2e8 237b8c775a7ef21c9d716841f710d4171da06ab01de9aec40596d86fbe508f3b c718f3f58b8449276bd8d3e5a8b2a4e0ff0d36939fe56f2aaa2144464f480f4c 2e5f4db9e005acbda2bc708ec8c4886d8605400f904b5aa1fdbbdc2365a0a485 91aca91aaf54d184c3a10925e151b0f1717ea3a0108f2e818b7d4ea12de17a10 fcc9872b222f544eb3a5d3c9898c9755d2c390cdd16648364e358257c91931fa 8e31f2b5f4563150ff942da203aa04a9546bd812810543850b472a3970d99144 9baf3419b2cb23c6dd1b362aa2dd2b74fbe5a014828455c62d5b0f8cb668d81e ea670b43b0cf64200e4b82f64ffc5cf662c21cae575e0bc8f4fbb73fb9f0ce66 4dba8ed6c2ee158f98e2f8a0ffc9db7cdd67ac30987c330870fddccf99d41790 9c3b35fab51c49950910704c789d7a1ffdaddf50acb657e76ea97a3a1dc976e3 02a0710cc36224344af113f27a88845ae00328687a47984b0b7f692898b72a14 35488783e32b9b52121abd7d37b08a9dc3e2f4d70e0f1656e8c60be9a4263eef cb0104893f46cf139b34b312529862de4b42fc1d5d6835c40ccdc01147e416e5 229457e5052d838e6684bfcbff9d0e5b1475492c7b24689e24cc6c8a57cec934 faa71ee52af14f53d5a10355358e58cb1865e769c554283bf1d4f0c31a2b1f12 58e03622a84cf46679f4a99cf1030ba2786a6b30d888900bc5b28e6ee7778ded 197e998c5c3bf25a17b7e2f740afac2ceffcd8dcf9fd6a264390dc04dd9d5ce5 573f287836be622af004d4ae1be2032cf3014e4716de5b6682ff34a4f94fe9f8 751d750541d05a07b9e4d756fe8b226e7b917140613eb09a56d07c9c8e766203 88d253083b3ccc63effd6078b090a8862f4ca5264d45e94460518cd95a86eaf9 4995be4bb6432458f3d7075d09dd965d7a9d7748b57a0e1c85ee529a904aaec3 faa7115369b77521d4fa031fd6f80e98e3ca0b310e2323668038f29d595b0a09 0a55ecba3c9321e5f65aeb0799e572d39f11d8c31afe064e71e4f450acee1ae5 f1567aeaaa571176f8925180e0f0282d63c5a557f81f28ce695b445bafe38bbb c23eb4ff9e2acc5cfa7bbd9ca0f6fab40deffef9e6f1b0447a15c562faddfa03 6685c8d8fe74fdbf1959cbf1b055dae862fbee7e0bf48017a7499ca742ff7c24 7f70862dde2968f052ca1c09e1ec083cb559ec4b5243fad6b79ab1c783f57bc3 d87943c6b187f1e89ca4a89488527db2bc97df31731974a9d456fa826ba2a2bb 7e3e62b77a364385cbf8fa3a55022736108fe576447f5cc9d0987370148d1b61 8aa41af28c82e1b90ddb20a21b5c1624cee29efcd97227c27dc4422ee55900c9 6502e803e2003a7d7be899fa8ecedb45af7f45038375032f0f7b05a0714320f8 f8cf70361f0bd041f5fd3e90154b21d4b2259d1b0b355f9c2a62577840e25af2 da627237597afd1cbcddcffec4a887614d4c9c01c75cbf6edba0a2d8a865a7f2 235ad01844be36e105df08b55e3e547225c1ed5475db9146aa7398e655e99661 746a718223652c696d1061f5d3a869fdcee27fa17dc4c1d5d077de52083970f0 190a36b4e3a01bb889eb14a28a0dddddbafa30df01e6709ea5c9bdbd8794d0de ec89afc38e1879df93f7d5d3eac5724e1ca9ec05a007f34f2e5cb800459f473d 4c5c986bcb34c5760af9fed5ca4fde704ca51adffa323c3f5699340d4573019a e1b90095465dbc8fc89c3e55ab35eb090b9da0630667055151c27b2842528dd7 0de0bdf9d98902c8a4c49c6dfc5aa80099a0f9a63585e685ab5f2754bd1deb3f 3278282ae8e7ea6d2e8790b28de040034ae5c9296c012fdf6972635472e9c4fc f5a92a2a755f8c2d637e7d8dae03cb37554b841100c1a1580b711a80c3e177b9 c1819aa647653bdc4540d903d139e353935e2f7cab577b739883bdcbea27977a 1a62499abe41b2646d077d59650af1d1d7903dadd53e0891238469dcdec9f6bd 7124c08ef8568adbcde95529dbde0da9dbb3e28a3130bed644243d39c022173b aca61fbb8fefad91fa9f18ca9d8131119d64cbdc06df5193dc11f199b5b71013 d657b6baa99a0fba3dc754f2e51d24f5cffe332cd0d8c44499f89ae9d804eb6d 6f7a9f553338c0cab7c83f4c8af514058e22fb3a404a676532e46da9a598bc78 6ee45a3d736a142fe7e13e562fe791eb7af7665e96507e4e496dc43bf54f791f 8f981a93c0359cf3fa487eceab9843fc0796360d837cd0cbc2a78bf19a45905c b3117952935013eca119b15716fa256a9387768db2b8bba21410ea4931755c3d 5e3f90ed45d86d7eb9e38cd079530397a1acaee98bfe1d0e30c96ee519336136 045721d6bd27a2420147a3bf0fd024c3a13dbe51f1990440fd4364fc1b677351 0de2f2f2dfbcc38b484779a7ff41eeba5519c47eab6485b1231eab82887f6953 396e930ed56b785c7a157182ecaffc33b8966b531803d15bad0e47463a898786 f0051065fd0122da0ab8e22fb5baf9bb5b66294ee2a4870becd246a50648c768 3b707a738a688d752fbd270aefda6ef9e6fffa82e40d980078a41f5673a67da2 81d8a61a154f5b58e4405101c40f1bfc4e9d690df2f6694ed2e77c156222b911 7e995c3628f37898e7daf1ea042ef843fc5e98709294754df62dbb2cc433bf05 0f0366fd1f0cc2ed064fb7837839edd6e997f30a1c0dbeb4494a4f8082eeedf4 cc9422f514c743386180219a3a9703b97ad2b34ff7e321a3e27959966d984811 aa178f5c1ff519b1e15236350530f800912f025180ded5a594b1c8246f4fd6a2 745a957be5685f3277475a4256e8dd583ce59baab0ce1fe5bf64664a08eeb27f 2b1584f7554fa1e36d4c38d86c046bbed8ba6363d2c5f51fe37d39d34eff45aa 62987d102a48d6051e6f2c46403282602f11cb9b385f131c80c8e6f9bd6017f7 fc742cde8685e8ba5ed0aceb672b6f8966c86b623d3edfa7b9e1a8672a3b19e8 1ec24d1e15e38d739bd3acb45653459aefe205a84650ca55ac29ebc588025353 257f64c7ca66ea1557eeb372ce004418281de28cd04667660575583314f7b5cf 963b19e1c25cfe361314dbbaac370140758d089ec99e5204d93361be41d6a222 a7d81e9918be41d7f77b507476bc059fdd5fbd23f44396ef059c4b1ca727a215 9ba7a729a641034fe942e2e7bccd72aeddfe860fccf9343e6fa3b5d06808561a f8c631408afb91308df463c8880e4a36afd826bdd50c313cb0f8c0cea6048ad7 2ac71a1ca86b9838947fd1315508e13778599f04dd1c9cb31af8386aeb5f930d 41d4369bfb8305950d88b03e32772e28e4b6dd470f86546fc66edd755bd028d0 2f78ee32c172fe170b63065e2619df6dafa556ed10df94d8ce565d9c74d0d70e 2b167658392ca8ba5ced2c6859cf21cd2f6ea37df70e20a5a8d231729cbbd6eb db2e79e3027b40ffc23418ee5f00e0dbded3fabfbd014b1300142e7d2fca3de2 c74c082083dbe38296fe318e167df88918f7328f179848c92a73931b19d12e54 3b346406ceb2980ba8ad713698e1e7c8355d4ae8e66a9bf49f36657fc618fd9a 9190114672f0454dc52523e1fe767fd714397fbfa3488a60c4863c32a2b84d9b b5200923efde6b93d8f7ee51fa2733d79e3d064ae56a3301e68359a804dbcc03 7a3b87158756381b8cb560ed819bbcee7b68834660e24721dc578ab5b5f0074f e3db1afdedfaa8fb489313c360be56105cca746bb23d9b9c5ee39e582b313f15 a1f34ee637ced35124924fbb85d97e74984ded2ab320e69861a0b60720364ddd b9d8b9fb5d101f40418ccc4c982dc6669196efad1067335b55938033f901d7e3 9bf1e58a5ebede71f74745c36e708a96f1295402f4845a1948e7aa64279f1e30 4efb9536c6862a4ccf8871d510a46979caea00577c1b00a3def25906db0e1a94 8340809a2c51e37476ea306597c86ed8196516ab4c0aed2e91d9946d8df6192f 421009142d5fe9615c071dd667cb500afe894bfadb7129b1e0899c34400c8035 3b77fc9278675992451871be34e040612722185a0419cd9ae6024cad54eef0ae 9533100fac72e7c0069e6bdae00f1303c6442af8f0192570b46dd692a400de22 3c9556e7147b40dee5e9ec5c160d25bc374008644c8451654a2be31a3f02ead5 19b5ccc2f432ce4e3315e8c148fd1c265609bdcd4355b8f7b7f4e32bd25c6341 1cafcbd630976e3e25c4792dc2c667dfa17b568853d0a113caad7048edb2cf19 47d1b575894a4149b25a2aefbc4f28fa327486191949071f954afb448cac64b0 6a5cd0d3b17fa0f6f72d1f0223d878bdd8bb3310423380ddc5b4e6ec5635ec43 87648002b40cd3e17f41f74f41085e28aaf9a9fbe2358c85c3e9050963f7f8e7 fe232ff970df2d8fde8f5fdb703348664f26b0027e94208e0aaa75d084f22489 937641dd38c433fad4d7323f3585245908891212cc3f261a9145c5961c9777ea 9efba3749adce903d0417c48ffa2eec2c23e76bbc7d93aae2ba1e9bdd0b3e419 4a7669be6624080c0276f035bea6bd1cdcde6577c35a9a470d0b66b9718ae5ab cd91712fedecc359344e7292cf30ea45e3b1907710fa65c970beeafed7ebf183 a0652ae2557867a5c4c911cc916c7887d475b17e4614c7a22cc85355a2f4c7b4 bcd4abfb4adba29612805ba07cc277e5877ed224c8f02b9cc19de6bd850b480c 9e6b0b569fb74d012cb1a1981486e6cf711767d839ad03917e1b74b7cf96fcde 7e62324fe2973ec6f29fe7720ea8294efcbd85e72f49a7b7ec429c3405fe736c ead1c1bdac04edaed2aa7859763b5f6ff72531baed3328b2ef73ee1ee39712fb 0d1e88a6fe906af4788491bd19568e784b2221b03edf1877d890813649f54675 b52dc09612f2f248aad62bd8bbeb3b5a8e2d20e05cc1ea6f1cef1b8a724dd23e 8fca0deaeb2156a49fb1d783e34639d3af855c83535e6d0ff482088e180fa737 3bad589035c5953b7331e1b72cd715f7e8d62fd4188cbabb65de47ca6b44883f 78527c25e3db628dcda0aa7103ccda3b01d5218fed87359d81b0a49fbf4c5c90 0aa9ccf289d137abcb053c05da7a78c07d74b02e0a90b517a3ffe5c943b3f9c7 082f585f152dda82487cce5f72c4f8b04e301f91dfc9015ba209507e7cb38402 43b756c07762c0d60cb8eb64da504ce0a85dc90eb695eb228d93854edd214c56 22acd37b303e1f5e08a12d53ffffdd1c21ae2e233797acb4f8c28813ab5a4d65 0d82730732722432b24ede7e89fd2a9361a2052e13492f54fb2e8564fd854156 8f503b8401a14cb57aece45f9e96cc8f14801adbe1b7d641f9e64231330f8983 916d58382420b7833c0c48e00267623e033defdd7b26f7f2239983266da36feb ee6a0b99b86e1f508bf26aa4e29d78a47258045c7f06812fb69eb17d2ccc1bc9 6766903d9fed4e1ed19962fad8a3b5511fd1a8e071af52036115ccbe01311ad9 c3ddef8fbcb26dfe1a85e5082601a6f9bebadbf8d06ac48b7e44b7b24926772a 06e76615e4457467a6b13570be339d75764626c15ed13ff21639dc9044cb1e98 82bc47da8d7e7d66c3de191f7e4d427e294a8bfc36c6527d7ff26ccce097a804 be1fc7b87e881ca3cfd1c12c7800f340ba8aa3ce1f993732daa26a06909d5913 9a951f75e4c89938d9b0976294e79614cba3b25c2a9150780cac2e6d312e449d 244860b84c7648a2dc95a8ec1b49d47cdc434195fc2ebf6783cb9b18aa784470 f5d13c1a54aa45e6b57892ba041676192dc94fc8b42bb714c274b6b13c434ad0 732beeb2dcd808c189fc381724b37075f5c96fc1f2dc12d37ea48cc773ed1852 41fe0421c888e2eb44b96d34121c68f95c94911cc9fe1d3b49a6736d3b93d803 5f4227d9a6295e6bad262af5f3f5bc810de0daa2a7eeeb8f1c3e575d806576e8 8be791c65bba22558639b688b5000f8f9bb4a6472471a8a48a747183fda0284a d52ed7cc3bba0d885cc340f47166de7a30c0df7f0a18c39a3613a842b1bb1648 070f5023ea7a64e29d02f4dc1acac9d37949473badeef59484340c9510a5a107 9201040d4667e62d3aa03bbe71ba17a6ac14c5ffeb7b2f26f929eac63110c09e 2bdbef7bc7d6e7e227c3a3f0a1524d6129a8feda3a5886d3285e6510900bd500 c4444c69f223aef39da17feed8d9ac16fabf06c5f285aedfda14c54c2ce69638 d8b255b567d9dc5a2d60a0fcaac827537de12caaf732919880940ec5f35cff33 719545af98ef4f3581a23c187164da75a126479cd1395faf2cadcacfc4fbf987 f82510d654cd116a35d12dc7f0cdc8a604d4dce79caae78c27ec0b039817a424 22b4ae751adaecba81fadef06f2e8f8fd7af022fd299fd74a8dad34e473520ac 80ecf1f5f3eb7c741e497c2b7474ddc7727584043aa92015bb45797a57698b8e 286d854973aec49fe2d60573129b4b9b1fe0450c50ac519e5a97fda56c8dbd61 422076fcef6cfaa7a466cf6efad1e5e453d668d36811cb44258c5422d24597e9 5dc1d40c15d83bc096f1267d90d6dedfcd7a30e4215d97c0d3d8616d88e35518 357b7f1df372a945e6373495a70096e36411f921dcc6a4cc15a148b6da6b2dec 9abeb74c6a7b6660d4c20825c60032189fba2e3ad8eda68133631364b3fbff9f a3f33a185c5b8da871f05c7bde62b59000318114b5f3ac006029a51664562dac 0f969c2568ebe6b0110408ffe92063d09494ac06962594cb0bd5f97fdd627f38 17e01014dd7a88ba4a6613cb261789fc9c529a8740adf710f4ac4381389967ac 9a87849a248559555071f725cdf1cb0198507f94297cd468d1a0764eac19cf5f 2f1ebfcce84f57314a676e066533d9690f0deb72dd8e28f06acfbdfece94e614 7da9448a90d2f3834463327e9166736da6e67e07eb2493c52413c6948b1c2c21 4b9558d20f927d747b3916927209bf1297c226eb7bd47fd2a6748c4e00934aad 0fbacb121da34376943d95e30b1b6558d10242b8e2326d5757a619545e81157b 5630390d96550173056c205a01145b31e6405a7351ee2aff0d856e7ea3a680c8 3d5c694e61e4ce343fc7e63b52426e9e242fe7f9c3b3c599b4a328bfa4affca0 1405a7f8b1ccb465468caf1d12437bda6df704081de3667fdb596eb707019a9c 453021f83318b0aad218f5c95f4ef566218bcc7e1f63e8ae99b35cf8a4acbedc 00b5ad3bd60419a1d5451aa1a2de814d1ff9eaac474d81fb3a92ebd719484ca4 a3cbed15d0a31a837733a8e73fffbba7c5065797c250c5f9ecdc41dbd32ea49f 6ada82cdf9293760125f4b86f332cbeea7a45fd1c34d910ba11eb0571370993d 9dd4d6fd61ff1a48838b2c7b2895c035536d6a90ba2bb34d47c16e6b23409de9 70685bb36ad36ef6b54d5379900823dd9c6bdb4fdd21b6575a6930869150bf85 5fd114e331cfd5f3b5ca28fef4d6a0e27150268d5a56d2c6a755e006873dc49c 12803e30cb575c41cf52a1335d9b83df9a89813570b906f880e322f3866d25ef acbd068122606741fb6436e0305d4e9e7ebb6c767576a3b9dfb5df0936eb7b54 36f858f9f513c8e59fed5f038855b8dd39decd7301d1f90455ce385b736cff8b 47abc31fc4c66300dd9dae15c8b02c059784567cbe094ffb555944e0547be9ac a7cb6b609531fd8a6024b8e90a3a26905b7f10628f17ea42fef27476f7c85f31 776f56609ab3e8e23c3bdf956e9323f4f4cab6524df8ec038ad905c61078de2d 562b845752db002b02841006bf8b875f438b4f30cb5dc249779e85f691c3b808 31be0b95175faa8bfdf96f07a3065c98600e9185f5aa7e6aaf9ac76ae3e4abf1 f258fb30f4fe0d538c6b899b1d63d3322816b80eee1c9524b1bd49147255fb98 fded3c7096ba4e3066eb19797873542cd9774d68e388cf5438784ffb13649f77 f441c4144f67c8ed0de542dc745c9adbb82914cd59dd829bcd29bd298d8302dc 5ec529025e1a3db0960ebdf4fa05d3dbdb2834abadba57d336e4a3fbd58a91db a41176c8f284075aff83d2ebe7681dd4aea633400e67a2b60106faab1713376d 7b35feb790505a316cad12e25176daa175ff81d82a9cce59266235ff37d33d07 afb72466838aa932c0ec3e5d80dd77bdfa503e71377add9a1368ed0f15eee6c8 89d20078fc59b4e5ea5e554078c98e3defe31c226fcee93f2abec4672308f13c 8549af96147fa7a39d99fe88408a0dd2a150af8e711e1a0cf967b76cd43f60bb 489226bf8a1c20eb728448754978ae6616f0da644b15563132e3a48347d3f6d6 e4311803be23fcf08aaaa3b621f546edac459fb61be50ece797c9945176299c6 b8b2191ed8b0569a1482a9fe6a85fe25d985398372aa08115ad8663a4a46e7c4 97478acab4965948569c0e4566bd045f96da2d7228e03ef7fef7231630314325 af9278a7d1974b2b7389bc0d8db5c5dad0c3a7004d0d19c75e9d5e2339ced704 f29e2c3d00d878c2b97534fbd7da67666a0ecc42bff7c9ab7240dcd5c57602d5 ad30ee5b51f55397298c2973b7ff21545efbe65bd4ce80d0f9b267208a5d87eb 0833b65437c3cc4e2837c3b81c4ab131000b9bec8d1ab3b25c252eff9fd9c78c 2aaf4094fa8642fb9fc8701d80e938578b5ad94354e7da1bf148fc672ba62dc2 0eaae8d67ff38613381c31a3691f6785becb66029dad426d923f350373d2f0b5 f6067f14f752f1b30f695244cd4c8ebe5288f5891e14385f0b0f78623337f5ab 91830b21a908df96e98f99a6c64f55ba7480ae6cef994163ab9d474245f3ad81 0cc8f86e1aa35b803a8cd395139eef46031d5fbd226e6ea196d1108e2f418111 81772e3dc497b09682b94985cf4ab71e570057a8773ba1e8746557f01da48408 942164ba0c48159c255ebb3863b63a6df01588796653f4ff64b654f6f83d4b1a 506efc1b92d298384278abf2f72c2d5f5f2270601778199755c2a286fede0ed2 5b3ef03dddd01baa4300e4057bb42b481377e340455e6b81a4dc76a136700139 eaff0337928ba69101929b32399b6124da408609132be8c82c24e1eb5a22db52 acb8cde10c11ed994d28af65e3ef0c4c5842ed9ee3fc85c9e4ec90b6e5cdbf3b cf7a11260080dc628364abaad8075e104719a78bb11b53ada7f6c8249765a179 c29ba40fa9d4f5f0127846b659981632274ef376d8bb21353a53c3999160d79f 9365cb846cc5ec93c7ba352cdbadc1ac2124491d03386759fc4e7e7942cc84f0 3475f2a78e8916870305d73c78feedccf85fcbc90eafffddc267e2bf53c03153 4369299d2fe816bab9ca7ed26a265881f99896eb80cab23118f6cbf29625527c b29b53d7b168d5487bf62d93ff443b6fb0d2650152f213e6299a119382f75df3 ac4b0089a85a24fa181d45b5e0a51b7554dacb4d151bd12530e1487793dd8fb2 034f28749a2c09e004e5b90216a2162e1b4615ca7a5e174141f775edc7dd25d1 b0c45140e01995f23fbfdb1b70e20549a171b8ebd02a6ff4a6d759966f3856ac a68eac2316cd109a77e371cd4c512dcf6861cb083d707000b790d7c3c80e3a1f 85b6d65c549b7dc5777d9522bae11b53ea14a93d435fb1a70ed3eec61624d2a9 bf130d40c98b99f0077fa37b7bf71a79a5280e53acdd0643f220ac35b0e3c7f2 812acd3f70db2017f330fa3157d561e683fb1214e5dae7779174f746ce1a3a15 cf1682b4798cf33c4130dbe3fac96307089fc6322945a2c525f81c6ac4264ae8 864a357ab50390424d4c56808d0f09851d1f9525fab0f1e46aa491bc8677b2f5 d1cf316b918c0dba562a04ace09ce847c772208f45929cf55ad2256133a33808 25a66205dfeef92ba80e3d0258e228edcf8dffc98ff670b66938933300d73e3f 950fde9bd0fbd91e94a81f868ec5cda6e24148eb1903bf5b883a32e2419f5f65 9204957da833b8a547808c297d77a99047769d62b54b86ff9bb096b1b7159da3 2ceb411b5b86803a1753fae4825d4387e0aca6c38f75339464660afd32bd3a90 3156a3834e68a6fe33ff0a5c2f06754f7a4daf144ba2041af41652422aad9679 3767be4c2203e559abb8df3e8651ab6d8771da6b062765493d4192f8a420960a aaa32b350461cad99e4e758d93ae88a5908a808ce67d54ee23077603d7695414 b9fd11b3b9a3528c03f6db50cd2e9d0dbc7e1378c19bba6cd16aad82ffa8db9d d9caa5edf4c5494fba0080e578a0e8eb484c88bc031ba90696728e9f34a61edf 9cb3f0ee74d4fcf8f47a1ce55b6c8cff354b3d50cd2fc758cf0c8b6a052c4c9a cc161034408240dcc11a86b1b19929f54acf399f8df17e3b1493ae6c06e16a42 3c971a04dfb1c2f148a6c791b64225e78e44fe8fc85bb86d1bae33e9120ac86d c8dd37876d0170edfa8e98de5002d77f8c0453e40d9b60821db0668bb6ca6c82 58e87f9a7ac1623396ab3204702bbc0b35017f632e66827ef1d3bbdcb59963f7 9ec71209cd582581dd733e42ec3e863c9b87c2e410839e6ccd8391f5ffc45de6 9370f59692e167ee38359f1e74965d9887543269973fb9c08b8eab8d57d6a9e4 cccedcb47ea530db8f5a768406fcd84225a08ebb7a7241ab15582d9531670543 255b173b97d2f2b70338d6f95296b4e1bbd2aa23ccb79e32a52ef2de144d3a5a c403c4473d5470522a6475dbad9c0faa4303f514d9ffe7caf2c7acca88ab5c16 44a7b818985c6db65674d1748913b0b1c0bab0e3a6e74976c2d3ce6a88f0cde9 bb824dcc4c65eff43086c6028344518e21b51a7e8cdbce54d1063f876a48d422 7f0267e3d37383973a2a151909c5af99e628ddba7b7982706fb12ac5b4eb4507 adb1be694386e391235c939033c857be3ff84123dce687b6978f0e03f6d89534 a8e34f21cf9c505c57eee55fcb4be98bb157f9d29682eb86f99a0e1a2c29d84d 95ab1cbeac57a420b639e7cf46dd033f8bee0834c122953fde9d89c2d1d83e57 748100ba48380a4c52ef482aed63504882c8eb076ab845574269e0c8ad92afc3 76914d631445fb7b688025c62dc930424945adbf893bc51ec86791605d3e7d9f d8d854a17794699c502c699004e0c19494835cd04f6ec57c6ae4a495a2937f19 b639fe8f6bd54274ba5d4abcd456a3e6ad0b77c9df7c2b2aa5fc01fff4e2e076 e5d134e572584d50a92e89fc19a0e394d542325ed60eae441a2905923afa4be9 087536681d41a56323781f7cb03f206837ae7ad03f9e314b10c9faea88491c57 75664eff297b1c3d9f4e0ed8f3671fa203aef8f915e75fffbf88ad49bec044ea aafa99cac77d158221d18d38b035a72575e3eb6be295ea3fd55e7bca8aa15b03 045ac706fbbaae9e2144891f2071d4f7e2a07c68bc8ade9f7253c4c226c0f5fe 50ff2e4ffd89dec34743146b35ccf14458da80076305c055800ed37ce2077d07 dde67019e74d5e5d1c3cda1d60faaf2b8a039d5a18343b558e680ede8ad50990 538febccb12df131c85e92e3711e7bc0f4526bac7b5a0fd4bed25f4f09ee068e 5a6527fbef4686310ba6cc2b08956bfab81ea8933af90d358b49f78278ada3a5 01675e833d58898e81b83a6c0bc4e94af4ffbf27f57cc46e7cf61ba2d0ba15c7 40ad1b4618b156efe2bbeaefaa23c50b46ed1232e58daf4e1523286f0f7a34bc 012e19309cbefb5cb2a83bcdfe3085725d019c975d0605f62a72e4d6a69a398a 70a62ba2267fc9d9ea3973521a01864521f21cafe293f64d5aab7c167b128ef5 decb747fd98b14911dd100cac6704dd18f6ef93a7c750541b5624f961d226028 467a76ddb790288da954f4237b31eba4436eb8cd1eb603b6313e97e1ef2b3d4a 9e7ef025ae785b146f22f8fc363f4da944c160848b814336c1a6a75b8ad0ccbc 2120e97947f4d3064705299cc027835fcb35cfc681fcc8ab18fc6b1e2327fbf9 eafc0787b6b6e36593cfe57f5bb034c6dcf67f24f6a57e43f7a120ae9889dad3 9974f99fa959f9ec3d2a0354f36818373b808126dade578e76cf36283f972d8c 17771ec59a3358aa573880f6226ebaff5c07c45c5addf082e3aa258a4a6982b4 8326d694294987c19f81331eb27bcc9ca0c093d6fe1e0a98d4514ba8cbe68ece 555aa3690b8e4231cc919bd3412730fb7b82f1c0712f87d76e42f386b396f776 0435942de39d51a5a83bc1b2d3eb2448e5a97fa1e17dcb88b1e795f77a3e0bbf 63561be91ddcba3dbe20751c0cb674b07e76c97c08db0addd02c308314814274 6a1a9f91281a278377095e3b45e9e0272d51013434b93cf11bbaf8ff00544fad a08367a0b00079ce8d1c45352e54d16f4cae96238954b7238c489a71465a0818 39dfa18951e84a9024c9a1d6e9acbb7cf4449906f4a83a2407f33fa5174c93b0 e5640705bb7c8431a12866cca5a7e05281cb2fd3caa29293a24a48d7ff4b9b37 d26d97db09f448e07b9182da190304c27b6165215a3c86b60b3c6ec58b250636 76baeed6f8c49d91a428c86d955fbe75d19e7ef8c1c5a4bc33559eb3f6a05053 88e89e8f4e157800076b295ba03e1c2791ce3f94cb9692c12d82c37955a4f86b c05b51a064bb185882cc765f9d152111e8ba3457e4f89027b89fd3b776b12ecf 7c17e30119610d0b94f657ace3cad58c6ccd00b52d219d7feeb640cc3e9906a3 98ca5166e63ae661ab4c90e0a0e1b2908c67811090b68e86913e44f971b41c8a f681ee8d2bd0beb01bd3560001c1267ac7fb063a6afcb58bba57219d460f9937 b52a52ebf8dde848b4d2640ce67478643e1c4948b90e967183bc1db7c4c4f80c 9b710c44a7cce4dc2fe1b7d1eb50cabcde0f3ec6ba1dc6f386ddd76c73cb9953 c352d693d113e54feb2f37709ec3e0b36c47fd82c7f7fe91123138ffaaa384b3 3ae0e186d2fe156ee0c501ae4a7d998d56c7b5c2d4b89b122b6ea4914d20ab78 c04b202c7af34356ab5f1b72e907d11a3dd5f18dab3f09367b9f49db4042314e e15e49ef8786c64f9c2fc84f4202e40ab3dce7f5ea19ff5fe73fd89f3af6c5c1 92a8d0c14e3b73d2654a7723f1f90b18c1a3fff0191b9244b9d4c397ae5e67df 7a982c1d4d48712fab63e5dafb8686328acafed144508c3618d174abf1d9c181 0837c73f155f302c9207e1c5d700d3ca739f6cfd32ad40b74235cd9e10c8f352 239ecf5dfb74911f6418d2f3fdafc7ca56d96643de5993b6b04a206b691e92af 417befbd4aa46bf5c96bb56643c692db2a0d3c7cad4a28747f618f0dd7bfc14a a813fd264a80157eeb2c67465d67f1951f46c19f00e8190ee5ee0f09ef99febd 0e96e63136aeae83e87debcb57d61ff30c154f5e5439038946f228063a5d273b 38d4f6124d1c11e11c6521c4caea6928840457f106eec2317a88a267e29f5cc4 22324ab0c3e286cf1d55502f87eb34d9982d216a53258681fcbba528602b07a0 9b7dbcc81d2e294908974b74b19d20742b82c5ea3a32485adff25cd67b2aca53 8dafcc13b17b7d2810d8352e524eadc42a38156a56023bdcf81e39f8c1f879f1 8002042c35a212537bd719ae6c0cece8e06358645d8ded059472337f73ad79ef f8d9c786a17387ffd0bfb829547cbe2c83cd1da87c1c081920ffbb7ad5240035 dde075bd5be805bb157f660a502ffa2e80a730603606bdf620e6f2cbc7278ea2 5095bccb96aa14d0df88ab63944d92d43e877fb013da47d3ef610f2da0b23ade 64f24c1b4f423a19bb7d93ba78938cb9f9f37840bfe6f110787b5c0b941bd935 1f2da3735fc5286d800630ffd27f948b49bf01011bad3f7bf09a07ea6147616f ade95368ba9fc26af963e2a00ad6fb9a71aa3a7dfb65413ce188d9217829ff9d 93eb811e2c78cb84e3e2b7265a04ab28e3ed9a6e208051b3614ae378a18c37f9 11aaa244a391e53ce3bb7e085a206e6c0c6f01717cd7df7ad23f0b537c3c19ec ac4429db7062d0cee1f8dac43d628fe16522b0695f05a844970aa4c7a342e205 15653619dc0b585dd5cfb29a50c6e114fb57cbc054fd5c06dcb743eb1185bd66 ef0ce81d5fca8bb4508e39d800ad05ed82be856d27f2b489691419f1f4331ee2 9fdb882b49d520e95b6b4852cf4f3ba3ebcdefdf6288828eb97d43a05931ade7 79fde2a06a83b1137db4f62fdeda23d63214d423779471c0fe9b0e01b009d576 6e62fe8b75ae08dc1e5abc1a02b5d8ffd65699cbae1acf69d13f4aaa57000903 bfc4a12b120fef6d7c95441897faabc58f59583ae57e36ba0da85f1823efb039 a134df46dc3ed280540f2914a183c520adb9cd7e7d610509624b6e5ce733d8f5 78478054928987eb8900bfc6dfd3b6fb07b764c19ee038816ae9e556f117a3dc aea2406858ebbb4cd81004231b9556fe8a0c9846ae633e074072580136167c93 6dae47ce95031013d30382feadfd0d378fa0ccc893efff314b7f852e07d343c1 6238022db21cedd7af94f0903b64c78d5e86c1ef332e0342392232751c1b3498 60048ced86b2eadba4de94e2bf568b4dd732ccb62209a1331c3ec62d7041b79d ab5b93965c749bf58ebbc53a46578d204db98b7380c2e3cf9f39ffed46dd2269 3d07c298935c503ddcd339a5e05f6bde8441ae8ad94b8c59b36a2cb73943bb0f 6c24a2ae3b1653e6e0e78709f7b49ff5022cf063530795a4c9401481f042d238 24dd21ed095c8d13d854008f26aaa55a7fba87cddf1485882241f1065a061e7a c90806175dfeedcee40afebc38fe9b6aaf5159bbff08d21c60d6ad4ab795ce7d 26c8fb3aeaacc7c0310fb3fad3958a2a6f41981cca66dc86f62413b896ce8ad3 80f533914cf980e00d5e13bcaf7d9e9781ef40c5fa308ca868c7fdd026f04c7b db92b9946725707ae91faeeb052b2cd333bcad85c70dbe520501705dc63d5f07 243752101eca349b6088f85a02602bf582201d1aa9db0c0becfc17b13f5b173f c4c5d2b86b35e4e56216f632f3138d9eefad76b2b3c5888ba90d43b8996a35aa ecbd98117e7ce75405fac0bbf95a013e57920090a0186248af80e9834345f8fb c8e3796fd6d0948454495aa1c8d6d7f7fda971cfa86e1efc0e00862b66d45833 a8bb19ccb06b9d03fdb3e3549f4b4df6380e447fd4e37466b68f58ee65712dec e7d535fdadc35d773d1f9d6c6c9fdb87c762b228827f124b2f948cef4fdbf32e ef85b4bf3db5aac01f4817b3d82170da7267282b4f4e4f35b90818e4a0a3a0d5 af14452419c232b7873a2cb715d4bb350ecf27a1b21c1c103bf260eb794c5296 4c1ac75ada3f4e3ff14a122581e5a02bf30dd85a916885e7d1006fc0b01ba648 cadfd7dd4660a9934c55bc84ed695d1fd68ea178a37ea0a7e6b8c9b7c73b025f 1737665648cc14f73aad4487ccb8f7d23a8d1b8ef7265f7d2723173650dff5d1 b991a81c1f316d5beb69ff2b50643a5c4549dde639ca491b4eea5313b9686604 2309fda143524992fe474309c2b733f3591b31bf5627b3d99c33093099f34121 dae2a43e7bc3ca0528170da9a8f090a7d11fb8fc8bf4131251f32e9ea8172da8 1c3063f77af1376123cd47825e7431acbe0af652a0a42d56d3519b638b21dc3f 8a2a568cbda45a7b5690cf61fd982b532e6f2ee92b790bddc798332f39d95716 e6da61b19e776d6d944198f17e5e3a8128d47793520758e660b49566273edab2 6fd1978b654c977383c64f30ef76199e562e189e3b72248cecc92cd10fb8c979 61fb35165c7e10739a99fa4d50bc40110649ec2582bb00e723bd9184af5c3ca0 23c43fe86e95a20a03b9f7b69b0d065b43b19cacd7d2eadc0ed1f7d106286e55 d615bada7f42881322ccbe4a65ef9eb5d1274a5ff7e556072e831d8ff457410b 3d0b2d6ccc202ace20b08b08cb42fe9b8990a4b507c341a847b523852aa0a0e6 22c7e4530525d24736df1d0d7b208a129ab88d5c1330d959bd9761403bc9cd91 5575a828cbff221a036ba89e366f6534d6a06382b8295b6110679c7db0157210 3e2102011f764cac1372c04bfdeed79115b9ad23bcbadba0dcee034417e02ff9 e701ec4f5f6def0074b6f62865c8a4b74d4f4ff4937b02a981852a39d40ec8be c54b13ccf109bd91ffe9a3964ecebd0b1212b8d6256ec09590bac016b4bf34aa 35fec546c33f05be6217e5e404d2e7d4cfaf5a88a83e837d499d50ac16f0e7df cef2c1cf793be672586ac5045d8ef976be534214818868704a5f6f637c6b4403 d699fd2fff037d6d8450e3b0dac321a0dd07ce51cbbbabd2008d6ecf62675428 9844fbbc43cac1ad202abcdb2457be2728c5b5f9713e436029db981aa0c23fec e729ab431a60a84eedabc97dd532fae56b193b3da2b257f656d2d1207b116079 829d56aeb8b1ecadaee33c159ea0c8cf0d5501724b45192819b91721e095d697 6aac6dd749637d52c11ee2f505c96370b72c9b15a166531a18603fea7b002292 57e5ef7eac6e6b7405783aec41a2efed28ae3194cb895a0ee47d6b44d3496e09 e25b1bee5963572dc8c8492b9bf57d46308e0af29d6b479196e161a3d44ae467 c6407d45f2637612e3b6d96daf8be48c32da5f1ddef4e210621ec71fbe7075ca 7fd278c6335fb3978ce2884ccb4da6663770fcac5960e71585a26f69d205e8fa d0ee202e31e8c359f5e97e209b7f4531025f92039fbec7c5d72e9bb523988fe6 0af01c68609be1939e3883c779467cb7305212dbf10b78137864c04889388793 3efcfca0a7fb6320d439157ab3f1b84c9afe94f143b0e213cf9c5c00e9fecd11 a08f57594e24cb9e43c8f09fb911af874479197a06513944844a5bc0b9f69f28 8c7e27cded647153e91a4f35496c5a3f4aec07523fcfd0cfd0c3cf639cbc7f33 bab32891466c2f517c23fdbb1509d7199ca4e476e3f41e84ee384437a5527bc8 78c5e51c91caa24f275de0ab153be7b010d5aa886bfddc8b12634a25cd0410e9 e6c9f55ec5853f874c1b3c675ff90e3fa7c09e8cb57d0abe72196a50967d484a b7519b38c0fd533eb5d8dea8ed8b179ab338a917e0d00f3ea69fe1c8106735c9 8c52b5dd6127931ba938694874f166d9c137b46148f41cb7c97d2380989f04a1 1a7033e75aecfdeb7457444796fe85dee437094ff6213c1af32e61dcceb81ddc 4247daa9aa704e9690131eda5e76905bf17a98ad8098c5a99341b2975e4a1265 3d56286fea57658d54cbd07dedd6ac20686dfdee5fc0ca05ad2d167f5ec11895 d4819619ab0600837b51cd75c1d01508778494151b8b3db0bb3e058f92117e26 432b4a4c61e0da1d13ba50b14dc09062a9f7eccfd9e9b64b91f426394ffb33eb 969ebd51f3a385ad4f4119d8f7ded8aa147f488163180003b6c15f12f4d2888c dbd5d5ce8c4f994154918b39cfb212cfcf0477012bb2e1e0d5f8d6477bb106c3 ea6f80f695bfaa797fdcdabe7e34d3269f3bc7963cc9edf0f39d50d379a16ea7 f26e9b9513b50e12e3e9bf8359ebb376c43a71f3341b0897d138cb950a8f4e25 403f783345abba9819c0bc3a5ba842f33e8589742247c74422911d3069888a73 68cf4c887b1d4b331638c862700d167fe8e16e062fcea22bfef8015d7596e0cc e95a5f9d9d4a24acc4546f2393b118e230bb4ed14c85b1d6522403f21a2964a3 73c9a0b322bd6f78b39bcc55ed9226ee8b4d0b57b964dbbd8b4d43f9528915a6 30430c052bdede05189fcd68c3e44e4f36a90e01b4a1488ab2a7a23e8eac0ab0 931aebdd1c67f0f54cf09322d70d73f7a6cde17853f666672ce3cc7ac1b87088 ea0180cc3bb1f0dd3a16534d86cf6b4a2bf06d9416f0d677d2c6280efca9b3ba 642275b499e5b735784e74ce3fb7bb67576785524721c412354986dbc72a35a3 453d63f26aa518da7bbc021e0f7c275f4ab5b3fa06f40c1a9770aabb996d6f95 3be0fd1804fae21f5780fbf1eda56c5b7d2f62dd1f0efc33db0b0a8fdef9c78a 6cc1925e29c2cfd6332df25bba60c3001770dcd7ff8b66463340c697a6ecafb6 c56b110ff150aaf2ffd920930a0048c9d76e5120534ee55e34f7056e043b3e31 60971af640c3e9faf56c804334d99319f24a78ea69e1ea3bbeb0590f2fc0d8cb e1eae91bee829e10538e7207c45fa2f059beca5c4215d7bd23 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 cleartomark {restore}if% End of font resource %% Font Page 00 /NimbusSansL-Bold-ENC-00 [ /.notdef/R/e/c/i/v/S/t/r/a/m/P/o/s/T/n/A/d/p/I/F/b/u/l/f/M/g/C/H/X/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef /.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef] def /NimbusSansL-Bold-Uni-00 NimbusSansL-Bold-ENC-00 /NimbusSansL-Bold MFEmb %%BeginFont: NimbusSansL-Bold %!PS-AdobeFont-1.0 Composite Font %%FontName: NimbusSansL-Bold-Uni %%Creator: Composite font created by Qt 25 dict begin /FontName /NimbusSansL-Bold-Uni def /PaintType 0 def /FontMatrix[1 0 0 1 0 0]def /FontType 0 def /FMapType 2 def /Encoding [ 0]def /FDepVector [ /NimbusSansL-Bold-Uni-00 findfont ]def FontName currentdict end definefont pop %%EndFont /F1 13.3333/NimbusSansL-Bold-Uni DF /F2 13.3333/NimbusSansL-Regu-Uni DF %%EndSetup %%Page: 1 1 %%BeginPageSetup QI %%EndPageSetup [1 0 0 1 -168 -248]ST 0 0 B 0 0 PE WB W BC 168 248 1114 816 R 255 0 0 P1 NB 413 682 777 VL 454 682 503 778 DL 317 682 771 VL 282 826 941 VL 464 828 471 929 DL 325 905 284 941 DL 428 905 471 929 DL 714 666 454 HL 1055 299 388 VL 810 835 905 VL 908 835 897 VL 765 956 1013 VL 1012 948 1011 VL 971 439 917 784 DL 805 703 784 VL 1135 648 712 VL 1174 648 721 VL 832 604 784 VL 860 604 1098 HL 1165 597 534 VL 1142 483 439 VL 0 0 B 0 0 PE WB NP 413 682 MT 406 693 LT 420 693 LT 413 682 LT CP BF QS 0 0 B 0 0 PE NP 454 682 MT 453 695 LT 465 689 LT 454 682 LT CP BF QS 0 0 B 0 0 PE NP 317 682 MT 310 693 LT 324 693 LT 317 682 LT CP BF QS 0 0 B 0 0 PE NP 282 826 MT 275 837 LT 289 837 LT 282 826 LT CP BF QS 0 0 B 0 0 PE NP 464 828 MT 458 840 LT 471 839 LT 464 828 LT CP BF QS 0 0 B 0 0 PE NP 325 905 MT 312 908 LT 321 917 LT 325 905 LT CP BF QS 0 0 B 0 0 PE NP 428 905 MT 435 916 LT 441 905 LT 428 905 LT CP BF QS 0 0 B 0 0 PE NP 454 666 MT 464 670 LT 476 666 LT 464 662 LT CP BF QS 0 0 B 0 0 PE NP 1055 299 MT 1048 310 LT 1062 310 LT 1055 299 LT CP BF QS 0 0 B 0 0 PE NP 810 835 MT 803 846 LT 817 846 LT 810 835 LT CP BF QS 0 0 B 0 0 PE NP 908 835 MT 901 846 LT 915 846 LT 908 835 LT CP BF QS 0 0 B 0 0 PE NP 765 956 MT 758 967 LT 772 967 LT 765 956 LT CP BF QS 0 0 B 0 0 PE NP 1012 948 MT 1005 959 LT 1019 959 LT 1012 948 LT CP BF QS 0 0 B 0 0 PE NP 917 784 MT 923 775 LT 920 762 LT 914 773 LT CP BF QS 0 0 B 0 0 PE NP 805 703 MT 798 714 LT 812 714 LT 805 703 LT CP BF QS 0 0 B 0 0 PE NP 1135 648 MT 1128 659 LT 1142 659 LT 1135 648 LT CP BF QS 0 0 B 0 0 PE NP 1174 648 MT 1167 659 LT 1181 659 LT 1174 648 LT CP BF QS 0 0 B 0 0 PE NP 832 604 MT 825 615 LT 839 615 LT 832 604 LT CP BF QS 0 0 B 0 0 PE NP 1098 604 MT 1088 600 LT 1076 604 LT 1088 608 LT CP BF QS 0 0 B 0 0 PE NP 1165 534 MT 1161 544 LT 1165 556 LT 1169 544 LT CP BF QS 0 0 B 0 0 PE NP 1142 439 MT 1138 449 LT 1142 461 LT 1146 449 LT CP BF QS 255 0 0 P1 NB 406 693 413 682 DL 420 693 406 HL 420 693 413 682 DL 453 695 454 682 DL 465 689 453 695 DL 465 689 454 682 DL 310 693 317 682 DL 324 693 310 HL 324 693 317 682 DL 275 837 282 826 DL 289 837 275 HL 289 837 282 826 DL 458 840 464 828 DL 471 839 458 840 DL 471 839 464 828 DL 312 908 325 905 DL 321 917 312 908 DL 321 917 325 905 DL 435 916 428 905 DL 441 905 435 916 DL 441 905 428 HL 464 670 454 666 DL 464 662 476 666 DL 476 666 464 670 DL 454 666 464 662 DL 1048 310 1055 299 DL 1062 310 1048 HL 1062 310 1055 299 DL 803 846 810 835 DL 817 846 803 HL 817 846 810 835 DL 901 846 908 835 DL 915 846 901 HL 915 846 908 835 DL 758 967 765 956 DL 772 967 758 HL 772 967 765 956 DL 1005 959 1012 948 DL 1019 959 1005 HL 1019 959 1012 948 DL 923 775 917 784 DL 914 773 920 762 DL 920 762 923 775 DL 917 784 914 773 DL 798 714 805 703 DL 812 714 798 HL 812 714 805 703 DL 1128 659 1135 648 DL 1142 659 1128 HL 1142 659 1135 648 DL 1167 659 1174 648 DL 1181 659 1167 HL 1181 659 1174 648 DL 825 615 832 604 DL 839 615 825 HL 839 615 832 604 DL 1088 600 1098 604 DL 1088 608 1076 604 DL 1076 604 1088 600 DL 1098 604 1088 608 DL 1161 544 1165 534 DL 1169 544 1165 556 DL 1165 556 1161 544 DL 1165 534 1169 544 DL 1138 449 1142 439 DL 1146 449 1142 461 DL 1142 461 1138 449 DL 1142 439 1146 449 DL 255 0 0 P1 1 255 255 192 BR 664 905 166 51 R CLSTART 501 657 156 51 ACR CLEND B P1 F1 F 936 Y<000100020003000200040005000200060007000800020009000A000B0008000C00030002000D000D000C0008>155 669 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 908 897 173 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 745 649 163 51 ACR CLEND B P1 1 255 255 192 BR F1 F 928 Y<000E00080009000F000D000A0004000700060007000800020009000A000B0008000C00030002000D000D000C0008>162 913 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 656 1013 207 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 493 765 197 51 ACR CLEND B P1 1 255 255 192 BR F1 F 1044 Y<0010000A001100070012000100020003000200040005000200060007000800020009000A000B0008000C00030002000D000D000C0008>196 661 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 893 1011 214 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 730 763 204 51 ACR CLEND B P1 1 255 255 192 BR F1 F 1042 Y<0010000A001100070012000E00080009000F000D000A0004000700060007000800020009000A000B0008000C00030002000D000D000C0008>203 898 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 786 553 74 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 623 305 64 51 ACR CLEND B P1 1 255 255 192 BR F1 F 584 Y<0013000D000C00060007000800020009000A>63 791 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 799 784 118 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 636 536 108 51 ACR CLEND B P1 1 255 255 192 BR F1 F 815 Y<00060007000800020009000A000B0008000C00030002000D000D000C0008>107 804 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 963 248 174 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 800 0 164 51 ACR CLEND B P1 1 255 255 192 BR F1 F 279 Y<00140008000200020015000C001500010016000F000F00090015001700020013000F0007000200080018000900030002>163 968 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 714 652 91 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 551 404 81 51 ACR CLEND B P1 1 255 255 192 BR F1 F 683 Y<000B000C0008000700190009000F0009001A00020008>79 719 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 971 388 171 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 808 140 161 51 ACR CLEND B P1 1 255 255 192 BR F1 F 419 Y<00060007000800020009000A000B0008000C00030002000D000D000C000800190009000F0009001A00020008>160 976 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 168 941 116 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 5 693 106 51 ACR CLEND B P1 1 255 255 192 BR F1 F 972 Y<0010000A0011000700120010001600110004000C000B000C00080007>104 173 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 399 777 65 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 236 529 55 51 ACR CLEND B P1 1 255 255 192 BR F1 F 808 Y<0019000400110004000B000C00080007>53 404 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 471 929 106 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 308 681 96 51 ACR CLEND B P1 1 255 255 192 BR F1 F 960 Y<0010000A0011000700120019000400110004000B000C00080007>94 476 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 282 775 75 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 119 527 65 51 ACR CLEND B P1 1 255 255 192 BR F1 F 806 Y<0010001600110004000C000B000C00080007>63 287 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 503 778 84 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 340 530 74 51 ACR CLEND B P1 1 255 255 192 BR F1 F 809 Y<001B000C000F00070008000C0017000B000C00080007>72 508 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 325 854 103 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 162 606 93 51 ACR CLEND B P1 1 255 255 192 BR F1 F 885 Y<0010000A001100070012000B000C000800070013000F0018000C>91 330 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 1098 597 78 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 935 349 68 51 ACR CLEND B P1 1 255 255 192 BR F1 F 628 Y<0013000D000C001C0009000F0011001700020008>67 1103 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 317 631 137 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 154 383 127 51 ACR CLEND B P1 1 255 255 192 BR F1 F 662 Y<000B000C00080007>26 372 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 1039 712 107 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 876 464 97 51 ACR CLEND B P1 1 255 255 192 BR F1 F 743 Y<0013000D000C001D000A00040007001C0009000F0011001700020008>96 1044 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 1174 721 108 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 1011 473 98 51 ACR CLEND B P1 1 255 255 192 BR F1 F 752 Y<0013000D000C0001000200030005001C0009000F0011001700020008>97 1179 AT CLO [1 0 0 1 -168 -248]ST 255 0 0 P1 1 255 255 192 BR 1038 483 131 51 R CLO [1 0 0 1 -168 -248]ST CLSTART 875 235 121 51 ACR CLEND B P1 1 255 255 192 BR F1 F 514 Y<0013000D000C001C0009000F001100170002000800190009000F0009001A00020008>120 1043 AT CLO [1 0 0 1 -168 -248]ST CLSTART 694 322 32 32 ACR CLEND 50 d2 P1 NB F2 F 592 Y<0001000200020003>22 866 AT CLO [1 0 0 1 -168 -248]ST CLSTART 694 358 67 25 ACR CLEND 50 d2 P1 NB F2 F 624 Y<000400050006000700080009000A000B000C>59 865 AT CLO [1 0 0 1 -168 -248]ST CLSTART 715 502 32 32 ACR CLEND 50 d2 P1 NB F2 F 772 Y<000D>5 896 AT CLO [1 0 0 1 -168 -248]ST CLSTART 976 193 32 32 ACR CLEND 50 d2 P1 NB F2 F 463 Y<0003>7 1156 AT CLO [1 0 0 1 -168 -248]ST CLSTART 976 201 32 32 ACR CLEND 50 d2 P1 NB F2 F 471 Y<0003>7 1156 AT CLO [1 0 0 1 -168 -248]ST CLSTART 804 193 32 32 ACR CLEND 50 d2 P1 NB F2 F 463 Y<0003>7 984 AT CLO [1 0 0 1 -168 -248]ST CLSTART 512 384 32 32 ACR CLEND 50 d2 P1 NB F2 F 654 Y<0003>7 692 AT CLO [1 0 0 1 -168 -248]ST CLSTART 999 288 32 32 ACR CLEND 50 d2 P1 NB F2 F 558 Y<0003>7 1179 AT CLO [1 0 0 1 -168 -248]ST CLSTART 999 315 32 32 ACR CLEND 50 d2 P1 NB F2 F 585 Y<000D>5 1180 AT CLO [1 0 0 1 -168 -248]ST CLSTART 288 384 32 32 ACR CLEND 50 d2 P1 NB F2 F 654 Y<000D>5 469 AT QP %%Trailer %%Pages: 1 %%DocumentFonts: NimbusSansL-Bold NimbusSansL-Regu %%EOF libffado-2.4.5/libffado/0000755000175000001440000000000014206145612014427 5ustar jwoitheuserslibffado-2.4.5/libffado/60-ffado.rules0000644000175000001440000000545114206145246017015 0ustar jwoitheusersSUBSYSTEM!="firewire", GOTO="ffado_end" # TC GROUP A/S ATTR{vendor}=="0x000166", GROUP="audio", ENV{ID_FFADO}="1" # Mark of the Unicorn, Inc. (aka MOTU) ATTR{vendor}=="0x0001f2", GROUP="audio", ENV{ID_FFADO}="1" # Apogee Electronics Corp. ATTR{vendor}=="0x0003db", GROUP="audio", ENV{ID_FFADO}="1" # Alesis Corporation ATTR{vendor}=="0x000595", GROUP="audio", ENV{ID_FFADO}="1" # Bridgeco Co AG ATTR{vendor}=="0x0007f5", GROUP="audio", ENV{ID_FFADO}="1" # Presonus Corporation ATTR{vendor}=="0x000a92", GROUP="audio", ENV{ID_FFADO}="1" # TerraTec Electronic GmbH ATTR{vendor}=="0x000aac", GROUP="audio", ENV{ID_FFADO}="1" # M-Audio ATTR{vendor}=="0x000d6c", GROUP="audio", ENV{ID_FFADO}="1" # Ego Systems Inc. ATTR{vendor}=="0x000f1b", GROUP="audio", ENV{ID_FFADO}="1" # Loud Technologies Inc. ATTR{vendor}=="0x000ff2", GROUP="audio", ENV{ID_FFADO}="1" # Stanton Magnetics,inc. ATTR{vendor}=="0x001260", GROUP="audio", ENV{ID_FFADO}="1" # Focusrite Audio Engineering Limited ATTR{vendor}=="0x00130e", GROUP="audio", ENV{ID_FFADO}="1" # Echo Digital Audio Corporation ATTR{vendor}=="0x001486", GROUP="audio", ENV{ID_FFADO}="1" # Phonic Corporation ATTR{vendor}=="0x001496", GROUP="audio", ENV{ID_FFADO}="1" # BEHRINGER Spezielle Studiotechnik GmbH ATTR{vendor}=="0x001564", GROUP="audio", ENV{ID_FFADO}="1" # FlexRadio Systems ATTR{vendor}=="0x001c2d", GROUP="audio", ENV{ID_FFADO}="1" # Weiss Engineering Ltd. ATTR{vendor}=="0x001c6a", GROUP="audio", ENV{ID_FFADO}="1" # ROLAND DG CORPORATION ATTR{vendor}=="0x0040ab", GROUP="audio", ENV{ID_FFADO}="1" # DnR ATTR{vendor}=="0x000f64", GROUP="audio", ENV{ID_FFADO}="1" # Avid (for Mbox 3 Pro) ATTR{vendor}=="0x00a07e", GROUP="audio", ENV{ID_FFAOD}="1" # Yamaha (for GO4x devices) ATTR{vendor}=="0x00a0de", GROUP="audio", ENV{ID_FFADO}="1" # Lexicon (from Onix-FW810S) ATTR{vendor}=="0x000fd7", GROUP="audio", ENV{ID_FFADO}="1" # Allen and Heath ATTR{vendor}=="0x0004c4", GROUP="audio", ENV{ID_FFADO}="1" # Midas ATTR{vendor}=="0x10c73f", GROUP="audio", ENV{ID_FFADO}="1" # The devices below are by vendors who make other firewire devices in # addition to their audio interfaces. They need more specific rules to # ensure only audio interfaces are covered here. # Tascam, a subsiduary of TEAC (the OUI is TEAC's) ATTR{vendor}=="0x00022e", ATTR{model}=="0x010067", GROUP="audio", ENV{ID_FFADO}="1" # The devices below abuse another Vendor's ID, and therefore we need more advanced rules for those. # CME, Matrix K FW ATTR{vendor}=="0x00000a", ATTR{model}=="0x030000", ATTR{units}=="*0x00a02d:0x010001*", GROUP="audio", ENV{ID_FFADO}="1" # Mackie, Onyx FireWire ATTR{vendor}=="0x00000f", ATTR{model}=="0x01006?", ATTR{units}=="*0x00a02d:0x010001*", GROUP="audio", ENV{ID_FFADO}="1" # RME ATTR{vendor}=="0x000a35", ATTR{units}=="0x000a35:0x00000[1234]", GROUP="audio", ENV{ID_FFADO}="1" LABEL="ffado_end" libffado-2.4.5/libffado/SConscript0000644000175000001440000000204114206145246016441 0ustar jwoitheusers# # Copyright (C) 2007-2008 Arnold Krille # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os.path Import( 'env' ) env = env.Clone() env.ScanReplace( "ffado.h.in" ) env.Alias( 'install', env.Install( os.path.join(env['includedir'],"libffado"), "ffado.h" ) ) env.Alias( 'install', env.Install( env['udevdir'], "60-ffado.rules" ) ) libffado-2.4.5/libffado/ffado.h.in0000644000175000001440000004154414206145246016277 0ustar jwoitheusers/* ffado.h * * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FFADO_H #define FFADO_H #define FFADO_API_VERSION $FFADO_API_VERSION #define FFADO_MAX_NAME_LEN 256 #include #define FFADO_STREAMING_MAX_URL_LENGTH 2048 #define FFADO_IGNORE_CAPTURE (1<<0) #define FFADO_IGNORE_PLAYBACK (1<<1) enum ffado_direction { FFADO_CAPTURE = 0, FFADO_PLAYBACK = 1, }; typedef struct ffado_handle* ffado_handle_t; #ifdef __cplusplus extern "C" { #endif #ifdef __APPLE__ #define WEAK_ATTRIBUTE weak_import #else #define WEAK_ATTRIBUTE __weak__ #endif #ifdef __GNUC__ #define FFADO_WEAK_EXPORT __attribute__((WEAK_ATTRIBUTE)) #else /* Add support for non-gcc platforms here */ #endif /* ABI stuff */ const char* ffado_get_version(); int ffado_get_api_version(); /* various function */ /* The basic operation of the API is as follows: * * ffado_streaming_init() * ffado_streaming_start() * while(running) { * retval = ffado_streaming_wait(); * if (retval == -1) { * ffado_streaming_reset(); * continue; * } * * ffado_streaming_transfer_capture_buffers(dev); * * for(all channels) { * // For both audio and MIDI channels, captured data is available * // in the buffer previously set with a call to * // ffado_streaming_set_capture_stream_buffer(dev, channel, buffer) * switch (channel_type) { * case audio: * // Process incoming audio as needed * case midi: * // Process incoming MIDI data as needed * } * } * * for(all channels) { * // For both audio and MIDI channels, data is written to buffers * // previously associated with the playback channel streams using * // ffado_streaming_set_playback_stream_buffer(dev, channel, buffer) * switch (channel_type) { * case audio: * // Set audio playback buffer contents * case midi: * // Set MIDI playback buffer contents * } * } * ffado_streaming_transfer_playback_buffers(dev); * * } * ffado_streaming_stop(); * ffado_streaming_finish(); * */ typedef struct _ffado_device ffado_device_t; /** * The sample format used by the ffado streaming API */ typedef unsigned int ffado_sample_t; // FIXME typedef unsigned int ffado_nframes_t; #define FFADO_MAX_SPECSTRING_LENGTH 256 #define FFADO_MAX_SPECSTRINGS 64 /** * This struct serves to define the devices that should be used by the library * device_spec_strings is an array of pointers that should contain nb_device_spec_strings * valid pointers to strings. * * The spec strings should be null terminated and can be no longer * than FFADO_MAX_SPECSTRINGS. * * nb_device_spec_strings < FFADO_MAX_SPECSTRING_LENGTH * nb_device_spec_strings >= 0 * * If nb_device_spec_strings == 0, all busses are scanned for attached devices, and * all found devices that are supported are combined into one large pseudo-device. The * device order is defined by the GUID of the device. Devices with lower GUID's will * be the first ones. * * If multiple device specifications are present, the device order is defined as follows: * - device(s) that correspond to a spec string with a lower index will be added before * devices from higher indexes. * - if a spec string results in multiple devices, they are sorted by GUID unless the * spec format dictates otherwise. * * The actual meaning of the device specification should be one of the following: * - Format 1: "hw:x[,y[,z]]" * x = the FireWire bus to use ('port' in raw1394 terminology) * (mandatory) * y = the node id the device currently has (bus resets might change that, but FFADO * will track these changes and keep using the device specified on startup) * (optional) * z = the stream direction to use. * 0 => capture (record) channels only * 1 => playback channels only * other/unspecified => both playback and capture * (optional) * * - Format 2: the device alias as defined in the ffado config file (UNIMPLEMENTED) */ typedef struct ffado_device_info { unsigned int nb_device_spec_strings; char **device_spec_strings; /* add some extra space to allow for future API extention w/o breaking binary compatibility */ int32_t reserved[32]; } ffado_device_info_t; /** * Structure to pass the options to the ffado streaming code. */ typedef struct ffado_options { /* driver related setup */ int32_t sample_rate; /* * you can specify a value here or -1 to autodetect */ /* buffer setup */ int32_t period_size; /* one period is the amount of frames that * has to be sent or received in order for * a period boundary to be signalled. * (unit: frames) */ int32_t nb_buffers; /* the size of the frame buffer (in periods) */ /* packetizer thread options */ int32_t realtime; int32_t packetizer_priority; /* verbosity */ int32_t verbose; /* slave mode */ int32_t slave_mode; /* snoop mode */ int32_t snoop_mode; /* add some extra space to allow for future API extention w/o breaking binary compatibility */ int32_t reserved[24]; } ffado_options_t; /** * The types of streams supported by the API * * A ffado_audio type stream is a stream that consists of successive samples. * The format is a 24bit UINT in host byte order, aligned as the 24LSB's of the * 32bit UINT of the read/write buffer. * The wait operation looks at this type of streams only. * * A ffado_midi type stream is a stream of midi bytes. The bytes are 8bit UINT, * aligned as the first 8LSB's of the 32bit UINT of the read/write buffer. * * A ffado_control type stream is a stream that provides control information. The * format of this control information is undefined, and the stream should be ignored. * */ typedef enum { ffado_stream_type_invalid = -1, ffado_stream_type_unknown = 0, ffado_stream_type_audio = 1, ffado_stream_type_midi = 2, ffado_stream_type_control = 3, } ffado_streaming_stream_type; /** * * Audio data types known to the API * */ typedef enum { ffado_audio_datatype_error = -1, ffado_audio_datatype_int24 = 0, ffado_audio_datatype_float = 1, } ffado_streaming_audio_datatype; /** * * Wait responses * */ typedef enum { ffado_wait_shutdown = -3, ffado_wait_error = -2, ffado_wait_xrun = -1, ffado_wait_ok = 0, } ffado_wait_response; /** * Initializes the streaming from/to a FFADO device. A FFADO device * is a virtual device composed of several BeBoB or compatible devices, * linked together in one sync domain. * * This prepares all IEEE1394 related stuff and sets up all buffering. * It elects a sync master if nescessary. * * @param device_info provides a way to specify the virtual device * @param options options regarding buffers, ieee1394 setup, ... * * @return Opaque device handle if successful. If this is NULL, the * init operation failed. * */ ffado_device_t *ffado_streaming_init( ffado_device_info_t device_info, ffado_options_t options); /** * This permits the setting of the period size at some time after * initialisation. The primary use of this function is to support the * setbufsize functionality of JACK. * * @param dev the ffado device * @param period the new period size * @return 0 on success, non-zero if an error occurred */ int ffado_streaming_set_period_size(ffado_device_t *dev, unsigned int period) FFADO_WEAK_EXPORT; /** * preparation should be done after setting all per-stream parameters * the way you want them. being buffer data type etc... * * @param dev the ffado device * @return */ int ffado_streaming_prepare(ffado_device_t *dev); /** * Finishes the FFADO streaming. Cleans up all internal data structures * and terminates connections. * * @param dev the ffado device to be closed. */ void ffado_streaming_finish(ffado_device_t *dev); /** * Returns the amount of capture channels available * * @param dev the ffado device * * @return the number of capture streams present & active on the device. * can be 0. returns -1 upon error. */ int ffado_streaming_get_nb_capture_streams(ffado_device_t *dev); /** * Returns the amount of playack channels available * * @param dev the ffado device * * @return the number of playback streams present & active on the device. * can be 0. returns -1 upon error. */ int ffado_streaming_get_nb_playback_streams(ffado_device_t *dev); /** * Copies the capture channel name into the specified buffer * * @param dev the ffado device * @param number the stream number * @param buffer the buffer to copy the name into. has to be allocated. * @param buffersize the size of the buffer * * @return the number of characters copied into the buffer */ int ffado_streaming_get_capture_stream_name(ffado_device_t *dev, int number, char* buffer, size_t buffersize); /** * Copies the playback channel name into the specified buffer * * @param dev the ffado device * @param number the stream number * @param buffer the buffer to copy the name into. has to be allocated. * @param buffersize the size of the buffer * * @return the number of characters copied into the buffer */ int ffado_streaming_get_playback_stream_name(ffado_device_t *dev, int number, char* buffer, size_t buffersize); /** * Returns the type of a capture channel * * @param dev the ffado device * @param number the stream number * * @return the channel type */ ffado_streaming_stream_type ffado_streaming_get_capture_stream_type(ffado_device_t *dev, int number); /** * Returns the type of a playback channel * * @param dev the ffado device * @param number the stream number * * @return the channel type */ ffado_streaming_stream_type ffado_streaming_get_playback_stream_type(ffado_device_t *dev, int number); /* * * Note: buffer handling will change in order to allow setting the sample type for *_read and *_write * and separately indicate if you want to use a user buffer or a managed buffer. * */ /** * Sets the decode buffer for the stream. This allows for zero-copy decoding. * The call to ffado_streaming_transfer_buffers will decode one period of the stream to * this buffer. Make sure it is large enough. * * @param dev the ffado device * @param number the stream number * @param buff a pointer to the sample buffer, make sure it is large enough * i.e. sizeof(your_sample_type)*period_size * @param t the type of the buffer. this determines sample type and the decode function used. * * @return -1 on error, 0 on success */ int ffado_streaming_set_capture_stream_buffer(ffado_device_t *dev, int number, char *buff); int ffado_streaming_capture_stream_onoff(ffado_device_t *dev, int number, int on); /** * Sets the encode buffer for the stream. This allows for zero-copy encoding (directly to the events). * The call to ffado_streaming_transfer_buffers will encode one period of the stream from * this buffer to the event buffer. * * @param dev the ffado device * @param number the stream number * @param buff a pointer to the sample buffer * @param t the type of the buffer. this determines sample type and the decode function used. * * @return -1 on error, 0 on success */ int ffado_streaming_set_playback_stream_buffer(ffado_device_t *dev, int number, char *buff); int ffado_streaming_playback_stream_onoff(ffado_device_t *dev, int number, int on); ffado_streaming_audio_datatype ffado_streaming_get_audio_datatype(ffado_device_t *dev); int ffado_streaming_set_audio_datatype(ffado_device_t *dev, ffado_streaming_audio_datatype t); /** * preparation should be done after setting all per-stream parameters * the way you want them. being buffer data type etc... * * @param dev * @return */ int ffado_streaming_prepare(ffado_device_t *dev); /** * Starts the streaming operation. This initiates the connections to the FFADO devices and * starts the packet handling thread(s). This has to be called before any I/O can occur. * * @param dev the ffado device * * @return 0 on success, -1 on failure. */ int ffado_streaming_start(ffado_device_t *dev); /** * Stops the streaming operation. This closes the connections to the FFADO devices and * stops the packet handling thread(s). * * @param dev the ffado device * * @return 0 on success, -1 on failure. */ int ffado_streaming_stop(ffado_device_t *dev); /** * Resets the streaming as if it was stopped and restarted. The difference is that the connections * are not nescessarily broken and restored. * * All buffers are reset in the initial state and all data in them is lost. * * @param dev the ffado device * * @return 0 on success, -1 on failure. */ int ffado_streaming_reset(ffado_device_t *dev); /** * Waits until there is at least one period of data available on all capture connections and * room for one period of data on all playback connections * * @param dev the ffado device * * @return The number of frames ready. -1 when a problem occurred. */ ffado_wait_response ffado_streaming_wait(ffado_device_t *dev); /** * Transfer & decode the events from the packet buffer to the sample buffers * * This should be called after the wait call returns, before reading/writing the sample buffers * with ffado_streaming_[read|write]. * * The purpose is to allow more precise timing information. ffado_streaming_wait returns as soon as the * period boundary is crossed, and can therefore be used to determine the time instant of this crossing (e.g. jack DLL). * * The actual decoding work is done in this function and can therefore be omitted in this timing calculation. * Note that you HAVE to call this function in order for the buffers not to overflow, and only call it when * ffado_streaming_wait doesn't indicate a buffer xrun (xrun handler resets buffer). * * If user supplied playback buffers are specified with ffado_streaming_set_playback_buffers * their contents should be valid before calling this function. * If user supplied capture buffers are specified with ffado_streaming_set_capture_buffers * their contents are updated in this function. * * Use either ffado_streaming_transfer_buffers to transfer all buffers at once, or use * ffado_streaming_transfer_playback_buffers and ffado_streaming_transfer_capture_buffers * to have more control. Don't use both. * * @param dev the ffado device * @return -1 on error. */ int ffado_streaming_transfer_buffers(ffado_device_t *dev); /** * Transfer & encode the events from the sample buffers to the packet buffer * * This should be called after the wait call returns, after writing the sample buffers * with ffado_streaming_write. * * If user supplied playback buffers are specified with ffado_streaming_set_playback_buffers * their contents should be valid before calling this function. * * Use either ffado_streaming_transfer_buffers to transfer all buffers at once, or use * ffado_streaming_transfer_playback_buffers and ffado_streaming_transfer_capture_buffers * to have more control. Don't use both. * * @param dev the ffado device * @return -1 on error. */ int ffado_streaming_transfer_playback_buffers(ffado_device_t *dev); /** * Transfer & decode the events from the packet buffer to the sample buffers * * This should be called after the wait call returns, before reading the sample buffers * with ffado_streaming_read. * * If user supplied capture buffers are specified with ffado_streaming_set_capture_buffers * their contents are updated in this function. * * Use either ffado_streaming_transfer_buffers to transfer all buffers at once, or use * ffado_streaming_transfer_playback_buffers and ffado_streaming_transfer_capture_buffers * to have more control. Don't use both. * * @param dev the ffado device * @return -1 on error. */ int ffado_streaming_transfer_capture_buffers(ffado_device_t *dev); #ifdef __cplusplus } #endif #endif /* FFADO_STREAMING */ libffado-2.4.5/libffado/.gitignore0000644000175000001440000000001012132617070016404 0ustar jwoitheusersffado.h libffado-2.4.5/src/0000755000175000001440000000000014206145613013451 5ustar jwoitheuserslibffado-2.4.5/src/DeviceStringParser.cpp0000644000175000001440000002560114206145246017726 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "DeviceStringParser.h" #include #include #include #include #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" IMPL_DEBUG_MODULE( DeviceStringParser, DeviceStringParser, DEBUG_LEVEL_NORMAL ); DeviceStringParser::DeviceString::DeviceString(DeviceStringParser& parent) : m_Parent(parent) , m_node( -1 ) , m_port( -1 ) , m_guid( 0 ) , m_String("") , m_Type(eInvalid) , m_debugModule( parent.m_debugModule ) { } DeviceStringParser::DeviceString::~DeviceString() { } bool DeviceStringParser::DeviceString::parse(std::string s) { m_String = s; debugOutput(DEBUG_LEVEL_VERBOSE, "parse: %s\n", s.c_str()); std::string prefix = s.substr(0,3); if(s.compare(0,3,"hw:")==0) { m_Type = eBusNode; std::string detail = s.substr(3); std::string::size_type comma_pos = detail.find_first_of(","); if(comma_pos == std::string::npos) { // node is unspecified m_node = -1; std::string port = detail; errno = 0; m_port = strtol(port.c_str(), NULL, 0); if(errno) { m_Type = eInvalid; m_port = -1; m_node = -1; debugOutput(DEBUG_LEVEL_VERBOSE, "failed to parse port\n"); return false; } } else { std::string port = detail.substr(0, comma_pos); std::string node = detail.substr(comma_pos+1); errno = 0; m_port = strtol(port.c_str(), NULL, 0); if(errno) { m_Type = eInvalid; m_port = -1; m_node = -1; debugOutput(DEBUG_LEVEL_VERBOSE, "failed to parse port\n"); return false; } errno = 0; m_node = strtol(node.c_str(), NULL, 0); if(errno) { m_Type = eInvalid; m_port = -1; m_node = -1; debugOutput(DEBUG_LEVEL_VERBOSE, "failed to parse node\n"); return false; } } } else if (s.compare(0,5,"guid:")==0) { std::string detail = s.substr(5); m_Type = eGUID; errno = 0; m_guid = strtoll(detail.c_str(), NULL, 0); if(errno) { m_Type = eInvalid; m_guid = 0; debugOutput(DEBUG_LEVEL_VERBOSE, "failed to parse guid\n"); return false; } } else { m_Type = eInvalid; debugOutput(DEBUG_LEVEL_VERBOSE, "invalid\n"); return false; } return true; } bool DeviceStringParser::DeviceString::isValidString(std::string s) { std::string prefix = s.substr(0,3); if(s.compare(0,3,"hw:")==0) { std::string detail = s.substr(3); std::string::size_type comma_pos = detail.find_first_of(","); if(comma_pos == std::string::npos) { std::string port = detail; errno = 0; strtol(port.c_str(), NULL, 0); if(errno) { return false; } } else { std::string port = detail.substr(0, comma_pos); std::string node = detail.substr(comma_pos+1); errno = 0; strtol(port.c_str(), NULL, 0); if(errno) { return false; } errno = 0; strtol(node.c_str(), NULL, 0); if(errno) { return false; } } } else if (s.compare(0,5,"guid:")==0) { std::string detail = s.substr(5); errno = 0; strtoll(detail.c_str(), NULL, 0); if(errno) { return false; } } else { return false; } return true; } bool DeviceStringParser::DeviceString::match(ConfigRom& configRom) { debugOutput(DEBUG_LEVEL_VERBOSE, "match %p (%s)\n", &configRom, configRom.getGuidString().c_str()); bool match; switch(m_Type) { case eBusNode: if(m_port < 0) { debugWarning("Need at least a port spec\n"); return false; } match = configRom.get1394Service().getPort() == m_port; if(m_node >= 0) { match &= ((configRom.getNodeId() & 0x3F) == m_node); } if(match) { debugOutput(DEBUG_LEVEL_VERBOSE, "(eBusNode) device matches device string %s\n", m_String.c_str()); } return match; case eGUID: //GUID should not be 0 match = m_guid && (m_guid == configRom.getGuid()); if(match) { debugOutput(DEBUG_LEVEL_VERBOSE, "(eGUID) device matches device string %s\n", m_String.c_str()); } return match; case eInvalid: default: debugError("invalid DeviceString type (%d)\n", m_Type); return false; } return false; } bool DeviceStringParser::DeviceString::operator==(const DeviceString& x) { bool retval; switch(m_Type) { case eBusNode: retval = (m_port == x.m_port) && (m_node == x.m_node); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "eBusNode %d,%d == %d,%d? %d\n", m_port, m_node, x.m_port, x.m_node, retval); return retval; case eGUID: retval = m_guid && (m_guid == x.m_guid); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "eGUID 0x%016" PRIX64 " == 0x%016" PRIX64 "? %d\n", m_guid, x.m_guid, retval); return retval; case eInvalid: debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "eInvalid \n"); default: return false; } } void DeviceStringParser::DeviceString::show() { debugOutput(DEBUG_LEVEL_INFO, "string: %s\n", m_String.c_str()); switch(m_Type) { case eBusNode: debugOutput(DEBUG_LEVEL_INFO, "type: eBusNode\n"); debugOutput(DEBUG_LEVEL_INFO, " Port: %d, Node: %d\n", m_port, m_node); break; case eGUID: debugOutput(DEBUG_LEVEL_INFO, "type: eGUID\n"); debugOutput(DEBUG_LEVEL_INFO, " GUID: %016" PRIX64 "\n", m_guid); break; case eInvalid: default: debugOutput(DEBUG_LEVEL_INFO, "type: eInvalid\n"); break; } } // ------------------------ DeviceStringParser::DeviceStringParser() {} DeviceStringParser::~DeviceStringParser() { while(m_DeviceStrings.size()) { DeviceString *tmp = m_DeviceStrings.at(0); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "removing device string: %p\n", tmp); m_DeviceStrings.erase(m_DeviceStrings.begin()); delete tmp; } } bool DeviceStringParser::parseString(std::string s) { debugOutput(DEBUG_LEVEL_VERBOSE, "parse: %s\n", s.c_str()); std::string::size_type next_sep; std::string tmp = s; do { debugOutput(DEBUG_LEVEL_VERBOSE, " left: %s\n", tmp.c_str()); next_sep = tmp.find_first_of(";"); std::string to_parse = tmp.substr(0, next_sep); DeviceString *d = new DeviceString(*this); if(d == NULL) { debugError("failed to allocate memory for device string\n"); continue; } if(d->parse(to_parse)) { addDeviceString(d); } else { debugWarning("Failed to parse device substring: \"%s\"\n", to_parse.c_str()); delete d; } tmp = tmp.substr(next_sep+1); } while(tmp.size() && next_sep != std::string::npos); pruneDuplicates(); return true; } bool DeviceStringParser::isValidString(std::string s) { debugOutput(DEBUG_LEVEL_VERBOSE, "isvalid? %s\n", s.c_str()); return DeviceString::isValidString(s); } bool DeviceStringParser::match(ConfigRom& c) { return matchPosition(c) != -1; } int DeviceStringParser::matchPosition(ConfigRom& c) { int pos = 0; for ( DeviceStringVectorIterator it = m_DeviceStrings.begin(); it != m_DeviceStrings.end(); ++it ) { if((*it)->match(c)) { return pos; } pos++; } return -1; } bool DeviceStringParser::addDeviceString(DeviceString *o) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "adding device string: %p\n", o); if (hasDeviceString(o)){ return false; } m_DeviceStrings.push_back(o); return true; } bool DeviceStringParser::removeDeviceString(DeviceString *o) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "removing device string: %p\n", o); int i=findDeviceString(o); if (i<0) { debugOutput(DEBUG_LEVEL_VERBOSE, "not found\n"); return false; } else { DeviceString *tmp = m_DeviceStrings.at(i); m_DeviceStrings.erase(m_DeviceStrings.begin()+i); delete tmp; return true; } } bool DeviceStringParser::hasDeviceString(DeviceString *o) { return (findDeviceString(o) >= 0); } int DeviceStringParser::findDeviceString(DeviceString *o) { int i=0; for ( DeviceStringVectorIterator it = m_DeviceStrings.begin(); it != m_DeviceStrings.end(); ++it ) { if(*it == o) { return i; } i++; } return -1; } void DeviceStringParser::pruneDuplicates() { DeviceStringVector duplicates; // find duplicates for ( DeviceStringVectorIterator it = m_DeviceStrings.begin(); it != m_DeviceStrings.end(); ++it ) { for ( DeviceStringVectorIterator it2 = it+1; it2 != m_DeviceStrings.end(); ++it2 ) { if(**it == **it2) { duplicates.push_back(*it2); } } } // remove duplicates for ( DeviceStringVectorIterator it = duplicates.begin(); it != duplicates.end(); ++it ) { removeDeviceString(*it); } } void DeviceStringParser::show() { debugOutput(DEBUG_LEVEL_INFO, "DeviceStringParser: %p\n", this); for ( DeviceStringVectorIterator it = m_DeviceStrings.begin(); it != m_DeviceStrings.end(); ++it ) { (*it)->show(); } } void DeviceStringParser::setVerboseLevel(int i) { setDebugLevel(i); } libffado-2.4.5/src/DeviceStringParser.h0000644000175000001440000000506214206145246017372 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_DEVICESTRINGPARSER__ #define __FFADO_DEVICESTRINGPARSER__ #include "debugmodule/debugmodule.h" #include #include #include class ConfigRom; class DeviceStringParser { protected: class DeviceString { public: enum eType { eInvalid = 0, eBusNode = 1, // old-style hw:bus,node eGUID = 2, // GUID match }; DeviceString(DeviceStringParser&); ~DeviceString(); bool parse(std::string s); bool match(ConfigRom &); std::string getString() {return m_String;}; void show(); bool operator==(const DeviceString& x); static bool isValidString(std::string s); private: DeviceStringParser & m_Parent; int m_node; int m_port; uint64_t m_guid; std::string m_String; enum eType m_Type; DECLARE_DEBUG_MODULE_REFERENCE; }; public: DeviceStringParser(); virtual ~DeviceStringParser(); int countDeviceStrings() {return m_DeviceStrings.size();}; bool match(ConfigRom &); int matchPosition(ConfigRom& c); bool parseString(std::string s); void show(); void setVerboseLevel(int i); static bool isValidString(std::string s); protected: bool removeDeviceString(DeviceString *); bool addDeviceString(DeviceString *); bool hasDeviceString(DeviceString *); private: int findDeviceString(DeviceString *); void pruneDuplicates(); typedef std::vector< DeviceString* > DeviceStringVector; typedef std::vector< DeviceString* >::iterator DeviceStringVectorIterator; DeviceStringVector m_DeviceStrings; protected: DECLARE_DEBUG_MODULE; }; #endif /* __FFADO_DEVICESTRINGPARSER__ */ libffado-2.4.5/src/SConscript0000644000175000001440000002606214206145246015473 0ustar jwoitheusers# # Copyright (C) 2007-2008 Arnold Krille # Copyright (C) 2007-2008 Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os Import( 'env' ) libenv = env.Clone() libenv.MergeFlags( "-I#/ -I#/src" ) ffado_source = env.Split( '\ devicemanager.cpp \ ffado.cpp \ ffadodevice.cpp \ debugmodule/debugmodule.cpp \ DeviceStringParser.cpp \ libieee1394/ARMHandler.cpp \ libieee1394/configrom.cpp \ libieee1394/csr1212.c \ libieee1394/CycleTimerHelper.cpp \ libieee1394/ieee1394service.cpp \ libieee1394/IEC61883.cpp \ libieee1394/IsoHandlerManager.cpp \ libstreaming/StreamProcessorManager.cpp \ libstreaming/util/cip.c \ libstreaming/generic/StreamProcessor.cpp \ libstreaming/generic/Port.cpp \ libstreaming/generic/PortManager.cpp \ libutil/cmd_serialize.cpp \ libutil/DelayLockedLoop.cpp \ libutil/IpcRingBuffer.cpp \ libutil/PacketBuffer.cpp \ libutil/Configuration.cpp \ libutil/OptionContainer.cpp \ libutil/PosixMessageQueue.cpp \ libutil/PosixSharedMemory.cpp \ libutil/PosixMutex.cpp \ libutil/PosixThread.cpp \ libutil/ringbuffer.c \ libutil/StreamStatistics.cpp \ libutil/SystemTimeSource.cpp \ libutil/TimestampedBuffer.cpp \ libutil/Watchdog.cpp \ libcontrol/Element.cpp \ libcontrol/BasicElements.cpp \ libcontrol/MatrixMixer.cpp \ libcontrol/CrossbarRouter.cpp \ libcontrol/ClockSelect.cpp \ libcontrol/Nickname.cpp \ ') if env['SERIALIZE_USE_EXPAT']: ffado_source.append('libutil/serialize_expat.cpp') ffado_source.append('libutil/serialize_expat_xml.cpp') else: ffado_source.append('libutil/serialize_libxml.cpp') bebob_source = env.Split( '\ bebob/bebob_avdevice.cpp \ bebob/bebob_avdevice_subunit.cpp \ bebob/bebob_avplug.cpp \ bebob/bebob_dl_bcd.cpp \ bebob/bebob_dl_codes.cpp \ bebob/bebob_dl_mgr.cpp \ bebob/bebob_functionblock.cpp \ bebob/bebob_mixer.cpp \ bebob/focusrite/focusrite_generic.cpp \ bebob/focusrite/focusrite_saffire.cpp \ bebob/focusrite/focusrite_saffirepro.cpp \ bebob/focusrite/focusrite_cmd.cpp \ bebob/terratec/terratec_device.cpp \ bebob/terratec/terratec_cmd.cpp \ bebob/edirol/edirol_fa101.cpp \ bebob/edirol/edirol_fa66.cpp \ bebob/esi/quatafire610.cpp \ bebob/mackie/onyxmixer.cpp \ bebob/yamaha/yamaha_cmd.cpp \ bebob/yamaha/yamaha_avdevice.cpp \ bebob/maudio/normal_avdevice.cpp \ bebob/maudio/special_avdevice.cpp \ bebob/maudio/special_mixer.cpp \ bebob/presonus/firebox_avdevice.cpp \ bebob/presonus/inspire1394_avdevice.cpp \ ' ) bebob_pkgdata = env.Split( '\ bebob/maudio/refdesign.xml \ bebob/maudio/fw410.xml \ bebob/maudio/fwap.xml \ ' ) genericavc_source = env.Split( '\ genericavc/avc_avdevice.cpp \ genericavc/stanton/scs.cpp \ ' ) genericavc_pkgdata = env.Split( '\ ' ) 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/fireworks_session_block.cpp \ fireworks/audiofire/audiofire_device.cpp \ ' ) fireworks_pkgdata = env.Split( '\ ' ) oxford_source = env.Split( '\ oxford/oxford_device.cpp \ libstreaming/amdtp-oxford/AmdtpOxfordReceiveStreamProcessor.cpp \ ' ) oxford_pkgdata = env.Split( '\ ' ) motu_source = env.Split( '\ motu/motu_avdevice.cpp \ motu/motu_controls.cpp \ motu/motu_mark3_controls.cpp \ motu/motu_mixerdefs.cpp \ motu/motu_mark3_mixerdefs.cpp \ motu/motu_mixer.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 \ dice/dice_firmware_loader.cpp \ dice/dice_eap.cpp \ dice/focusrite/focusrite_eap.cpp \ dice/focusrite/saffire_pro40.cpp \ dice/focusrite/saffire_pro26.cpp \ dice/focusrite/saffire_pro24.cpp \ dice/focusrite/saffire_pro14.cpp \ dice/focusrite/saffire_56.cpp \ dice/maudio/profire_2626.cpp \ dice/presonus/firestudio_tube.cpp \ dice/presonus/firestudio_project.cpp \ dice/presonus/firestudio_mobile.cpp \ ' ) bounce_source = env.Split( '\ bounce/bounce_avdevice.cpp \ bounce/bounce_slave_avdevice.cpp \ ' ) metric_halo_source = env.Split( '\ metrichalo/mh_avdevice.cpp \ ' ) rme_source = env.Split( '\ rme/rme_shm.cpp \ rme/rme_avdevice.cpp \ rme/rme_avdevice_settings.cpp \ rme/fireface_flash.cpp \ rme/fireface_hw.cpp \ rme/fireface_settings_ctrls.cpp \ libstreaming/rme/RmePort.cpp \ libstreaming/rme/RmePortInfo.cpp \ libstreaming/rme/RmeReceiveStreamProcessor.cpp \ libstreaming/rme/RmeTransmitStreamProcessor.cpp \ ' ) digidesign_source = env.Split( '\ digidesign/digidesign_avdevice.cpp \ libstreaming/digidesign/DigidesignPort.cpp \ libstreaming/digidesign/DigidesignPortInfo.cpp \ libstreaming/digidesign/DigidesignReceiveStreamProcessor.cpp \ libstreaming/digidesign/DigidesignTransmitStreamProcessor.cpp \ ' ) amdtp_source = env.Split( '\ libstreaming/amdtp/AmdtpPort.cpp \ libstreaming/amdtp/AmdtpPortInfo.cpp \ libstreaming/amdtp/AmdtpReceiveStreamProcessor.cpp \ libstreaming/amdtp/AmdtpTransmitStreamProcessor.cpp \ ' ) libavc_source = env.Split( '\ 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_descriptor_audio.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 \ ' ) source = ffado_source pkgdata = [] if env['ENABLE_BEBOB']: env['ENABLE_GENERICAVC'] = True libenv.MergeFlags( "-DENABLE_BEBOB" ) source += bebob_source pkgdata += bebob_pkgdata if env['ENABLE_FIREWORKS']: env['ENABLE_GENERICAVC'] = True libenv.MergeFlags( "-DENABLE_FIREWORKS" ) source += fireworks_source pkgdata += fireworks_pkgdata if env['ENABLE_OXFORD']: env['ENABLE_GENERICAVC'] = True libenv.MergeFlags( "-DENABLE_OXFORD" ) source += oxford_source pkgdata += oxford_pkgdata if env['ENABLE_MOTU']: libenv.MergeFlags( "-DENABLE_MOTU" ) source += motu_source if env['ENABLE_DICE']: env['ENABLE_GENERICAVC'] = True libenv.MergeFlags( "-DENABLE_DICE" ) source += dice_source if env['ENABLE_METRIC_HALO']: libenv.MergeFlags( "-DENABLE_METRIC_HALO" ) source += metric_halo_source if env['ENABLE_RME']: libenv.MergeFlags( "-DENABLE_RME" ) source += rme_source if env['ENABLE_DIGIDESIGN']: libenv.MergeFlags( "-DENABLE_DIGIDESIGN" ) source += digidesign_source if env['ENABLE_BOUNCE']: env['ENABLE_GENERICAVC'] = True libenv.MergeFlags( "-DENABLE_BOUNCE" ) source += bounce_source if env['ENABLE_GENERICAVC']: libenv.MergeFlags( "-DENABLE_GENERICAVC" ) source += libavc_source source += amdtp_source source += genericavc_source pkgdata += genericavc_pkgdata if not env.GetOption( "clean" ): libenv.MergeFlags( "-lrt -lpthread" ) libenv.MergeFlags( env['LIBRAW1394_FLAGS'].decode() ) libenv.MergeFlags( env['LIBIEC61883_FLAGS'].decode() ) libenv.MergeFlags( env['LIBCONFIG_FLAGS'].decode() ) if not env['SERIALIZE_USE_EXPAT']: if 'LIBXML30_FLAGS' in env : libenv.MergeFlags( env['LIBXML30_FLAGS'].decode() ) if not('LIBXML30_FLAGS' in env) : libenv.MergeFlags( env['LIBXML26_FLAGS'].decode() ) else: libenv.PrependUnique( LIBS=["expat"] ) libenv.MergeFlags( "-DSERIALIZE_USE_EXPAT" ) if env['REQUIRE_LIBAVC']: libenv.MergeFlags( env['LIBAVC1394_FLAGS'].decode() ) libname_versioned = "libffado.so.%s" % libenv['VERSION'] libname_versioned_short = "libffado.so.%s" % libenv['VERSION'].split('.')[0] libenv.MergeFlags( "-Wl,-soname=%s" % libname_versioned_short ) ffadolib = libenv.SharedLibrary( "ffado", source ) #libenv.Install( "$libdir", ffadolib ) installer = libenv.InstallAs ( "$libdir/%s" % libname_versioned , ffadolib ) # if stripping would be something for us #libenv.AddPostAction(installer, [['strip', env['STRIPFLAGS'], t[0].path]]) # make the required links libenv.NoCache( '$libdir/%s' % libname_versioned ) libenv.AddPostAction(installer, [['rm', '-f', '$libdir/libffado.so', '$libdir/%s' % libname_versioned_short], ['cd', '$libdir', '&&','ln', '-s', libname_versioned_short, 'libffado.so', '&&','ln', '-s', installer[0].name, libname_versioned_short, ] ]) if libenv['BUILD_STATIC_LIB']: ffadolib_static = libenv.StaticLibrary( "ffado", source ) # # Install the pkgdata to $sharedir # for data in pkgdata: libenv.Install( "$sharedir", data ) # # For the debugging apps # env2 = libenv.Clone() env2.PrependUnique( LIBPATH=env['build_base']+"src" ) env2.PrependUnique( LIBS="ffado" ) apps = { \ "test-debugmodule" : "debugmodule/test_debugmodule.cpp", \ "test-dll" : "libutil/test-dll.cpp", \ "test-unittests-util" : "libutil/unittests.cpp", \ "test-cyclecalc" : "libieee1394/test-cyclecalc.cpp", \ } installapps = [] for app in apps.keys(): env2.Program( target=app, source = env.Split( apps[app] ) ) if app.find( "test" ) == -1: env2.Install( "$bindir", app ) libffado-2.4.5/src/bebob/0000755000175000001440000000000014206145612014521 5ustar jwoitheuserslibffado-2.4.5/src/bebob/bebob_avdevice.cpp0000644000175000001440000007253514206145246020163 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "devicemanager.h" #include "bebob/bebob_avdevice.h" #include "bebob/bebob_avdevice_subunit.h" #include "bebob/bebob_mixer.h" #include "bebob/focusrite/focusrite_saffire.h" #include "bebob/focusrite/focusrite_saffirepro.h" #include "bebob/terratec/terratec_device.h" #include "bebob/mackie/onyxmixer.h" #include "bebob/edirol/edirol_fa101.h" #include "bebob/edirol/edirol_fa66.h" #include "bebob/esi/quatafire610.h" #include "bebob/yamaha/yamaha_avdevice.h" #include "bebob/maudio/normal_avdevice.h" #include "bebob/maudio/special_avdevice.h" #include "bebob/presonus/firebox_avdevice.h" #include "bebob/presonus/inspire1394_avdevice.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libavc/general/avc_plug_info.h" #include "libavc/general/avc_extended_plug_info.h" #include "libavc/general/avc_subunit_info.h" #include "libavc/streamformat/avc_extended_stream_format.h" #include "libutil/cmd_serialize.h" #include "libavc/avc_definitions.h" #include "debugmodule/debugmodule.h" #include #include #include #include #include #include using namespace AVC; namespace BeBoB { Device::Device( DeviceManager& d, ffado_smartptr< ConfigRom >( configRom ) ) : GenericAVC::Device( d, configRom ) , m_last_discovery_config_id ( 0xFFFFFFFFFFFFFFFFLLU ) , m_Mixer ( 0 ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::Device (NodeID %d)\n", getConfigRom().getNodeId() ); } Device::~Device() { destroyMixer(); } bool Device::probe( Util::Configuration& c, ConfigRom& configRom, bool generic ) { unsigned int vendorId = configRom.getNodeVendorId(); unsigned int modelId = configRom.getModelId(); if(generic) { /* M-Audio Special Devices don't support followed commands */ if ((vendorId == FW_VENDORID_MAUDIO) && ((modelId == 0x00010071) || (modelId == 0x00010091))) return true; // try a bebob-specific command to check for the firmware ExtendedPlugInfoCmd extPlugInfoCmd( configRom.get1394Service() ); UnitPlugAddress unitPlugAddress( UnitPlugAddress::ePT_PCR, configRom.getNodeId() ); extPlugInfoCmd.setPlugAddress( PlugAddress( PlugAddress::ePD_Input, PlugAddress::ePAM_Unit, unitPlugAddress ) ); extPlugInfoCmd.setNodeId( configRom.getNodeId() ); extPlugInfoCmd.setCommandType( AVCCommand::eCT_Status ); extPlugInfoCmd.setVerbose( configRom.getVerboseLevel() ); ExtendedPlugInfoInfoType extendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_NoOfChannels ); extendedPlugInfoInfoType.initialize(); extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType ); if ( !extPlugInfoCmd.fire() ) { debugError( "Number of channels command failed\n" ); return false; } if((extPlugInfoCmd.getResponse() != AVCCommand::eR_Implemented)) { // command not supported return false; } ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType(); if ( infoType && infoType->m_plugNrOfChns ) { return true; } return false; } else { // check if device is in supported devices list Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); return c.isValid(vme) && vme.driver == Util::Configuration::eD_BeBoB; } } FFADODevice * Device::createDevice(DeviceManager& d, ffado_smartptr( configRom )) { unsigned int vendorId = configRom->getNodeVendorId(); unsigned int modelId = configRom->getModelId(); switch (vendorId) { case FW_VENDORID_MACKIE: if (modelId == 0x00010065 ) { return new Mackie::OnyxMixerDevice(d, configRom); } case FW_VENDORID_EDIROL: switch (modelId) { case 0x00010048: return new Edirol::EdirolFa101Device(d, configRom); case 0x00010049: return new Edirol::EdirolFa66Device(d, configRom); default: return new Device(d, configRom); } case FW_VENDORID_ESI: if (modelId == 0x00010064 || modelId == 0x00000210) { return new ESI::QuataFireDevice(d, configRom); } break; case FW_VENDORID_TERRATEC: switch(modelId) { case 0x00000003: return new Terratec::Phase88Device(d, configRom); default: // return a plain BeBoB device return new Device(d, configRom); } case FW_VENDORID_FOCUSRITE: switch(modelId) { case 0x00000003: case 0x00000006: return new Focusrite::SaffireProDevice(d, configRom); case 0x00000000: return new Focusrite::SaffireDevice(d, configRom); default: // return a plain BeBoB device return new Device(d, configRom); } case FW_VENDORID_YAMAHA: switch (modelId) { case 0x0010000b: case 0x0010000c: return new Yamaha::GoDevice(d, configRom); default: // return a plain BeBoB device return new Device(d, configRom); } case FW_VENDORID_MAUDIO: switch (modelId) { case 0x0000000a: // Ozonic case 0x00010046: // fw410 case 0x00010060: // Audiophile case 0x00010062: // Solo return new MAudio::Normal::Device(d, configRom, modelId); case 0x00010071: // FireWire 1814 case 0x00010091: // ProjectMix I/O return new MAudio::Special::Device(d, configRom); default: return new Device(d, configRom); } case FW_VENDORID_PRESONUS: switch (modelId) { case 0x00010000: return new Presonus::Firebox::Device(d, configRom); case 0x00010001: return new Presonus::Inspire1394::Device(d, configRom); default: return new Device(d, configRom); } default: return new Device(d, configRom); } return NULL; } #define BEBOB_CHECK_AND_ADD_SR(v, x) \ { if(supportsSamplingFrequency(x)) \ v.push_back(x); } bool Device::discover() { unsigned int vendorId = getConfigRom().getNodeVendorId(); unsigned int modelId = getConfigRom().getModelId(); Util::Configuration &c = getDeviceManager().getConfiguration(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); if (c.isValid(vme) && vme.driver == Util::Configuration::eD_BeBoB) { debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", vme.vendor_name.c_str(), vme.model_name.c_str()); } else { debugWarning("Using generic BeBoB support for unsupported device '%s %s'\n", getConfigRom().getVendorName().c_str(), getConfigRom().getModelName().c_str()); } if ( !Unit::discover() ) { debugError( "Could not discover unit\n" ); return false; } if((getAudioSubunit( 0 ) == NULL)) { debugError( "Unit doesn't have an Audio subunit.\n"); return false; } if((getMusicSubunit( 0 ) == NULL)) { debugError( "Unit doesn't have a Music subunit.\n"); return false; } if(!buildMixer()) { debugWarning("Could not build mixer\n"); } // keep track of the config id of this discovery m_last_discovery_config_id = getConfigurationId(); return true; } bool Device::buildMixer() { debugOutput(DEBUG_LEVEL_VERBOSE, "Building a generic BeBoB mixer...\n"); // create a Mixer // this removes the mixer if it already exists // note: a mixer self-registers to it's parent delete m_Mixer; // create the mixer & register it if(getAudioSubunit(0) == NULL) { debugWarning("Could not find audio subunit, mixer not available.\n"); m_Mixer = NULL; } else { m_Mixer = new Mixer(*this); } if (m_Mixer) m_Mixer->setVerboseLevel(getDebugLevel()); return m_Mixer != NULL; } bool Device::destroyMixer() { delete m_Mixer; return true; } bool Device::setSelectorFBValue(int id, int value) { FunctionBlockCmd fbCmd( get1394Service(), FunctionBlockCmd::eFBT_Selector, id, FunctionBlockCmd::eCA_Current ); fbCmd.setNodeId( getNodeId() ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Control ); fbCmd.m_pFBSelector->m_inputFbPlugNumber = (value & 0xFF); fbCmd.setVerboseLevel( getDebugLevel() ); if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); return false; } // if ( getDebugLevel() >= DEBUG_LEVEL_NORMAL ) { // Util::Cmd::CoutSerializer se; // fbCmd.serialize( se ); // } // if((fbCmd.getResponse() != AVCCommand::eR_Accepted)) { debugWarning("fbCmd.getResponse() != AVCCommand::eR_Accepted\n"); } return (fbCmd.getResponse() == AVCCommand::eR_Accepted); } int Device::getSelectorFBValue(int id) { FunctionBlockCmd fbCmd( get1394Service(), FunctionBlockCmd::eFBT_Selector, id, FunctionBlockCmd::eCA_Current ); fbCmd.setNodeId( getNodeId() ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Status ); fbCmd.m_pFBSelector->m_inputFbPlugNumber = 0xFF; fbCmd.setVerboseLevel( getDebugLevel() ); if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); return -1; } // if ( getDebugLevel() >= DEBUG_LEVEL_NORMAL ) { // Util::Cmd::CoutSerializer se; // fbCmd.serialize( se ); // } if((fbCmd.getResponse() != AVCCommand::eR_Implemented)) { debugWarning("fbCmd.getResponse() != AVCCommand::eR_Implemented\n"); } return fbCmd.m_pFBSelector->m_inputFbPlugNumber; } bool Device::setFeatureFBVolumeCurrent(int id, int channel, int v) { FunctionBlockCmd fbCmd( get1394Service(), FunctionBlockCmd::eFBT_Feature, id, FunctionBlockCmd::eCA_Current ); fbCmd.setNodeId( getNodeId() ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Control ); fbCmd.m_pFBFeature->m_audioChannelNumber = channel; fbCmd.m_pFBFeature->m_controlSelector = FunctionBlockFeature::eCSE_Feature_Volume; AVC::FunctionBlockFeatureVolume vl; fbCmd.m_pFBFeature->m_pVolume = vl.clone(); fbCmd.m_pFBFeature->m_pVolume->m_volume = v; fbCmd.setVerboseLevel( getDebugLevel() ); if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); return false; } // if ( getDebugLevel() >= DEBUG_LEVEL_NORMAL ) { // Util::Cmd::CoutSerializer se; // fbCmd.serialize( se ); // } if((fbCmd.getResponse() != AVCCommand::eR_Accepted)) { debugWarning("fbCmd.getResponse() != AVCCommand::eR_Accepted\n"); } return (fbCmd.getResponse() == AVCCommand::eR_Accepted); } int Device::getFeatureFBVolumeValue(int id, int channel, FunctionBlockCmd::EControlAttribute controlAttribute) { FunctionBlockCmd fbCmd( get1394Service(), FunctionBlockCmd::eFBT_Feature, id, controlAttribute); fbCmd.setNodeId( getNodeId() ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Status ); fbCmd.m_pFBFeature->m_audioChannelNumber = channel; fbCmd.m_pFBFeature->m_controlSelector = FunctionBlockFeature::eCSE_Feature_Volume; AVC::FunctionBlockFeatureVolume vl; fbCmd.m_pFBFeature->m_pVolume = vl.clone(); fbCmd.m_pFBFeature->m_pVolume->m_volume = 0; fbCmd.setVerboseLevel( getDebugLevel() ); if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); return 0; } // if ( getDebugLevel() >= DEBUG_LEVEL_NORMAL ) { // Util::Cmd::CoutSerializer se; // fbCmd.serialize( se ); // } if((fbCmd.getResponse() != AVCCommand::eR_Implemented)) { debugWarning("fbCmd.getResponse() != AVCCommand::eR_Implemented\n"); } int16_t volume=(int16_t)(fbCmd.m_pFBFeature->m_pVolume->m_volume); return volume; } int Device::getFeatureFBVolumeMinimum(int id, int channel) { return getFeatureFBVolumeValue(id, channel, AVC::FunctionBlockCmd::eCA_Minimum); } int Device::getFeatureFBVolumeMaximum(int id, int channel) { return getFeatureFBVolumeValue(id, channel, AVC::FunctionBlockCmd::eCA_Maximum); } int Device::getFeatureFBVolumeCurrent(int id, int channel) { return getFeatureFBVolumeValue(id, channel, AVC::FunctionBlockCmd::eCA_Current); } bool Device::setFeatureFBLRBalanceCurrent(int id, int channel, int v) { FunctionBlockCmd fbCmd( get1394Service(), FunctionBlockCmd::eFBT_Feature, id, FunctionBlockCmd::eCA_Current ); fbCmd.setNodeId( getNodeId() ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Control ); fbCmd.m_pFBFeature->m_audioChannelNumber = channel; fbCmd.m_pFBFeature->m_controlSelector = FunctionBlockFeature::eCSE_Feature_LRBalance; AVC::FunctionBlockFeatureLRBalance bl; fbCmd.m_pFBFeature->m_pLRBalance = bl.clone(); fbCmd.m_pFBFeature->m_pLRBalance->m_lrBalance = v; fbCmd.setVerboseLevel( getDebugLevel() ); if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); return false; } // if ( getDebugLevel() >= DEBUG_LEVEL_NORMAL ) { // Util::Cmd::CoutSerializer se; // fbCmd.serialize( se ); // } if((fbCmd.getResponse() != AVCCommand::eR_Accepted)) { debugWarning("fbCmd.getResponse() != AVCCommand::eR_Accepted\n"); } return (fbCmd.getResponse() == AVCCommand::eR_Accepted); } int Device::getFeatureFBLRBalanceValue(int id, int channel, FunctionBlockCmd::EControlAttribute controlAttribute) { FunctionBlockCmd fbCmd( get1394Service(), FunctionBlockCmd::eFBT_Feature, id, controlAttribute); fbCmd.setNodeId( getNodeId() ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Status ); fbCmd.m_pFBFeature->m_audioChannelNumber = channel; fbCmd.m_pFBFeature->m_controlSelector = FunctionBlockFeature::eCSE_Feature_LRBalance; AVC::FunctionBlockFeatureLRBalance bl; fbCmd.m_pFBFeature->m_pLRBalance = bl.clone(); fbCmd.m_pFBFeature->m_pLRBalance->m_lrBalance = 0; fbCmd.setVerboseLevel( getDebugLevel() ); if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); return 0; } // if ( getDebugLevel() >= DEBUG_LEVEL_NORMAL ) { // Util::Cmd::CoutSerializer se; // fbCmd.serialize( se ); // } if((fbCmd.getResponse() != AVCCommand::eR_Implemented)) { debugWarning("fbCmd.getResponse() != AVCCommand::eR_Implemented\n"); } int16_t balance=(int16_t)(fbCmd.m_pFBFeature->m_pLRBalance->m_lrBalance); return balance; } int Device::getFeatureFBLRBalanceMinimum(int id, int channel) { return getFeatureFBLRBalanceValue(id, channel, AVC::FunctionBlockCmd::eCA_Minimum); } int Device::getFeatureFBLRBalanceMaximum(int id, int channel) { return getFeatureFBLRBalanceValue(id, channel, AVC::FunctionBlockCmd::eCA_Maximum); } int Device::getFeatureFBLRBalanceCurrent(int id, int channel) { return getFeatureFBLRBalanceValue(id, channel, AVC::FunctionBlockCmd::eCA_Current); } bool Device::setProcessingFBMixerSingleCurrent(int id, int iPlugNum, int iAChNum, int oAChNum, int setting) { AVC::FunctionBlockCmd fbCmd(get1394Service(), AVC::FunctionBlockCmd::eFBT_Processing, id, AVC::FunctionBlockCmd::eCA_Current); fbCmd.setNodeId(getNodeId()); fbCmd.setSubunitId(0x00); fbCmd.setCommandType(AVCCommand::eCT_Control); fbCmd.setVerboseLevel( getDebugLevel() ); AVC::FunctionBlockProcessing *fbp = fbCmd.m_pFBProcessing; fbp->m_selectorLength = 0x04; fbp->m_fbInputPlugNumber = iPlugNum; fbp->m_inputAudioChannelNumber = iAChNum; fbp->m_outputAudioChannelNumber = oAChNum; // mixer object is not generated automatically fbp->m_pMixer = new AVC::FunctionBlockProcessingMixer; fbp->m_pMixer->m_mixerSetting = setting; if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); return false; } if((fbCmd.getResponse() != AVCCommand::eR_Accepted)) { debugWarning("fbCmd.getResponse() != AVCCommand::eR_Accepted\n"); } return (fbCmd.getResponse() == AVCCommand::eR_Accepted); } int Device::getProcessingFBMixerSingleCurrent(int id, int iPlugNum, int iAChNum, int oAChNum) { AVC::FunctionBlockCmd fbCmd(get1394Service(), AVC::FunctionBlockCmd::eFBT_Processing, id, AVC::FunctionBlockCmd::eCA_Current); fbCmd.setNodeId(getNodeId()); fbCmd.setSubunitId(0x00); fbCmd.setCommandType(AVCCommand::eCT_Status); fbCmd.setVerboseLevel( getDebugLevel() ); AVC::FunctionBlockProcessing *fbp = fbCmd.m_pFBProcessing; fbp->m_selectorLength = 0x04; fbp->m_fbInputPlugNumber = iPlugNum; fbp->m_inputAudioChannelNumber = iAChNum; fbp->m_outputAudioChannelNumber = oAChNum; // mixer object is not generated automatically fbp->m_pMixer = new AVC::FunctionBlockProcessingMixer; if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); return 0; } if( (fbCmd.getResponse() != AVCCommand::eR_Implemented) ) { debugWarning("fbCmd.getResponse() != AVCCommand::eR_Implemented\n"); } int16_t setting = (int16_t)(fbp->m_pMixer->m_mixerSetting); return setting; } void Device::showDevice() { debugOutput(DEBUG_LEVEL_NORMAL, "Device is a BeBoB device\n"); GenericAVC::Device::showDevice(); flushDebugOutput(); } void Device::setVerboseLevel(int l) { if (m_Mixer) m_Mixer->setVerboseLevel( l ); GenericAVC::Device::setVerboseLevel( l ); debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); } AVC::Subunit* Device::createSubunit(AVC::Unit& unit, AVC::ESubunitType type, AVC::subunit_t id ) { AVC::Subunit* s=NULL; switch (type) { case eST_Audio: s=new BeBoB::SubunitAudio(unit, id ); break; case eST_Music: s=new BeBoB::SubunitMusic(unit, id ); break; default: s=NULL; break; } if(s) s->setVerboseLevel(getDebugLevel()); return s; } AVC::Plug * Device::createPlug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId, int globalId ) { Plug *p= new BeBoB::Plug( unit, subunit, functionBlockType, functionBlockId, plugAddressType, plugDirection, plugId, globalId ); if (p) p->setVerboseLevel(getDebugLevel()); return p; } bool Device::propagatePlugInfo() { // we don't have to propagate since we discover things // another way debugOutput(DEBUG_LEVEL_VERBOSE, "Skip plug info propagation\n"); return true; } uint8_t Device::getConfigurationIdSampleRate() { ExtendedStreamFormatCmd extStreamFormatCmd( get1394Service() ); UnitPlugAddress unitPlugAddress( UnitPlugAddress::ePT_PCR, 0 ); extStreamFormatCmd.setPlugAddress( PlugAddress( PlugAddress::ePD_Input, PlugAddress::ePAM_Unit, unitPlugAddress ) ); extStreamFormatCmd.setNodeId( getNodeId() ); extStreamFormatCmd.setCommandType( AVCCommand::eCT_Status ); extStreamFormatCmd.setVerbose( getDebugLevel() ); if ( !extStreamFormatCmd.fire() ) { debugError( "Stream format command failed\n" ); return 0; } FormatInformation* formatInfo = extStreamFormatCmd.getFormatInformation(); FormatInformationStreamsCompound* compoundStream = dynamic_cast< FormatInformationStreamsCompound* > ( formatInfo->m_streams ); if ( compoundStream ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Sample rate 0x%02x\n", compoundStream->m_samplingFrequency ); return compoundStream->m_samplingFrequency; } debugError( "Could not retrieve sample rate\n" ); return 0; } uint8_t Device::getConfigurationIdNumberOfChannel( PlugAddress::EPlugDirection ePlugDirection ) { ExtendedPlugInfoCmd extPlugInfoCmd( get1394Service() ); UnitPlugAddress unitPlugAddress( UnitPlugAddress::ePT_PCR, 0 ); extPlugInfoCmd.setPlugAddress( PlugAddress( ePlugDirection, PlugAddress::ePAM_Unit, unitPlugAddress ) ); extPlugInfoCmd.setNodeId( getNodeId() ); extPlugInfoCmd.setCommandType( AVCCommand::eCT_Status ); extPlugInfoCmd.setVerbose( getDebugLevel() ); ExtendedPlugInfoInfoType extendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_NoOfChannels ); extendedPlugInfoInfoType.initialize(); extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType ); if ( !extPlugInfoCmd.fire() ) { debugError( "Number of channels command failed\n" ); return 0; } ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType(); if ( infoType && infoType->m_plugNrOfChns ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Number of channels 0x%02x\n", infoType->m_plugNrOfChns->m_nrOfChannels ); return infoType->m_plugNrOfChns->m_nrOfChannels; } debugError( "Could not retrieve number of channels\n" ); return 0; } uint16_t Device::getConfigurationIdSyncMode() { SignalSourceCmd signalSourceCmd( get1394Service() ); SignalUnitAddress signalUnitAddr; signalUnitAddr.m_plugId = 0x01; signalSourceCmd.setSignalDestination( signalUnitAddr ); signalSourceCmd.setNodeId( getNodeId() ); signalSourceCmd.setSubunitType( eST_Unit ); signalSourceCmd.setSubunitId( 0xff ); signalSourceCmd.setVerbose( getDebugLevel() ); signalSourceCmd.setCommandType( AVCCommand::eCT_Status ); if ( !signalSourceCmd.fire() ) { debugError( "Signal source command failed\n" ); return 0; } SignalAddress* pSyncPlugSignalAddress = signalSourceCmd.getSignalSource(); SignalSubunitAddress* pSyncPlugSubunitAddress = dynamic_cast( pSyncPlugSignalAddress ); if ( pSyncPlugSubunitAddress ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Sync mode 0x%02x\n", ( pSyncPlugSubunitAddress->m_subunitType << 3 | pSyncPlugSubunitAddress->m_subunitId ) << 8 | pSyncPlugSubunitAddress->m_plugId ); return ( pSyncPlugSubunitAddress->m_subunitType << 3 | pSyncPlugSubunitAddress->m_subunitId ) << 8 | pSyncPlugSubunitAddress->m_plugId; } SignalUnitAddress* pSyncPlugUnitAddress = dynamic_cast( pSyncPlugSignalAddress ); if ( pSyncPlugUnitAddress ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Sync mode 0x%02x\n", 0xff << 8 | pSyncPlugUnitAddress->m_plugId ); return ( 0xff << 8 | pSyncPlugUnitAddress->m_plugId ); } debugError( "Could not retrieve sync mode\n" ); return 0; } bool Device::needsRediscovery() { // require rediscovery if the config id differs from the one saved // in the previous discovery return getConfigurationId() != m_last_discovery_config_id; } uint64_t Device::getConfigurationId() { // create a unique configuration id. uint64_t id = 0; id = getConfigurationIdSampleRate(); id |= getConfigurationIdNumberOfChannel( PlugAddress::ePD_Input ) << 8; id |= getConfigurationIdNumberOfChannel( PlugAddress::ePD_Output ) << 16; id |= ((uint64_t)getConfigurationIdSyncMode()) << 24; return id; } bool Device::serialize( std::string basePath, Util::IOSerialize& ser ) const { bool result; result = GenericAVC::Device::serialize( basePath, ser ); return result; } bool Device::deserialize( std::string basePath, Util::IODeserialize& deser ) { bool result; result = GenericAVC::Device::deserialize( basePath, deser ); return result; } std::string Device::getCachePath() { std::string cachePath; char* pCachePath; string path = CACHEDIR; if ( path.size() && path[0] == '~' ) { path.erase( 0, 1 ); // remove ~ path.insert( 0, getenv( "HOME" ) ); // prepend the home path } if ( asprintf( &pCachePath, "%s/cache/", path.c_str() ) < 0 ) { debugError( "Could not create path string for cache pool (trying '/var/cache/libffado' instead)\n" ); cachePath = "/var/cache/libffado/"; } else { cachePath = pCachePath; free( pCachePath ); } return cachePath; } bool Device::loadFromCache() { std::string sDevicePath = getCachePath() + getConfigRom().getGuidString(); char* configId; asprintf(&configId, "%016" PRIx64 "", getConfigurationId() ); if ( !configId ) { debugError( "could not create id string\n" ); return false; } std::string sFileName = sDevicePath + "/" + configId + ".xml"; free( configId ); debugOutput( DEBUG_LEVEL_NORMAL, "filename %s\n", sFileName.c_str() ); struct stat buf; if ( stat( sFileName.c_str(), &buf ) != 0 ) { debugOutput( DEBUG_LEVEL_NORMAL, "\"%s\" does not exist\n", sFileName.c_str() ); return false; } else { if ( !S_ISREG( buf.st_mode ) ) { debugOutput( DEBUG_LEVEL_NORMAL, "\"%s\" is not a regular file\n", sFileName.c_str() ); return false; } } Util::XMLDeserialize deser( sFileName, getDebugLevel() ); if (!deser.isValid()) { debugOutput( DEBUG_LEVEL_NORMAL, "cache not valid: %s\n", sFileName.c_str() ); return false; } bool result = deserialize( "", deser ); if ( result ) { debugOutput( DEBUG_LEVEL_NORMAL, "could create valid bebob driver from %s\n", sFileName.c_str() ); } if(result) { buildMixer(); } return result; } bool Device::saveCache() { // the path looks like this: // PATH_TO_CACHE + GUID + CONFIGURATION_ID string tmp_path = getCachePath() + getConfigRom().getGuidString(); // the following piece should do something like // 'mkdir -p some/path/with/some/dirs/which/do/not/exist' vector tokens; tokenize( tmp_path, tokens, "/" ); string path; for ( vector::const_iterator it = tokens.begin(); it != tokens.end(); ++it ) { path += "/" + *it; struct stat buf; if ( stat( path.c_str(), &buf ) == 0 ) { if ( !S_ISDIR( buf.st_mode ) ) { debugError( "\"%s\" is not a directory\n", path.c_str() ); return false; } } else { if ( mkdir( path.c_str(), S_IRWXU | S_IRWXG ) != 0 ) { debugError( "Could not create \"%s\" directory\n", path.c_str() ); return false; } } } // come up with an unique file name for the current settings char* configId; asprintf(&configId, "%016" PRIx64 "", BeBoB::Device::getConfigurationId() ); if ( !configId ) { debugError( "Could not create id string\n" ); return false; } string filename = path + "/" + configId + ".xml"; free( configId ); debugOutput( DEBUG_LEVEL_NORMAL, "filename %s\n", filename.c_str() ); Util::XMLSerialize ser( filename ); return serialize( "", ser ); } } // end of namespace libffado-2.4.5/src/bebob/bebob_avdevice.h0000644000175000001440000001160314206145246017615 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_DEVICE_H #define BEBOB_DEVICE_H #include #include "debugmodule/debugmodule.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libavc/avc_definitions.h" #include "libavc/general/avc_extended_cmd_generic.h" #include "libavc/general/avc_unit.h" #include "libavc/general/avc_subunit.h" #include "libavc/general/avc_plug.h" #include "libavc/audiosubunit/avc_function_block.h" #include "bebob/bebob_avplug.h" #include "bebob/bebob_avdevice_subunit.h" #include "bebob/bebob_mixer.h" #include "libstreaming/amdtp/AmdtpReceiveStreamProcessor.h" #include "libstreaming/amdtp/AmdtpTransmitStreamProcessor.h" #include "libstreaming/amdtp/AmdtpPort.h" #include "libstreaming/amdtp/AmdtpPortInfo.h" #include "libutil/serialize.h" #include "genericavc/avc_avdevice.h" #include "ffadodevice.h" #include #include namespace BeBoB { class Device : public GenericAVC::Device { public: Device( DeviceManager& d, ffado_smartptr( configRom )); virtual ~Device(); static bool probe( Util::Configuration&, ConfigRom& configRom, bool generic = false ); virtual bool loadFromCache(); virtual bool saveCache(); virtual bool discover(); static FFADODevice * createDevice( DeviceManager& d, ffado_smartptr( configRom )); virtual AVC::Subunit* createSubunit(AVC::Unit& unit, AVC::ESubunitType type, AVC::subunit_t id ); virtual AVC::Plug *createPlug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId, int globalId = -1 ); virtual int getSelectorFBValue(int id); virtual bool setSelectorFBValue(int id, int v); virtual int getFeatureFBVolumeMinimum(int id, int channel); virtual int getFeatureFBVolumeMaximum(int id, int channel); virtual int getFeatureFBVolumeCurrent(int id, int channel); virtual bool setFeatureFBVolumeCurrent(int id, int channel, int v); virtual int getFeatureFBLRBalanceMinimum(int id, int channel); virtual int getFeatureFBLRBalanceMaximum(int id, int channel); virtual int getFeatureFBLRBalanceCurrent(int id, int channel); virtual bool setFeatureFBLRBalanceCurrent(int id, int channel, int v); virtual bool setProcessingFBMixerSingleCurrent(int id, int iPlugNum, int iAChNum, int oAChNum, int setting); virtual int getProcessingFBMixerSingleCurrent(int id, int iPlugNum, int iAChNum, int oAChNum); virtual void showDevice(); virtual void setVerboseLevel(int l); protected: virtual bool propagatePlugInfo(); virtual bool buildMixer(); virtual bool destroyMixer(); virtual int getFeatureFBVolumeValue(int id, int channel, AVC::FunctionBlockCmd::EControlAttribute controlAttribute); virtual int getFeatureFBLRBalanceValue(int id, int channel, AVC::FunctionBlockCmd::EControlAttribute controlAttribute); public: virtual bool serialize( std::string basePath, Util::IOSerialize& ser ) const; virtual bool deserialize( std::string basePath, Util::IODeserialize& deser ); virtual uint64_t getConfigurationId(); virtual bool needsRediscovery(); std::string getCachePath(); protected: virtual uint8_t getConfigurationIdSampleRate(); virtual uint8_t getConfigurationIdNumberOfChannel( AVC::PlugAddress::EPlugDirection ePlugDirection ); virtual uint16_t getConfigurationIdSyncMode(); std::vector m_supported_frequencies; uint64_t m_last_discovery_config_id; protected: Mixer* m_Mixer; }; } #endif libffado-2.4.5/src/bebob/bebob_avdevice_subunit.cpp0000644000175000001440000003436114206145246021727 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "bebob/bebob_functionblock.h" #include "bebob/bebob_avdevice_subunit.h" #include "bebob/bebob_avdevice.h" #include "bebob/bebob_avplug.h" #include "libieee1394/configrom.h" #include "libavc/general/avc_plug_info.h" #include "libavc/streamformat/avc_extended_stream_format.h" #include "libutil/cmd_serialize.h" #include using namespace AVC; ////////////////////////// BeBoB::SubunitAudio::SubunitAudio( AVC::Unit& avDevice, subunit_t id ) : AVC::SubunitAudio( avDevice, id ) { } BeBoB::SubunitAudio::SubunitAudio() : AVC::SubunitAudio() { } BeBoB::SubunitAudio::~SubunitAudio() { for ( FunctionBlockVector::iterator it = m_functions.begin(); it != m_functions.end(); ++it ) { delete *it; } } AVC::Plug * BeBoB::SubunitAudio::createPlug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId ) { return new BeBoB::Plug( unit, subunit, functionBlockType, functionBlockId, plugAddressType, plugDirection, plugId ); } bool BeBoB::SubunitAudio::discover() { debugOutput(DEBUG_LEVEL_NORMAL, "Discovering %s...\n", getName()); // discover the AV/C generic part if ( !AVC::SubunitAudio::discover() ) { return false; } // do the remaining BeBoB audio subunit discovery if ( !discoverFunctionBlocks() ) { debugError( "function block discovering failed\n" ); return false; } return true; } bool BeBoB::SubunitAudio::discoverConnections() { debugOutput(DEBUG_LEVEL_NORMAL, "Discovering connections...\n"); if ( !Subunit::discoverConnections() ) { return false; } for ( FunctionBlockVector::iterator it = m_functions.begin(); it != m_functions.end(); ++it ) { FunctionBlock* function = *it; if ( !function->discoverConnections() ) { debugError( "functionblock connection discovering failed ('%s')\n", function->getName() ); return false; } } return true; } const char* BeBoB::SubunitAudio::getName() { return "BeBoB::AudioSubunit"; } bool BeBoB::SubunitAudio::discoverFunctionBlocks() { debugOutput( DEBUG_LEVEL_NORMAL, "Discovering function blocks...\n"); if ( !discoverFunctionBlocksDo( ExtendedSubunitInfoCmd::eFBT_AudioSubunitSelector) ) { debugError( "Could not discover function block selector\n" ); return false; } if ( !discoverFunctionBlocksDo( ExtendedSubunitInfoCmd::eFBT_AudioSubunitFeature) ) { debugError( "Could not discover function block feature\n" ); return false; } if ( !discoverFunctionBlocksDo( ExtendedSubunitInfoCmd::eFBT_AudioSubunitProcessing) ) { debugError( "Could not discover function block processing\n" ); return false; } if ( !discoverFunctionBlocksDo( ExtendedSubunitInfoCmd::eFBT_AudioSubunitCodec) ) { debugError( "Could not discover function block codec\n" ); return false; } // print a function block list #ifdef DEBUG if ((int)getDebugLevel() >= DEBUG_LEVEL_NORMAL) { for ( FunctionBlockVector::iterator it = m_functions.begin(); it != m_functions.end(); ++it ) { debugOutput(DEBUG_LEVEL_NORMAL, "%20s FB, type 0x%X, id=%d\n", (*it)->getName(), (*it)->getType(), (*it)->getId()); } } #endif return true; } bool BeBoB::SubunitAudio::discoverFunctionBlocksDo( ExtendedSubunitInfoCmd::EFunctionBlockType fbType ) { int page = 0; bool cmdSuccess = false; bool finished = false; do { ExtendedSubunitInfoCmd extSubunitInfoCmd( m_unit->get1394Service() ); extSubunitInfoCmd.setNodeId( m_unit->getConfigRom().getNodeId() ); extSubunitInfoCmd.setCommandType( AVCCommand::eCT_Status ); extSubunitInfoCmd.setSubunitId( getSubunitId() ); extSubunitInfoCmd.setSubunitType( getSubunitType() ); extSubunitInfoCmd.setVerbose( (int)getDebugLevel() ); extSubunitInfoCmd.m_fbType = fbType; extSubunitInfoCmd.m_page = page; cmdSuccess = extSubunitInfoCmd.fire(); if ( cmdSuccess && ( extSubunitInfoCmd.getResponse() == AVCCommand::eR_Implemented ) ) { for ( ExtendedSubunitInfoPageDataVector::iterator it = extSubunitInfoCmd.m_infoPageDatas.begin(); cmdSuccess && ( it != extSubunitInfoCmd.m_infoPageDatas.end() ); ++it ) { cmdSuccess = createFunctionBlock( fbType, **it ); } if ( ( extSubunitInfoCmd.m_infoPageDatas.size() != 0 ) && ( extSubunitInfoCmd.m_infoPageDatas.size() == 5 ) ) { page++; } else { finished = true; } } else { finished = true; } } while ( cmdSuccess && !finished ); return cmdSuccess; } bool BeBoB::SubunitAudio::createFunctionBlock( ExtendedSubunitInfoCmd::EFunctionBlockType fbType, ExtendedSubunitInfoPageData& data ) { FunctionBlock::ESpecialPurpose purpose = convertSpecialPurpose( data.m_functionBlockSpecialPupose ); FunctionBlock* fb = 0; switch ( fbType ) { case ExtendedSubunitInfoCmd::eFBT_AudioSubunitSelector: { fb = new FunctionBlockSelector( *this, data.m_functionBlockId, purpose, data.m_noOfInputPlugs, data.m_noOfOutputPlugs, (int)getDebugLevel() ); } break; case ExtendedSubunitInfoCmd::eFBT_AudioSubunitFeature: { fb = new FunctionBlockFeature( *this, data.m_functionBlockId, purpose, data.m_noOfInputPlugs, data.m_noOfOutputPlugs, (int)getDebugLevel() ); } break; case ExtendedSubunitInfoCmd::eFBT_AudioSubunitProcessing: { switch ( data.m_functionBlockType ) { case ExtendedSubunitInfoCmd::ePT_EnhancedMixer: { fb = new FunctionBlockEnhancedMixer( *this, data.m_functionBlockId, purpose, data.m_noOfInputPlugs, data.m_noOfOutputPlugs, (int)getDebugLevel() ); } break; case ExtendedSubunitInfoCmd::ePT_Mixer: case ExtendedSubunitInfoCmd::ePT_Generic: case ExtendedSubunitInfoCmd::ePT_UpDown: case ExtendedSubunitInfoCmd::ePT_DolbyProLogic: case ExtendedSubunitInfoCmd::ePT_3DStereoExtender: case ExtendedSubunitInfoCmd::ePT_Reverberation: case ExtendedSubunitInfoCmd::ePT_Chorus: case ExtendedSubunitInfoCmd::ePT_DynamicRangeCompression: default: /* It is no use to add a dummy FunctionBlockProcessing because then the function type is not set in FunctionBlockProcessing. When we try to discover the plugs attached to this function block it will fail. It's better just to skip them. */ debugOutput( DEBUG_LEVEL_NORMAL, "Found a processing subfunction (type %d) which is not supported. " "It will be ignored.\n", data.m_functionBlockType); return true; } } break; case ExtendedSubunitInfoCmd::eFBT_AudioSubunitCodec: { /* It is no use to add a dummy FunctionBlockProcessing because then the function type is not set in FunctionBlockProcessing. When we try to discover the plugs attached to this function block it will fail. It's better just to skip them. */ debugOutput( DEBUG_LEVEL_NORMAL, "Found a codec subfunction (type %d) which is not supported. " "It will be ignored.\n", data.m_functionBlockType); return true; } break; default: debugError( "Unhandled function block type found\n" ); return false; } if ( !fb ) { debugError( "Could create function block\n" ); return false; } if ( !fb->discover() ) { debugError( "Could not discover function block %s\n", fb->getName() ); delete fb; return false; } m_functions.push_back( fb ); return true; } BeBoB::FunctionBlock::ESpecialPurpose BeBoB::SubunitAudio::convertSpecialPurpose( function_block_special_purpose_t specialPurpose ) { FunctionBlock::ESpecialPurpose p; switch ( specialPurpose ) { case ExtendedSubunitInfoPageData::eSP_InputGain: p = FunctionBlock::eSP_InputGain; break; case ExtendedSubunitInfoPageData::eSP_OutputVolume: p = FunctionBlock::eSP_OutputVolume; break; default: p = FunctionBlock::eSP_NoSpecialPurpose; } return p; } bool BeBoB::SubunitAudio::serializeChild( std::string basePath, Util::IOSerialize& ser ) const { bool result = true; int i = 0; for ( FunctionBlockVector::const_iterator it = m_functions.begin(); it != m_functions.end(); ++it ) { FunctionBlock* pFB = *it; std::ostringstream strstrm; strstrm << basePath << "FunctionBlock" << i << "/"; result &= pFB->serialize( strstrm.str() , ser ); i++; } return result; } bool BeBoB::SubunitAudio::deserializeChild( std::string basePath, Util::IODeserialize& deser, AVC::Unit& avDevice ) { int i = 0; bool bFinished = false; do { std::ostringstream strstrm; strstrm << basePath << "FunctionBlock" << i << "/"; FunctionBlock* pFB = FunctionBlock::deserialize( strstrm.str(), deser, avDevice, *this ); if ( pFB ) { m_functions.push_back( pFB ); i++; } else { bFinished = true; } } while ( !bFinished ); return true; } bool BeBoB::SubunitAudio::deserializeUpdateChild( std::string basePath, Util::IODeserialize& deser ) { bool result = true; int i = 0; for ( FunctionBlockVector::iterator it = m_functions.begin(); it != m_functions.end(); ++it ) { std::ostringstream strstrm; strstrm << basePath << "FunctionBlock" << i << "/"; result &= (*it)->deserializeUpdate( basePath, deser ); i++; } return result; } //////////////////////////////////////////// BeBoB::SubunitMusic::SubunitMusic( AVC::Unit& avDevice, subunit_t id ) : AVC::SubunitMusic( avDevice, id ) { } BeBoB::SubunitMusic::SubunitMusic() : AVC::SubunitMusic() { } BeBoB::SubunitMusic::~SubunitMusic() { } AVC::Plug * BeBoB::SubunitMusic::createPlug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId ) { return new BeBoB::Plug( unit, subunit, functionBlockType, functionBlockId, plugAddressType, plugDirection, plugId ); } bool BeBoB::SubunitMusic::discover() { debugOutput(DEBUG_LEVEL_NORMAL, "Discovering %s...\n", getName()); // discover the AV/C generic part if ( !AVC::SubunitMusic::discover() ) { return false; } // do the remaining BeBoB music subunit discovery // which is nothing return true; } const char* BeBoB::SubunitMusic::getName() { return "BeBoB::MusicSubunit"; } bool BeBoB::SubunitMusic::serializeChild( std::string basePath, Util::IOSerialize& ser ) const { return true; } bool BeBoB::SubunitMusic::deserializeChild( std::string basePath, Util::IODeserialize& deser, AVC::Unit& avDevice ) { return true; } bool BeBoB::SubunitMusic::deserializeUpdateChild( std::string basePath, Util::IODeserialize& deser ) { return true; } libffado-2.4.5/src/bebob/bebob_avdevice_subunit.h0000644000175000001440000001040714206145246021367 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_AVDEVICESUBUNIT_H #define BEBOB_AVDEVICESUBUNIT_H #include "bebob/bebob_avplug.h" #include "bebob/bebob_functionblock.h" #include "debugmodule/debugmodule.h" #include "libavc/general/avc_extended_subunit_info.h" #include "libavc/avc_definitions.h" #include "libavc/general/avc_generic.h" #include #include "libavc/general/avc_subunit.h" #include "libavc/musicsubunit/avc_musicsubunit.h" #include "libavc/audiosubunit/avc_audiosubunit.h" #include "libavc/general/avc_plug.h" namespace BeBoB { ///////////////////////////// class SubunitAudio : public AVC::SubunitAudio { public: SubunitAudio( AVC::Unit& avDevice, AVC::subunit_t id ); SubunitAudio(); virtual ~SubunitAudio(); virtual bool discover(); virtual bool discoverConnections(); virtual AVC::Plug *createPlug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId ); virtual const char* getName(); virtual FunctionBlockVector getFunctionBlocks() { return m_functions; }; protected: bool discoverFunctionBlocks(); bool discoverFunctionBlocksDo( AVC::ExtendedSubunitInfoCmd::EFunctionBlockType fbType ); bool createFunctionBlock( AVC::ExtendedSubunitInfoCmd::EFunctionBlockType fbType, AVC::ExtendedSubunitInfoPageData& data ); FunctionBlock::ESpecialPurpose convertSpecialPurpose( AVC::function_block_special_purpose_t specialPurpose ); virtual bool serializeChild( std::string basePath, Util::IOSerialize& ser ) const; virtual bool deserializeChild( std::string basePath, Util::IODeserialize& deser, AVC::Unit& unit ); virtual bool deserializeUpdateChild( std::string basePath, Util::IODeserialize& deser ); protected: FunctionBlockVector m_functions; }; ///////////////////////////// class SubunitMusic : public AVC::SubunitMusic { public: SubunitMusic( AVC::Unit& avDevice, AVC::subunit_t id ); SubunitMusic(); virtual ~SubunitMusic(); virtual bool discover(); virtual AVC::Plug *createPlug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId ); virtual const char* getName(); protected: virtual bool serializeChild( std::string basePath, Util::IOSerialize& ser ) const; virtual bool deserializeChild( std::string basePath, Util::IODeserialize& deser, AVC::Unit& unit ); virtual bool deserializeUpdateChild( std::string basePath, Util::IODeserialize& deser ); }; } #endif libffado-2.4.5/src/bebob/bebob_avplug.cpp0000644000175000001440000005550714206145246017673 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "bebob/bebob_avplug.h" #include "bebob/bebob_avdevice.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/cmd_serialize.h" #include using namespace AVC; namespace BeBoB { Plug::Plug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId ) : AVC::Plug( unit, subunit, functionBlockType, functionBlockId, plugAddressType, plugDirection, plugId ) { debugOutput( DEBUG_LEVEL_VERBOSE, "nodeId = %d, subunitType = %d, " "subunitId = %d, functionBlockType = %d, " "functionBlockId = %d, addressType = %d, " "direction = %d, id = %d\n", unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), functionBlockType, functionBlockId, plugAddressType, plugDirection, plugId ); } Plug::Plug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId, int globalId ) : AVC::Plug( unit, subunit, functionBlockType, functionBlockId, plugAddressType, plugDirection, plugId, globalId ) { debugOutput( DEBUG_LEVEL_VERBOSE, "nodeId = %d, subunitType = %d, " "subunitId = %d, functionBlockType = %d, " "functionBlockId = %d, addressType = %d, " "direction = %d, id = %d\n", unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), functionBlockType, functionBlockId, plugAddressType, plugDirection, plugId ); } Plug::Plug( const Plug& rhs ) : AVC::Plug( rhs ) { } Plug::Plug() : AVC::Plug() { } Plug::~Plug() { } bool Plug::discover() { if ( !discoverPlugType() ) { debugError( "discover: Could not discover plug type (%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverName() ) { debugError( "Could not discover name (%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverNoOfChannels() ) { debugError( "Could not discover number of channels " "(%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverChannelPosition() ) { debugError( "Could not discover channel positions " "(%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverChannelName() ) { debugError( "Could not discover channel name " "(%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverClusterInfo() ) { debugError( "Could not discover channel name " "(%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverStreamFormat() ) { debugError( "Could not discover stream format " "(%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverSupportedStreamFormats() ) { debugError( "Could not discover supported stream formats " "(%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } return m_unit->getPlugManager().addPlug( *this ); } bool Plug::discoverConnections() { return discoverConnectionsInput() && discoverConnectionsOutput(); } bool Plug::discoverPlugType() { ExtendedPlugInfoCmd extPlugInfoCmd = setPlugAddrToPlugInfoCmd(); ExtendedPlugInfoInfoType extendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_PlugType ); extendedPlugInfoInfoType.initialize(); extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType ); extPlugInfoCmd.setVerbose( getDebugLevel() ); if ( !extPlugInfoCmd.fire() ) { debugError( "plug type command failed\n" ); return false; } m_infoPlugType = eAPT_Unknown; if ( extPlugInfoCmd.getResponse() == AVCCommand::eR_Implemented ) { ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType(); if ( infoType && infoType->m_plugType ) { plug_type_t plugType = infoType->m_plugType->m_plugType; debugOutput( DEBUG_LEVEL_VERBOSE, "plug %d is of type %d (%s)\n", m_id, plugType, extendedPlugInfoPlugTypeToString( plugType ) ); switch ( plugType ) { case ExtendedPlugInfoPlugTypeSpecificData::eEPIPT_IsoStream: m_infoPlugType = eAPT_IsoStream; break; case ExtendedPlugInfoPlugTypeSpecificData::eEPIPT_AsyncStream: m_infoPlugType = eAPT_AsyncStream; break; case ExtendedPlugInfoPlugTypeSpecificData::eEPIPT_Midi: m_infoPlugType = eAPT_Midi; break; case ExtendedPlugInfoPlugTypeSpecificData::eEPIPT_Sync: m_infoPlugType = eAPT_Sync; break; case ExtendedPlugInfoPlugTypeSpecificData::eEPIPT_Analog: m_infoPlugType = eAPT_Analog; break; case ExtendedPlugInfoPlugTypeSpecificData::eEPIPT_Digital: m_infoPlugType = eAPT_Digital; break; default: m_infoPlugType = eAPT_Unknown; } } } else { debugError( "Plug does not implement extended plug info plug " "type info command\n" ); return false; } return true; } bool Plug::discoverName() { ExtendedPlugInfoCmd extPlugInfoCmd = setPlugAddrToPlugInfoCmd(); ExtendedPlugInfoInfoType extendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_PlugName ); extendedPlugInfoInfoType.initialize(); extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType ); extPlugInfoCmd.setVerbose( getDebugLevel() ); if ( !extPlugInfoCmd.fire() ) { debugError( "name command failed\n" ); return false; } ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType(); if ( infoType && infoType->m_plugName ) { std::string name = infoType->m_plugName->m_name; debugOutput( DEBUG_LEVEL_VERBOSE, "plug %d has name '%s'\n", m_id, name.c_str() ); m_name = name; } return true; } bool Plug::discoverNoOfChannels() { ExtendedPlugInfoCmd extPlugInfoCmd = setPlugAddrToPlugInfoCmd(); //extPlugInfoCmd.setVerbose( true ); ExtendedPlugInfoInfoType extendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_NoOfChannels ); extendedPlugInfoInfoType.initialize(); extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType ); extPlugInfoCmd.setVerbose( getDebugLevel() ); if ( !extPlugInfoCmd.fire() ) { debugError( "number of channels command failed\n" ); return false; } ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType(); if ( infoType && infoType->m_plugNrOfChns ) { nr_of_channels_t nrOfChannels = infoType->m_plugNrOfChns->m_nrOfChannels; debugOutput( DEBUG_LEVEL_VERBOSE, "plug %d has %d channels\n", m_id, nrOfChannels ); m_nrOfChannels = nrOfChannels; } return true; } bool Plug::discoverChannelPosition() { ExtendedPlugInfoCmd extPlugInfoCmd = setPlugAddrToPlugInfoCmd(); ExtendedPlugInfoInfoType extendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_ChannelPosition ); extendedPlugInfoInfoType.initialize(); extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType ); extPlugInfoCmd.setVerbose( getDebugLevel() ); if ( !extPlugInfoCmd.fire() ) { debugError( "channel position command failed\n" ); return false; } ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType(); if ( infoType && infoType->m_plugChannelPosition ) { if ( !copyClusterInfo( *( infoType->m_plugChannelPosition ) ) ) { debugError( "Could not copy channel position " "information\n" ); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "plug %d: channel position information " "retrieved\n", m_id ); debugOutputClusterInfos( DEBUG_LEVEL_VERBOSE ); } return true; } bool Plug::copyClusterInfo(ExtendedPlugInfoPlugChannelPositionSpecificData& channelPositionData ) { int index = 1; for ( ExtendedPlugInfoPlugChannelPositionSpecificData::ClusterInfoVector::const_iterator it = channelPositionData.m_clusterInfos.begin(); it != channelPositionData.m_clusterInfos.end(); ++it ) { const ExtendedPlugInfoPlugChannelPositionSpecificData::ClusterInfo* extPlugSpClusterInfo = &( *it ); ClusterInfo clusterInfo; clusterInfo.m_nrOfChannels = extPlugSpClusterInfo->m_nrOfChannels; clusterInfo.m_index = index; index++; for ( ExtendedPlugInfoPlugChannelPositionSpecificData::ChannelInfoVector::const_iterator cit = extPlugSpClusterInfo->m_channelInfos.begin(); cit != extPlugSpClusterInfo->m_channelInfos.end(); ++cit ) { const ExtendedPlugInfoPlugChannelPositionSpecificData::ChannelInfo* extPlugSpChannelInfo = &( *cit ); ChannelInfo channelInfo; channelInfo.m_streamPosition = extPlugSpChannelInfo->m_streamPosition-1; // FIXME: this can only become a mess with the two meanings // of the location parameter. the audio style meaning // starts from 1, the midi style meaning from 0 // lucky for us we recalculate this for the midi channels // and don't use this value. channelInfo.m_location = extPlugSpChannelInfo->m_location; clusterInfo.m_channelInfos.push_back( channelInfo ); } m_clusterInfos.push_back( clusterInfo ); } return true; } bool Plug::discoverChannelName() { for ( ClusterInfoVector::iterator clit = m_clusterInfos.begin(); clit != m_clusterInfos.end(); ++clit ) { ClusterInfo* clitInfo = &*clit; for ( ChannelInfoVector::iterator pit = clitInfo->m_channelInfos.begin(); pit != clitInfo->m_channelInfos.end(); ++pit ) { ChannelInfo* channelInfo = &*pit; ExtendedPlugInfoCmd extPlugInfoCmd = setPlugAddrToPlugInfoCmd(); ExtendedPlugInfoInfoType extendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_ChannelName ); extendedPlugInfoInfoType.initialize(); extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType ); extPlugInfoCmd.setVerbose( getDebugLevel() ); ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType(); if ( infoType ) { infoType->m_plugChannelName->m_streamPosition = channelInfo->m_streamPosition + 1; } if ( !extPlugInfoCmd.fire() ) { debugError( "channel name command failed\n" ); return false; } infoType = extPlugInfoCmd.getInfoType(); if ( infoType && infoType->m_plugChannelName ) { debugOutput( DEBUG_LEVEL_VERBOSE, "plug %d stream " "position %d: channel name = %s\n", m_id, channelInfo->m_streamPosition, infoType->m_plugChannelName->m_plugChannelName.c_str() ); channelInfo->m_name = infoType->m_plugChannelName->m_plugChannelName; } } } return true; } bool Plug::discoverClusterInfo() { if ( m_infoPlugType == eAPT_Sync ) { // If the plug is of type sync it is either a normal 2 channel // stream (not compound stream) or it is a compound stream // with exactly one cluster. This depends on the // extended stream format command version which is used. // We are not interested in this plug so we skip it. debugOutput( DEBUG_LEVEL_VERBOSE, "%s plug %d is of type sync -> skip\n", getName(), m_id ); return true; } for ( ClusterInfoVector::iterator clit = m_clusterInfos.begin(); clit != m_clusterInfos.end(); ++clit ) { ClusterInfo* clusterInfo = &*clit; ExtendedPlugInfoCmd extPlugInfoCmd = setPlugAddrToPlugInfoCmd(); ExtendedPlugInfoInfoType extendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_ClusterInfo ); extendedPlugInfoInfoType.initialize(); extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType ); extPlugInfoCmd.setVerbose( getDebugLevel() ); extPlugInfoCmd.getInfoType()->m_plugClusterInfo->m_clusterIndex = clusterInfo->m_index; if ( !extPlugInfoCmd.fire() ) { debugError( "cluster info command failed\n" ); return false; } ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType(); if ( infoType && infoType->m_plugClusterInfo ) { debugOutput( DEBUG_LEVEL_VERBOSE, "%s plug %d: cluster index = %d, " "portType %s, cluster name = %s\n", getName(), m_id, infoType->m_plugClusterInfo->m_clusterIndex, extendedPlugInfoClusterInfoPortTypeToString( infoType->m_plugClusterInfo->m_portType ), infoType->m_plugClusterInfo->m_clusterName.c_str() ); clusterInfo->m_portType = infoType->m_plugClusterInfo->m_portType; clusterInfo->m_name = infoType->m_plugClusterInfo->m_clusterName; } } return true; } bool Plug::discoverConnectionsInput() { ExtendedPlugInfoCmd extPlugInfoCmd = setPlugAddrToPlugInfoCmd(); ExtendedPlugInfoInfoType extendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_PlugInput ); extendedPlugInfoInfoType.initialize(); extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType ); extPlugInfoCmd.setVerbose( getDebugLevel() ); if ( !extPlugInfoCmd.fire() ) { debugError( "plug type command failed\n" ); return false; } if ( extPlugInfoCmd.getResponse() == AVCCommand::eR_Rejected ) { // Plugs does not like to be asked about connections debugOutput( DEBUG_LEVEL_VERBOSE, "Plug '%s' rejects " "connections command\n", getName() ); return true; } ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType(); if ( infoType && infoType->m_plugInput ) { PlugAddressSpecificData* plugAddress = infoType->m_plugInput->m_plugAddress; if ( plugAddress->m_addressMode == PlugAddressSpecificData::ePAM_Undefined ) { // This plug has no input connection return true; } if ( !discoverConnectionsFromSpecificData( eAPD_Input, plugAddress, m_inputConnections ) ) { debugWarning( "Could not discover connections for plug '%s'\n", getName() ); } } else { debugError( "no valid info type for plug '%s'\n", getName() ); return false; } return true; } bool Plug::discoverConnectionsOutput() { ExtendedPlugInfoCmd extPlugInfoCmd = setPlugAddrToPlugInfoCmd(); ExtendedPlugInfoInfoType extendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_PlugOutput ); extendedPlugInfoInfoType.initialize(); extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType ); extPlugInfoCmd.setVerbose( getDebugLevel() ); if ( !extPlugInfoCmd.fire() ) { debugError( "plug type command failed\n" ); return false; } if ( extPlugInfoCmd.getResponse() == AVCCommand::eR_Rejected ) { // Plugs does not like to be asked about connections debugOutput( DEBUG_LEVEL_VERBOSE, "Plug '%s' rejects " "connections command\n", getName() ); return true; } ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType(); if ( infoType && infoType->m_plugOutput ) { if ( infoType->m_plugOutput->m_nrOfOutputPlugs != infoType->m_plugOutput->m_outputPlugAddresses.size() ) { debugError( "number of output plugs (%d) disagree with " "number of elements in plug address vector (%zd)\n", infoType->m_plugOutput->m_nrOfOutputPlugs, infoType->m_plugOutput->m_outputPlugAddresses.size()); } if ( infoType->m_plugOutput->m_nrOfOutputPlugs == 0 ) { // This plug has no output connections return true; } for ( unsigned int i = 0; i < infoType->m_plugOutput->m_outputPlugAddresses.size(); ++i ) { PlugAddressSpecificData* plugAddress = infoType->m_plugOutput->m_outputPlugAddresses[i]; if ( !discoverConnectionsFromSpecificData( eAPD_Output, plugAddress, m_outputConnections ) ) { debugWarning( "Could not discover connections for " "plug '%s'\n", getName() ); } } } else { debugError( "no valid info type for plug '%s'\n", getName() ); return false; } return true; } ExtendedPlugInfoCmd Plug::setPlugAddrToPlugInfoCmd() { ExtendedPlugInfoCmd extPlugInfoCmd( m_unit->get1394Service() ); switch( getSubunitType() ) { case eST_Unit: { UnitPlugAddress::EPlugType ePlugType = UnitPlugAddress::ePT_Unknown; switch ( m_addressType ) { case eAPA_PCR: ePlugType = UnitPlugAddress::ePT_PCR; break; case eAPA_ExternalPlug: ePlugType = UnitPlugAddress::ePT_ExternalPlug; break; case eAPA_AsynchronousPlug: ePlugType = UnitPlugAddress::ePT_AsynchronousPlug; break; default: ePlugType = UnitPlugAddress::ePT_Unknown; } UnitPlugAddress unitPlugAddress( ePlugType, m_id ); extPlugInfoCmd.setPlugAddress( PlugAddress( convertPlugDirection( getPlugDirection() ), PlugAddress::ePAM_Unit, unitPlugAddress ) ); } break; case eST_Music: case eST_Audio: { switch( m_addressType ) { case eAPA_SubunitPlug: { SubunitPlugAddress subunitPlugAddress( m_id ); extPlugInfoCmd.setPlugAddress( PlugAddress( convertPlugDirection( getPlugDirection() ), PlugAddress::ePAM_Subunit, subunitPlugAddress ) ); } break; case eAPA_FunctionBlockPlug: { FunctionBlockPlugAddress functionBlockPlugAddress( m_functionBlockType, m_functionBlockId, m_id ); extPlugInfoCmd.setPlugAddress( PlugAddress( convertPlugDirection( getPlugDirection() ), PlugAddress::ePAM_FunctionBlock, functionBlockPlugAddress ) ); } break; default: extPlugInfoCmd.setPlugAddress(PlugAddress()); } } break; default: debugError( "Unknown subunit type\n" ); } extPlugInfoCmd.setNodeId( m_unit->getConfigRom().getNodeId() ); extPlugInfoCmd.setCommandType( AVCCommand::eCT_Status ); extPlugInfoCmd.setSubunitId( getSubunitId() ); extPlugInfoCmd.setSubunitType( getSubunitType() ); return extPlugInfoCmd; } } libffado-2.4.5/src/bebob/bebob_avplug.h0000644000175000001440000000525214206145246017330 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_AVPLUG_H #define BEBOB_AVPLUG_H #include "libavc/ccm/avc_signal_source.h" #include "libavc/streamformat/avc_extended_stream_format.h" #include "libavc/general/avc_extended_plug_info.h" #include "libavc/general/avc_extended_cmd_generic.h" #include "libavc/avc_definitions.h" #include "libavc/general/avc_generic.h" #include "libavc/general/avc_plug.h" #include "libutil/serialize.h" #include "debugmodule/debugmodule.h" class Ieee1394Service; class ConfigRom; namespace BeBoB { class AvDevice; class Plug : public AVC::Plug { public: // \todo This constructors sucks. too many parameters. fix it. Plug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId ); Plug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId, int globalId ); Plug( const Plug& rhs ); virtual ~Plug(); bool discover(); bool discoverConnections(); public: protected: Plug(); bool discoverPlugType(); bool discoverName(); bool discoverNoOfChannels(); bool discoverChannelPosition(); bool discoverChannelName(); bool discoverClusterInfo(); bool discoverConnectionsInput(); bool discoverConnectionsOutput(); private: bool copyClusterInfo(AVC::ExtendedPlugInfoPlugChannelPositionSpecificData& channelPositionData ); AVC::ExtendedPlugInfoCmd setPlugAddrToPlugInfoCmd(); }; } #endif // BEBOB_AVPLUG_H libffado-2.4.5/src/bebob/bebob_dl_bcd.cpp0000644000175000001440000002302114206145246017566 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "bebob_dl_bcd.h" #include namespace BeBoB { enum { BCDMagic = 0x446f4362, }; // XXX not very nice tool box function? std::string makeString( fb_octlet_t v ) { std::string s; for ( unsigned int i=0; i ( &v )[i]; } return s; } std::string makeDate( fb_octlet_t v ) { std::string s; char* vc = reinterpret_cast ( &v ); s += vc[6]; s += vc[7]; s += '.'; s += vc[4]; s += vc[5]; s += '.'; s += vc[0]; s += vc[1]; s += vc[2]; s += vc[3]; return s; } std::string makeTime( fb_octlet_t v ) { std::string s; char* vc = reinterpret_cast( &v ); s += vc[0]; s += vc[1]; s += ':'; s += vc[2]; s += vc[3]; s += ':'; s += vc[4]; s += vc[5]; s += vc[6]; s += vc[7]; return s; } enum { BCDFileVersionOffset = 0x28, V0HeaderCRCOffset = 0x2c, V0HeaderSize = 0x60, V1HeaderCRC0ffset = 0x2c, V1HeaderSize = 0x70, }; IMPL_DEBUG_MODULE( BCD, BCD, DEBUG_LEVEL_NORMAL ); }; BeBoB::BCD::BCD( std::string filename ) : m_file( 0 ) , m_filename( filename ) , m_bcd_version( -1 ) , m_softwareDate( 0 ) , m_softwareTime( 0 ) , m_softwareId( 0 ) , m_softwareVersion( 0 ) , m_hardwareId( 0 ) , m_vendorOUI( 0 ) , m_imageBaseAddress( 0 ) , m_imageLength( 0 ) , m_imageOffset( 0 ) , m_imageCRC( 0 ) , m_cneLength( 0 ) , m_cneOffset( 0 ) , m_cneCRC( 0 ) { initCRC32Table(); } BeBoB::BCD::~BCD() { if ( m_file ) { fclose( m_file ); } } bool BeBoB::BCD::parse() { using namespace std; m_file = fopen( m_filename.c_str(), "r" ); if ( !m_file ) { debugError( "parse: Could not open file '%s'\n", m_filename.c_str() ); return false; } fb_quadlet_t identifier; size_t bytes_read = fread( &identifier, 1, sizeof( identifier ), m_file ); if ( bytes_read != sizeof( identifier ) ) { debugError( "parse: 4 bytes read failed at position 0\n" ); return false; } if ( identifier != BCDMagic ) { debugError( "parse: File has not BCD header magic, 0x%08x expected, " "0x%08x found\n", BCDMagic, identifier ); return false; } if ( fseek( m_file, BCDFileVersionOffset, SEEK_SET ) == -1 ) { debugError( "parse: fseek failed\n" ); return false; } bytes_read = fread( &m_bcd_version, 1, sizeof( fb_quadlet_t ), m_file ); if ( bytes_read != sizeof( fb_quadlet_t ) ) { debugError( "parse: %zd bytes read at position %d failed\n", sizeof( fb_quadlet_t ), BCDFileVersionOffset ); return false; } unsigned int headerSize = 0; unsigned int crcOffset = 0; switch( m_bcd_version ) { case 0: headerSize = V0HeaderSize; crcOffset = V0HeaderCRCOffset; break; case 1: headerSize = V1HeaderSize; crcOffset = V1HeaderCRC0ffset; break; default: debugError( "parse: Unknown BCD file version %d found\n", m_bcd_version ); return false; } if ( !checkHeaderCRC( crcOffset, headerSize ) ) { debugError( "parse: Header CRC check failed\n" ); return false; } if ( !readHeaderInfo() ) { debugError( "parse: Could not read all header info\n" ); return false; } return true; } bool BeBoB::BCD::readHeaderInfo() { if ( !read( 0x08, &m_softwareDate ) ) { return false; } if ( !read( 0x10, &m_softwareTime ) ) { return false; } if ( !read( 0x18, &m_softwareId ) ) { return false; } if ( !read( 0x1c, &m_softwareVersion ) ) { return false; } if ( !read( 0x20, &m_hardwareId ) ) { return false; } if ( !read( 0x24, &m_vendorOUI ) ) { return false; } if ( !read( 0x30, &m_imageOffset ) ) { return false; } if ( !read( 0x34, &m_imageBaseAddress ) ) { return false; } if ( !read( 0x38, &m_imageLength ) ) { return false; } if ( !read( 0x3c, &m_imageCRC ) ) { return false; } if ( !read( 0x50, &m_cneOffset ) ) { return false; } if ( !read( 0x58, &m_cneLength ) ) { return false; } if ( !read( 0x5c, &m_cneCRC ) ) { return false; } return true; } bool BeBoB::BCD::read( int addr, fb_quadlet_t* q ) { if ( std::fseek( m_file, addr, SEEK_SET ) == -1 ) { debugError( "read: seek to position 0x%08x failed\n", addr ); return false; } size_t bytes_read = std::fread( q, 1, sizeof( *q ), m_file ); if ( bytes_read != sizeof( *q ) ) { debugError( "read: %zd byte read failed at position 0x%08x\n", sizeof( *q ), addr ); return false; } return true; } bool BeBoB::BCD::read( int addr, fb_octlet_t* o ) { if ( std::fseek( m_file, addr, SEEK_SET ) == -1 ) { debugError( "read: seek to position 0x%08x failed\n", addr ); return false; } size_t bytes_read = std::fread( o, 1, sizeof( *o ), m_file ); if ( bytes_read != sizeof( *o ) ) { debugError( "read: %zd byte read failed at position 0x%08x\n", sizeof( *o ), addr ); return false; } return true; } bool BeBoB::BCD::read( int addr, unsigned char* b, size_t len ) { if ( std::fseek( m_file, addr, SEEK_SET ) == -1 ) { debugError( "read: seek to position 0x%08x failed\n", addr ); return false; } size_t bytes_read = std::fread( b, 1, len, m_file ); if ( bytes_read != len ) { debugError( "read: %zd byte read failed at position 0x%08x\n", len, addr ); return false; } return true; } void BeBoB::BCD::initCRC32Table() { unsigned long polynomial = 0x04c11db7; for ( int i = 0; i <= 0xff; ++i ) { crc32_table[i] = reflect( i, 8 ) << 24; for ( int j = 0; j < 8; ++j ) { crc32_table[i] = (crc32_table[i] << 1) ^ (crc32_table[i] & (1 << 31) ? polynomial : 0); } crc32_table[i] = reflect( crc32_table[i], 32 ); } } unsigned long BeBoB::BCD::reflect( unsigned long ref, char ch ) { unsigned long value = 0; for ( int i = 1; i < (ch + 1); ++i ) { if(ref & 1) { value |= 1 << (ch - i); } ref >>= 1; } return value; } unsigned int BeBoB::BCD::getCRC( unsigned char* text, size_t len ) { unsigned long crc = 0xffffffff; unsigned char* buffer; buffer = text; while ( len-- ) { crc = (crc >> 8) ^ crc32_table[(crc & 0xff) ^ *buffer++]; } return crc ^ 0xffffffff; } bool BeBoB::BCD::checkHeaderCRC( unsigned int crcOffset, unsigned int headerSize ) { fb_quadlet_t headerCRC; if ( !read( crcOffset, &headerCRC ) ) { debugError( "checkHeaderCRC: Could not read header CRC\n" ); return false; } const int headerLength = headerSize; unsigned char buf[headerLength]; if ( !read( 0x00, buf, headerLength ) ) { debugError( "checkHeaderCRC: Could not read complete header from file\n" ); return false; } buf[crcOffset+0] = 0x00; buf[crcOffset+1] = 0x00; buf[crcOffset+2] = 0x00; buf[crcOffset+3] = 0x00; fb_quadlet_t calcCRC = getCRC( buf, headerLength ); if ( headerCRC != calcCRC ) { debugError( "checkHeaderCRC: CRC check failed, 0x%08x expected, " "0x%08x calculated\n", headerCRC, calcCRC ); return false; } return true; } void BeBoB::BCD::displayInfo() { using namespace std; printf( "BCD Info\n" ); printf( "\tBCD File Version\t%d\n", m_bcd_version ); printf( "\tSoftware Date:\t\t%s, %s\n", makeDate( m_softwareDate ).c_str(), makeTime( m_softwareTime ).c_str() ); printf( "\tSoftware Version:\t0x%08x\n", m_softwareVersion ); printf( "\tSoftware Id:\t\t0x%08x\n", m_softwareId ); printf( "\tHardware ID:\t\t0x%08x\n", m_hardwareId ); printf( "\tVendor OUI:\t\t0x%08x\n", m_vendorOUI ); printf( "\tImage Offset:\t\t0x%08x\n", m_imageOffset ); printf( "\tImage Base Address:\t0x%08x\n", m_imageBaseAddress ); printf( "\tImage Length:\t\t0x%08x\n", m_imageLength ); printf( "\tImage CRC:\t\t0x%08x\n", m_imageCRC ); printf( "\tCNE Length:\t\t0x%08x\n", m_cneLength ); printf( "\tCNE Offset:\t\t0x%08x\n", m_cneOffset ); printf( "\tCNE CRC:\t\t0x%08x\n", m_cneCRC ); } libffado-2.4.5/src/bebob/bebob_dl_bcd.h0000644000175000001440000000646314206145246017246 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_DL_BCD_H #define BEBOB_DL_BCD_H #include "fbtypes.h" #include "debugmodule/debugmodule.h" #include #include namespace BeBoB { class BCD { public: BCD( std::string filename ); ~BCD(); bool parse(); fb_octlet_t getSoftwareDate() const { return m_softwareDate; } fb_octlet_t getSoftwareTime() const { return m_softwareTime; } fb_quadlet_t getSoftwareId() const { return m_softwareId; } fb_quadlet_t getSoftwareVersion() const { return m_softwareVersion; } fb_quadlet_t getHardwareId() const { return m_hardwareId; } fb_quadlet_t getVendorOUI() const { return m_vendorOUI; } fb_quadlet_t getImageBaseAddress() const { return m_imageBaseAddress; } fb_quadlet_t getImageOffset() const { return m_imageOffset; } fb_quadlet_t getImageLength() const { return m_imageLength; } fb_quadlet_t getImageCRC() const { return m_imageCRC; } fb_quadlet_t getCnEOffset() const { return m_cneOffset; } fb_quadlet_t getCnELength() const { return m_cneLength; } fb_quadlet_t getCnECRC() const { return m_cneCRC; } bool read( int addr, fb_quadlet_t* q ); bool read( int addr, fb_octlet_t* o ); bool read( int addr, unsigned char* b, size_t len ); void displayInfo(); protected: unsigned long crc32_table[256]; void initCRC32Table(); unsigned long reflect(unsigned long ref, char ch); unsigned int getCRC(unsigned char* text, size_t len); bool checkHeaderCRC( unsigned int crcOffset, unsigned int headerSize ); bool readHeaderInfo(); protected: std::FILE* m_file; std::string m_filename; fb_quadlet_t m_bcd_version; fb_octlet_t m_softwareDate; fb_octlet_t m_softwareTime; fb_quadlet_t m_softwareId; fb_quadlet_t m_softwareVersion; fb_quadlet_t m_hardwareId; fb_quadlet_t m_vendorOUI; fb_quadlet_t m_imageBaseAddress; fb_quadlet_t m_imageLength; fb_quadlet_t m_imageOffset; fb_quadlet_t m_imageCRC; fb_quadlet_t m_cneLength; fb_quadlet_t m_cneOffset; fb_quadlet_t m_cneCRC; DECLARE_DEBUG_MODULE; }; std::string makeString( fb_octlet_t v ); std::string makeDate( fb_octlet_t v ); std::string makeTime( fb_octlet_t v ); }; #endif libffado-2.4.5/src/bebob/bebob_dl_codes.cpp0000644000175000001440000002250714206145246020143 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "bebob/bebob_dl_codes.h" #include "bebob/bebob_dl_bcd.h" unsigned short BeBoB::CommandCodes::m_gCommandId = 0; BeBoB::CommandCodes::CommandCodes( fb_quadlet_t protocolVersion, fb_byte_t commandCode, size_t msgSize, fb_byte_t operandSizeRequest, fb_byte_t operandSizeRespone ) : m_commandId( m_gCommandId++ ) , m_protocolVersion( protocolVersion ) , m_commandCode( commandCode ) , m_msgSize( msgSize ) , m_operandSizeRequest( operandSizeRequest ) , m_operandSizeResponse( operandSizeRespone ) , m_resp_protocolVersion( 0 ) , m_resp_commandId( 0 ) , m_resp_commandCode( 0 ) , m_resp_operandSize( 0 ) { } BeBoB::CommandCodes::~CommandCodes() { } bool BeBoB::CommandCodes::serialize( Util::Cmd::IOSSerialize& se ) { byte_t tmp; bool result = se.write( m_protocolVersion, "CommandCodes: protocol version" ); tmp = m_commandId & 0xff; result &= se.write( tmp, "CommandCodes: command id low" ); tmp = m_commandId >> 8; result &= se.write( tmp, "CommandCodes: command id high" ); result &= se.write( m_commandCode, "CommandCodes: command code" ); result &= se.write( m_operandSizeRequest, "CommandCodes: request operand size" ); return result; } bool BeBoB::CommandCodes::deserialize( Util::Cmd::IISDeserialize& de ) { bool result = de.read( &m_resp_protocolVersion ); fb_byte_t tmp; result &= de.read( &tmp ); m_resp_commandId = tmp; result &= de.read( &tmp ); m_resp_commandId |= tmp << 8; result &= de.read( &m_resp_commandCode ); result &= de.read( &m_resp_operandSize ); return result; } size_t BeBoB::CommandCodes::getMaxSize() { return 2 * sizeof( fb_quadlet_t ) + m_msgSize; } //////////////////////////////// BeBoB::CommandCodesReset::CommandCodesReset( fb_quadlet_t protocolVersion, EStartMode startMode ) : CommandCodes( protocolVersion, eCmdC_Reset, sizeof( m_startMode ), 1, 0 ) , m_startMode( startMode ) { } BeBoB::CommandCodesReset::~CommandCodesReset() { } bool BeBoB::CommandCodesReset::serialize( Util::Cmd::IOSSerialize& se ) { bool result = CommandCodes::serialize( se ); result &= se.write( m_startMode, "CommandCodesReset: boot mode" ); return result; } bool BeBoB::CommandCodesReset::deserialize( Util::Cmd::IISDeserialize& de ) { return CommandCodes::deserialize( de ); } //////////////////////////////// BeBoB::CommandCodesProgramGUID::CommandCodesProgramGUID( fb_quadlet_t protocolVersion, fb_octlet_t guid ) : CommandCodes( protocolVersion, eCmdC_ProgramGUID, sizeof( m_guid ), 2, 0 ) , m_guid( guid ) { } BeBoB::CommandCodesProgramGUID::~CommandCodesProgramGUID() { } bool BeBoB::CommandCodesProgramGUID::serialize( Util::Cmd::IOSSerialize& se ) { bool result = CommandCodes::serialize( se ); fb_quadlet_t tmp = m_guid >> 32; result &= se.write( tmp, "CommandCodesProgramGUID: GUID (high)" ); tmp = m_guid & 0xffffffff; result &= se.write( tmp, "CommandCodesProgramGUID: GUID (low)" ); return result; } bool BeBoB::CommandCodesProgramGUID::deserialize( Util::Cmd::IISDeserialize& de ) { return CommandCodes::deserialize( de ); } //////////////////////////////// BeBoB::CommandCodesDownloadStart::CommandCodesDownloadStart( fb_quadlet_t protocolVersion, EObject object ) : CommandCodes( protocolVersion, eCmdC_DownloadStart, 10*4, 10, 1 ) , m_object( object ) , m_date( 0 ) , m_time( 0 ) , m_id( 0 ) , m_version( 0 ) , m_address( 0 ) , m_length( 0 ) , m_crc( 0 ) { } BeBoB::CommandCodesDownloadStart::~CommandCodesDownloadStart() { } bool BeBoB::CommandCodesDownloadStart::serialize( Util::Cmd::IOSSerialize& se ) { bool result = CommandCodes::serialize( se ); result &= se.write( m_object, "CommandCodesDownloadStart: object" ); for ( unsigned int i = 0; i < sizeof( m_date ); ++i ) { fb_byte_t* tmp = ( fb_byte_t* )( &m_date ); result &= se.write( tmp[i], "CommandCodesDownloadStart: date" ); } for ( unsigned int i = 0; i < sizeof( m_date ); ++i ) { fb_byte_t* tmp = ( fb_byte_t* )( &m_time ); result &= se.write( tmp[i], "CommandCodesDownloadStart: time" ); } result &= se.write( m_id, "CommandCodesDownloadStart: id" ); result &= se.write( m_version, "CommandCodesDownloadStart: version" ); result &= se.write( m_address, "CommandCodesDownloadStart: address" ); result &= se.write( m_length, "CommandCodesDownloadStart: length" ); result &= se.write( m_crc, "CommandCodesDownloadStart: crc" ); return result; } bool BeBoB::CommandCodesDownloadStart::deserialize( Util::Cmd::IISDeserialize& de ) { bool result = CommandCodes::deserialize( de ); result &= de.read( reinterpret_cast( &m_resp_max_block_size ) ); return result; } //////////////////////////////// BeBoB::CommandCodesDownloadBlock::CommandCodesDownloadBlock( fb_quadlet_t protocolVersion ) : CommandCodes( protocolVersion, eCmdC_DownloadBlock, 12, 3, 2 ) , m_seqNumber( 0 ) , m_address ( 0 ) , m_resp_seqNumber( 0 ) , m_resp_errorCode( 0 ) { } BeBoB::CommandCodesDownloadBlock::~CommandCodesDownloadBlock() { } bool BeBoB::CommandCodesDownloadBlock::serialize( Util::Cmd::IOSSerialize& se ) { bool result = CommandCodes::serialize( se ); result &= se.write( m_seqNumber, "CommandCodesDownloadBlock: sequence number" ); result &= se.write( m_address, "CommandCodesDownloadBlock: address" ); result &= se.write( m_numBytes, "CommandCodesDownloadBlock: number of bytes" ); return result; } bool BeBoB::CommandCodesDownloadBlock::deserialize( Util::Cmd::IISDeserialize& de ) { bool result = CommandCodes::deserialize( de ); result &= de.read( &m_resp_seqNumber ); result &= de.read( &m_resp_errorCode ); return result; } //////////////////////////////// BeBoB::CommandCodesDownloadEnd::CommandCodesDownloadEnd( fb_quadlet_t protocolVersion ) : CommandCodes( protocolVersion, eCmdC_DownloadEnd, 2, 0, 2 ) { } BeBoB::CommandCodesDownloadEnd::~CommandCodesDownloadEnd() { } bool BeBoB::CommandCodesDownloadEnd::serialize( Util::Cmd::IOSSerialize& se ) { return CommandCodes::serialize( se ); } bool BeBoB::CommandCodesDownloadEnd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result = CommandCodes::deserialize( de ); result &= de.read( &m_resp_crc32 ); result &= de.read( &m_resp_valid ); return result; } //////////////////////////////// BeBoB::CommandCodesInitializePersParam::CommandCodesInitializePersParam( fb_quadlet_t protocolVersion ) : CommandCodes( protocolVersion, eCmdC_InitPersParams, 0, 0, 0 ) { } BeBoB::CommandCodesInitializePersParam::~CommandCodesInitializePersParam() { } bool BeBoB::CommandCodesInitializePersParam::serialize( Util::Cmd::IOSSerialize& se ) { return CommandCodes::serialize( se ); } bool BeBoB::CommandCodesInitializePersParam::deserialize( Util::Cmd::IISDeserialize& de ) { return CommandCodes::deserialize( de ); } //////////////////////////////// BeBoB::CommandCodesInitializeConfigToFactorySetting::CommandCodesInitializeConfigToFactorySetting( fb_quadlet_t protocolVersion ) : CommandCodes( protocolVersion, eCmdC_InitConfigToFactorySetting, 0, 0, 0 ) { } BeBoB::CommandCodesInitializeConfigToFactorySetting::~CommandCodesInitializeConfigToFactorySetting() { } bool BeBoB::CommandCodesInitializeConfigToFactorySetting::serialize( Util::Cmd::IOSSerialize& se ) { return CommandCodes::serialize( se ); } bool BeBoB::CommandCodesInitializeConfigToFactorySetting::deserialize( Util::Cmd::IISDeserialize& de ) { return CommandCodes::deserialize( de ); } //////////////////////////////// BeBoB::CommandCodesGo::CommandCodesGo( fb_quadlet_t protocolVersion, EStartMode startMode ) : CommandCodes( protocolVersion, eCmdC_Go, sizeof( m_startMode ), 1, 1 ) , m_startMode( startMode ) { } BeBoB::CommandCodesGo::~CommandCodesGo() { } bool BeBoB::CommandCodesGo::serialize( Util::Cmd::IOSSerialize& se ) { bool result = CommandCodes::serialize( se ); result &= se.write( m_startMode, "CommandCodesGo: start mode" ); return result; } bool BeBoB::CommandCodesGo::deserialize( Util::Cmd::IISDeserialize& de ) { bool result = CommandCodes::deserialize( de ); result &= de.read( &m_resp_validCRC ); return result; } libffado-2.4.5/src/bebob/bebob_dl_codes.h0000644000175000001440000002362414206145246017611 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_DL_CODES_H #define BEBOB_DL_CODES_H #include "fbtypes.h" #include "libutil/cmd_serialize.h" namespace BeBoB { enum EBootloaderProtocolVersion { eBPV_Unknown = 0, eBPV_V1 = 1, eBPV_V2 = 2, eBPV_V3 = 3, }; enum EBootloaderCommandCodes { eCmdC_Halt = 0x01, eCmdC_Reset = 0x02, eCmdC_ReadImageCRC = 0x03, eCmdC_DownloadStart = 0x04, eCmdC_DownloadBlock = 0x05, eCmdC_DownloadEnd = 0x06, eCmdC_SwitchTo1394Shell = 0x07, eCmdC_ReadShellChars = 0x08, eCmdC_WriteShellChars = 0x09, eCmdC_ProgramGUID = 0x0a, eCmdC_ProgramMAC = 0x0b, eCmdC_InitPersParams = 0x0c, eCmdC_InitConfigToFactorySetting = 0x0d, eCmdC_SetDebugGUID = 0x0f, eCmdC_ProgramHWIdVersion = 0x10, eCmdC_Go = 0x11, }; ///////////////////////// class CommandCodes { public: CommandCodes( fb_quadlet_t protocolVersion, fb_byte_t commandCode, size_t msgSize, fb_byte_t operandSizeRequestField, fb_byte_t operandSizeResponseField ); virtual ~CommandCodes(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual size_t getMaxSize(); EBootloaderCommandCodes getCommandCode() const { return static_cast( m_commandCode ); } fb_byte_t getProtocolVersion() const { return m_protocolVersion; } size_t getMsgSize() const { return m_msgSize; } fb_byte_t getOperandSizeRequest() const { return m_operandSizeRequest; } fb_byte_t getOperandSizeResponse() const { return m_operandSizeResponse; } unsigned short getCommandId() const { return m_commandId; } fb_quadlet_t getRespProtocolVersion() const { return m_resp_protocolVersion; } unsigned short getRespCommandId() const { return m_resp_commandId; } fb_byte_t getRespCommandCode() const { return m_resp_commandCode; } fb_byte_t getRespOperandSize() const { return m_resp_operandSize; } fb_byte_t getRespSizeInQuadlets() const { return 2 + m_operandSizeResponse; } protected: static unsigned short m_gCommandId; unsigned short m_commandId; fb_quadlet_t m_protocolVersion; fb_byte_t m_commandCode; size_t m_msgSize; fb_byte_t m_operandSizeRequest; fb_byte_t m_operandSizeResponse; fb_quadlet_t m_resp_protocolVersion; unsigned short m_resp_commandId; fb_byte_t m_resp_commandCode; fb_byte_t m_resp_operandSize; }; ///////////////////////// class CommandCodesReset : public CommandCodes { public: enum EStartMode { eSM_Application = 0, eSM_Bootloader, eSM_Debugger, }; CommandCodesReset( fb_quadlet_t protocolVersion, EStartMode startMode ); virtual ~CommandCodesReset(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); EStartMode getStartMode() const { return static_cast( m_startMode ); } bool setStartMode( EStartMode startMode ) { m_startMode = startMode; return true; } private: fb_byte_t m_startMode; }; ///////////////////////// class CommandCodesProgramGUID : public CommandCodes { public: CommandCodesProgramGUID( fb_quadlet_t protocolVersion, fb_octlet_t guid ); virtual ~CommandCodesProgramGUID(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); fb_octlet_t getGUID() const { return m_guid; } bool setGUID( fb_octlet_t guid ) { m_guid = guid; return true; } private: fb_octlet_t m_guid; }; ///////////////////////// class CommandCodesDownloadStart : public CommandCodes { public: enum EObject { eO_Application = 0, eO_Config = 1, eO_Debugger = 2, eO_Bootloader = 3, eO_WarpImage = 4, eO_SerialBootCode = 5, }; CommandCodesDownloadStart( fb_quadlet_t protocolVersion, EObject object ); virtual ~CommandCodesDownloadStart(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); bool setDate( fb_octlet_t date ) { m_date = date; return true; } bool setTime( fb_octlet_t time ) { m_time = time; return true; } bool setId( fb_quadlet_t id ) { m_id = id; return true; } bool setVersion( fb_quadlet_t version ) { m_version = version; return true; } bool setBaseAddress( fb_quadlet_t address ) { m_address = address; return true; } bool setLength( fb_quadlet_t length ) { m_length = length; return true; } bool setCRC( fb_quadlet_t crc ) { m_crc = crc; return true; } int getMaxBlockSize() const { return m_resp_max_block_size; } private: fb_quadlet_t m_object; fb_octlet_t m_date; fb_octlet_t m_time; fb_quadlet_t m_id; fb_quadlet_t m_version; fb_quadlet_t m_address; fb_quadlet_t m_length; fb_quadlet_t m_crc; int m_resp_max_block_size; }; ///////////////////////// class CommandCodesDownloadBlock : public CommandCodes { public: CommandCodesDownloadBlock( fb_quadlet_t protocolVersion ); virtual ~CommandCodesDownloadBlock(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); bool setSeqNumber( fb_quadlet_t seqNumber ) { m_seqNumber = seqNumber; return true; } bool setAddress( fb_quadlet_t address ) { m_address = address; return true; } bool setNumberBytes( fb_quadlet_t numByte ) { m_numBytes = numByte; return true; } fb_quadlet_t getRespSeqNumber() const { return m_resp_seqNumber; } fb_quadlet_t getRespErrorCode() const { return m_resp_errorCode; } private: fb_quadlet_t m_seqNumber; fb_quadlet_t m_address; fb_quadlet_t m_numBytes; fb_quadlet_t m_resp_seqNumber; fb_quadlet_t m_resp_errorCode; }; ///////////////////////// class CommandCodesDownloadEnd : public CommandCodes { public: CommandCodesDownloadEnd( fb_quadlet_t protocolVersion ); virtual ~CommandCodesDownloadEnd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); fb_quadlet_t getRespCrc32() const { return m_resp_crc32; } bool getRespIsValid() const { return m_resp_valid == 0; } private: quadlet_t m_resp_crc32; quadlet_t m_resp_valid; }; ///////////////////////// class CommandCodesInitializePersParam : public CommandCodes { public: CommandCodesInitializePersParam( fb_quadlet_t protocolVersion ); virtual ~CommandCodesInitializePersParam(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); }; ///////////////////////// class CommandCodesInitializeConfigToFactorySetting : public CommandCodes { public: CommandCodesInitializeConfigToFactorySetting( fb_quadlet_t protocolVersion ); virtual ~CommandCodesInitializeConfigToFactorySetting(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); }; ///////////////////////// class CommandCodesGo : public CommandCodes { public: enum EStartMode { eSM_Application = 0, eSM_Debugger = 2, }; CommandCodesGo( fb_quadlet_t protocolVersion, EStartMode startMode ); virtual ~CommandCodesGo(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); EStartMode getStartMode() const { return static_cast( m_startMode ); } bool setStartMode( EStartMode startMode ) { m_startMode = startMode; return true; } fb_quadlet_t getRespIsValidCRC() const { return m_resp_validCRC; } private: fb_quadlet_t m_startMode; fb_quadlet_t m_resp_validCRC; }; }; #endif libffado-2.4.5/src/bebob/bebob_dl_mgr.cpp0000644000175000001440000005452714206145246017642 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "bebob_dl_mgr.h" #include "bebob_dl_codes.h" #include "bebob_dl_bcd.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/cmd_serialize.h" #include "libutil/Time.h" #include "libutil/ByteSwap.h" #include "ffadodevice.h" #include #include #include #include namespace BeBoB { enum { AddrRegInfo = 0xffffc8020000ULL, AddrRegReq = 0xffffc8021000ULL, AddrRegReqBuf = 0xffffc8021040ULL, AddrRegResp = 0xffffc8029000ULL, AddrRegRespBuf = 0xffffc8029040ULL, }; enum { RegInfoManufactorIdOffset = 0x00, RegInfoProtocolVersionOffset = 0x08, RegInfoBootloaderVersionOffset = 0x0c, RegInfoGUID = 0x10, RegInfoHardwareModelId = 0x18, RegInfoHardwareRevision = 0x1c, RegInfoSoftwareDate = 0x20, RegInfoSoftwareTime = 0x28, RegInfoSoftwareId = 0x30, RegInfoSoftwareVersion = 0x34, RegInfoBaseAddress = 0x38, RegInfoMaxImageLen = 0x3c, RegInfoBootloaderDate = 0x40, RegInfoBootloaderTime = 0x48, RegInfoDebuggerDate = 0x50, RegInfoDebuggerTime = 0x58, RegInfoDebuggerId = 0x60, RegInfoDebuggerVersion = 0x64 }; enum { MaxRetries = 10, }; IMPL_DEBUG_MODULE( BootloaderManager, BootloaderManager, DEBUG_LEVEL_NORMAL ); } BeBoB::BootloaderManager::BootloaderManager(Ieee1394Service& ieee1349service, fb_nodeid_t nodeId ) : m_ieee1394service( &ieee1349service ) , m_protocolVersion( eBPV_Unknown ) , m_isAppRunning( false ) , m_forceEnabled( false ) , m_bStartBootloader( true ) { memset( &m_cachedInfoRegs, 0, sizeof( m_cachedInfoRegs ) ); m_configRom = new ConfigRom( *m_ieee1394service, nodeId ); // XXX throw exception if initialize fails! m_configRom->initialize(); if ( !cacheInfoRegisters() ) { debugError( "BootloaderManager: could not cache info registers\n" ); } switch( m_cachedInfoRegs.m_protocolVersion ) { case 1: m_protocolVersion = eBPV_V1; break; case 3: m_protocolVersion = eBPV_V3; break; default: // exception? break; } pthread_mutex_init( &m_mutex, 0 ); pthread_cond_init( &m_cond, 0 ); m_functor = new MemberFunctor0< BeBoB::BootloaderManager*, void (BeBoB::BootloaderManager::*)() > ( this, &BeBoB::BootloaderManager::busresetHandler, false ); m_ieee1394service->addBusResetHandler( m_functor ); } BeBoB::BootloaderManager::~BootloaderManager() { m_ieee1394service->remBusResetHandler( m_functor ); delete( m_functor ); delete m_configRom; pthread_cond_destroy( &m_cond ); pthread_mutex_destroy( &m_mutex ); } bool BeBoB::BootloaderManager::cacheInfoRegisters() { if ( !m_configRom->updatedNodeId() ) { debugError( "cacheInfoRegisters: did not find device anymore\n" ); return false; } if ( !m_ieee1394service->read( 0xffc0 | m_configRom->getNodeId(), AddrRegInfo, sizeof( m_cachedInfoRegs )/4, reinterpret_cast( &m_cachedInfoRegs ) ) ) { return false; } if ( m_cachedInfoRegs.m_bootloaderVersion != 0x0 ) { m_isAppRunning = false; } else { m_isAppRunning = true; } m_cachedInfoRegs.m_guid = ( m_cachedInfoRegs.m_guid >> 32 ) | ( m_cachedInfoRegs.m_guid << 32 ); return true; } bool BeBoB::BootloaderManager::cacheInfoRegisters( int retries ) { for ( int i = 0; i < retries; ++i ) { if ( cacheInfoRegisters() ) { return true; } sleep( 1 ); printf("."); fflush(stdout); } return false; } std::string BeBoB::BootloaderManager::getSoftwareDate() { return makeDate( m_cachedInfoRegs.m_softwareDate ); } std::string BeBoB::BootloaderManager::getSoftwareTime() { return makeDate( m_cachedInfoRegs.m_softwareTime ); } void BeBoB::BootloaderManager::printInfoRegisters() { using namespace std; if ( !cacheInfoRegisters() ) { debugError( "Could not read info registers\n" ); return; } printf( "Info Registers\n" ); printf( "\tManufactors Id:\t\t%s\n", makeString( m_cachedInfoRegs.m_manId ).c_str() ); printf( "\tProtocol Version:\t0x%08x\n", m_cachedInfoRegs.m_protocolVersion ); printf( "\tBootloader Version:\t0x%08x\n", m_cachedInfoRegs.m_bootloaderVersion ); printf( "\tGUID:\t\t\t0x%08x%08x\n", ( unsigned int )( m_cachedInfoRegs.m_guid >> 32 ), ( unsigned int )( m_cachedInfoRegs.m_guid & 0xffffffff ) ); printf( "\tHardware Model ID:\t0x%08x\n", m_cachedInfoRegs.m_hardwareModelId ); printf( "\tHardware Revision:\t0x%08x\n", m_cachedInfoRegs.m_hardwareRevision ); if ( m_cachedInfoRegs.m_softwareDate && m_cachedInfoRegs.m_softwareTime ) { printf( "\tSoftware Date:\t\t%s, %s\n", makeDate( m_cachedInfoRegs.m_softwareDate ).c_str(), makeTime( m_cachedInfoRegs.m_softwareTime ).c_str() ); } printf( "\tSoftware Id:\t\t0x%08x\n", m_cachedInfoRegs.m_softwareId ); printf( "\tSoftware Version:\t0x%08x\n", m_cachedInfoRegs.m_softwareVersion ); printf( "\tBase Address:\t\t0x%08x\n", m_cachedInfoRegs.m_baseAddress ); printf( "\tMax. Image Len:\t\t0x%08x\n", m_cachedInfoRegs.m_maxImageLen ); if ( m_cachedInfoRegs.m_bootloaderDate && m_cachedInfoRegs.m_bootloaderTime ) { printf( "\tBootloader Date:\t%s, %s\n", makeDate( m_cachedInfoRegs.m_bootloaderDate ).c_str(), makeTime( m_cachedInfoRegs.m_bootloaderTime ).c_str() ); } if ( m_cachedInfoRegs.m_debuggerDate && m_cachedInfoRegs.m_debuggerTime ) { printf( "\tDebugger Date:\t\t%s, %s\n", makeDate( m_cachedInfoRegs.m_debuggerDate ).c_str(), makeTime( m_cachedInfoRegs.m_debuggerTime ).c_str() ); } printf( "\tDebugger Id:\t\t0x%08x\n", m_cachedInfoRegs.m_debuggerId ); printf( "\tDebugger Version:\t0x%08x\n", m_cachedInfoRegs.m_debuggerVersion ); } bool BeBoB::BootloaderManager::downloadFirmware( std::string filename ) { using namespace std; printf( "parse BCD file\n" ); ffado_smartptr bcd = ffado_smartptr( new BCD( filename ) ); if ( !bcd.get() ) { debugError( "downloadFirmware: Could not open or parse BCD '%s'\n", filename.c_str() ); return false; } if ( !bcd->parse() ) { debugError( "downloadFirmware: BCD parsing failed\n" ); return false; } printf( "check firmware device compatibility... " ); if ( !m_forceEnabled ) { if ( !checkDeviceCompatibility( *bcd ) ) { printf( "failed.\n" ); return false; } printf( "ok\n" ); } else { printf( "forced\n" ); } if ( m_bStartBootloader ) { printf( "prepare for download (start bootloader)\n" ); if ( !startBootloaderCmd() ) { debugError( "downloadFirmware: Could not start bootloader\n" ); return false; } } printf( "start downloading protocol for application image\n" ); if ( !downloadObject( *bcd, eOT_Application ) ) { debugError( "downloadFirmware: Firmware download failed\n" ); return false; } printf( "start downloading protocol for CnE\n" ); if ( !downloadObject( *bcd, eOT_CnE ) ) { debugError( "downloadFirmware: CnE download failed\n" ); return false; } printf( "setting CnE to factory default settings\n" ); if ( !initializeConfigToFactorySettingCmd() ) { debugError( "downloadFirmware: Could not reinitalize CnE\n" ); return false; } printf( "start application\n" ); if ( !startApplicationCmd() ) { debugError( "downloadFirmware: Could not restart application\n" ); return false; } return true; } bool BeBoB::BootloaderManager::downloadCnE( std::string filename ) { using namespace std; printf( "parse BCD file\n" ); ffado_smartptr bcd = ffado_smartptr( new BCD( filename ) ); if ( !bcd.get() ) { debugError( "downloadCnE: Could not open or parse BCD '%s'\n", filename.c_str() ); return false; } if ( !bcd->parse() ) { debugError( "downloadCnE: BCD parsing failed\n" ); return false; } printf( "check firmware device compatibility... " ); if ( !m_forceEnabled ) { if ( !checkDeviceCompatibility( *bcd ) ) { printf( "failed.\n" ); return false; } printf( "ok\n" ); } else { printf( "forced\n" ); } if ( m_bStartBootloader ) { printf( "prepare for download (start bootloader)\n" ); if ( !startBootloaderCmd() ) { debugError( "downloadCnE: Could not start bootloader\n" ); return false; } } printf( "start downloading protocol for CnE\n" ); if ( !downloadObject( *bcd, eOT_CnE ) ) { debugError( "downloadCnE: CnE download failed\n" ); return false; } printf( "setting CnE to factory default settings\n" ); if ( !initializeConfigToFactorySettingCmd() ) { debugError( "downloadFirmware: Could not reinitalize CnE\n" ); return false; } printf( "start application\n" ); if ( !startApplicationCmd() ) { debugError( "downloadCnE: Could not restart application\n" ); return false; } return true; } bool BeBoB::BootloaderManager::downloadObject( BCD& bcd, EObjectType eObject ) { using namespace std; CommandCodesDownloadStart::EObject eCCDSObject; fb_quadlet_t baseAddress; fb_quadlet_t imageLength; fb_quadlet_t crc; fb_quadlet_t offset; switch ( eObject ) { case eOT_Application: eCCDSObject = CommandCodesDownloadStart::eO_Application; baseAddress = bcd.getImageBaseAddress(); imageLength = bcd.getImageLength(); crc = bcd.getImageCRC(); offset = bcd.getImageOffset(); break; case eOT_CnE: eCCDSObject = CommandCodesDownloadStart::eO_Config; baseAddress = 0; imageLength = bcd.getCnELength(); crc = bcd.getCnECRC(); offset = bcd.getCnEOffset(); break; default: return false; } CommandCodesDownloadStart ccDStart ( m_protocolVersion, eCCDSObject ); ccDStart.setDate( bcd.getSoftwareDate() ); ccDStart.setTime( bcd.getSoftwareTime() ); ccDStart.setId( bcd.getSoftwareId() ); ccDStart.setVersion( bcd.getSoftwareVersion() ); ccDStart.setBaseAddress( baseAddress ); ccDStart.setLength( imageLength ); ccDStart.setCRC( crc ); if ( !writeRequest( ccDStart ) ) { debugError( "downloadObject: start command write request failed\n" ); return false; } // bootloader erases the flash, have to wait until is ready // to answer our next request printf( "wait until flash erasing has terminated\n" ); int cnt = 30; while(cnt--) { sleep( 1 ); printf("."); fflush(stdout); } printf("\n"); if ( !readResponse( ccDStart ) ) { debugError( "downloadObject: (start) command read request failed\n" ); return false; } if ( ccDStart.getMaxBlockSize() < 0 ) { debugError( "downloadObject: (start) command reported error %d\n", ccDStart.getMaxBlockSize() ); return false; } unsigned int maxBlockSize = m_configRom->getAsyMaxPayload(); unsigned int i = 0; fb_quadlet_t address = 0; bool result = true; int totalBytes = imageLength; int downloadedBytes = 0; while ( imageLength > 0 ) { unsigned int blockSize = imageLength > maxBlockSize ? maxBlockSize : imageLength; fb_byte_t block[blockSize]; if ( !bcd.read( offset, block, blockSize ) ) { result = false; break; } if ( !get1394Serivce()->write( 0xffc0 | getConfigRom()->getNodeId(), AddrRegReqBuf, ( blockSize + 3 ) / 4, reinterpret_cast( block ) ) ) { debugError( "downloadObject: Could not write to node %d\n", getConfigRom()->getNodeId() ); return false; } CommandCodesDownloadBlock ccBlock( m_protocolVersion ); ccBlock.setSeqNumber( i ); ccBlock.setAddress( baseAddress + address ); ccBlock.setNumberBytes( blockSize ); if ( !writeRequest( ccBlock ) ) { debugError( "downloadObject: (block) write request failed\n" ); result = false; break; } SleepRelativeUsec( 100 ); if ( !readResponse( ccBlock ) ) { debugError( "downloadObject: (block) read request failed\n" ); result = false; break; } if ( i != ccBlock.getRespSeqNumber() ) { debugError( "downloadObject: (block) wrong sequence number " "reported. %d expected, %d reported\n", i, ccBlock.getRespSeqNumber() ); result = false; break; } if ( ccBlock.getRespErrorCode() != 0 ) { debugError( "downloadObject: (block) failed download with " "error code 0x%08x\n", ccBlock.getRespErrorCode() ); result = false; break; } downloadedBytes += blockSize; if ( ( i % 100 ) == 0 ) { printf( "%10d/%d bytes downloaded\r", downloadedBytes, totalBytes ); fflush(stdout); } imageLength -= blockSize; address += blockSize; offset += blockSize; i++; } printf( "%10d/%d bytes downloaded\n", downloadedBytes, totalBytes ); if ( !result ) { debugError( "downloadObject: seqNumber = %d, " "address = 0%08x, offset = 0x%08x, " "restImageLength = 0x%08x\n", i, address, offset, imageLength ); } CommandCodesDownloadEnd ccEnd( m_protocolVersion ); if ( !writeRequest( ccEnd ) ) { debugError( "downloadObject: (end) command write failed\n" ); } printf( "wait for transaction completion\n" ); cnt = 10; while(cnt--) { sleep( 1 ); printf("."); fflush(stdout); } printf("\n"); if ( !readResponse( ccEnd ) ) { debugError( "downloadObject: (end) command read failed\n" ); } if ( result ) { if ( ccEnd.getRespIsValid() ) { if ( ccEnd.getRespCrc32() == crc ) { debugOutput( DebugModule::eDL_Normal, "downloadObject: CRC match\n" ); } else { debugError( "downloadObject: CRC mismatch. 0x%08x expected, " "0x%08x reported", crc, ccEnd.getRespCrc32() ); result = false; } } else { debugError( "downloadObject: (end) object is not valid\n" ); result = false; } } printf( "download protocol successfully completed\n" ); return result; } bool BeBoB::BootloaderManager::programGUID( fb_octlet_t guid ) { if ( m_bStartBootloader ) { if ( !startBootloaderCmd() ) { debugError( "programGUID: Could not start bootloader\n" ); return false; } } if ( !programGUIDCmd( guid ) ) { debugError( "programGUID: Could not program guid\n" ); return false; } if ( !startApplicationCmd() ) { debugError( "Could not restart application\n"); return false; } return true; } void BeBoB::BootloaderManager::busresetHandler() { pthread_cond_signal( &m_cond ); } void BeBoB::BootloaderManager::waitForBusReset() { struct timespec timeout; int retcode; // pthread_cond_timedwait() uses CLOCK_REALTIME to evaluate its // timeout argument. clock_gettime(CLOCK_REALTIME, &timeout); do { printf("."); fflush(stdout); timeout.tv_sec = timeout.tv_sec + 1; retcode = pthread_cond_timedwait( &m_cond, &m_mutex, &timeout ); } while (retcode == ETIMEDOUT); } bool BeBoB::BootloaderManager::writeRequest( CommandCodes& cmd ) { unsigned char buf[ ( ( cmd.getMaxSize()+3 )/4 ) * 4 ]; memset( buf, 0, sizeof( buf ) ); Util::Cmd::BufferSerialize se( buf, sizeof( buf ) ); if ( !cmd.serialize( se ) ) { debugError( "writeRequest: Could not serialize command code %d\n", cmd.getCommandCode() ); return false; } if ( !get1394Serivce()->write( 0xffc0 | getConfigRom()->getNodeId(), AddrRegReq, sizeof( buf )/4, reinterpret_cast( buf ) ) ) { debugError( "writeRequest: Could not ARM write to node %d\n", getConfigRom()->getNodeId() ); return false; } return true; } bool BeBoB::BootloaderManager::readResponse( CommandCodes& writeRequestCmd ) { const size_t buf_length = 0x40; unsigned char raw[buf_length]; if ( !get1394Serivce()->read( 0xffc0 | getConfigRom()->getNodeId(), AddrRegResp, writeRequestCmd.getRespSizeInQuadlets(), reinterpret_cast( raw ) ) ) { return false; } Util::Cmd::BufferDeserialize de( raw, buf_length ); if ( !writeRequestCmd.deserialize( de ) ) { debugError( "readResponse: deserialize failed\n" ); return false; } bool result = writeRequestCmd.getProtocolVersion() == writeRequestCmd.getRespProtocolVersion(); result &= writeRequestCmd.getCommandId() == writeRequestCmd.getRespCommandId(); result &= writeRequestCmd.getCommandCode() == writeRequestCmd.getRespCommandCode(); #ifdef DEBUG if ( !result ) { debugError( "readResponse: protocol version: %d expected, " " %d reported\n", writeRequestCmd.getProtocolVersion(), writeRequestCmd.getRespProtocolVersion() ); debugError( "readResponse: command id: %d expected, " " %d reported\n", writeRequestCmd.getCommandId(), writeRequestCmd.getRespCommandId() ); debugError( "readResponse: command code: %d expected, " " %d reported\n", writeRequestCmd.getCommandCode(), writeRequestCmd.getRespCommandCode() ); } #endif return result; } bool BeBoB::BootloaderManager::startBootloaderCmd() { CommandCodesReset cmd( m_protocolVersion, CommandCodesReset::eSM_Bootloader ) ; if ( !writeRequest( cmd ) ) { debugError( "startBootloaderCmd: writeRequest failed\n" ); return false; } waitForBusReset(); if ( !cacheInfoRegisters( MaxRetries ) ) { debugError( "startBootloaderCmd: Could not read info registers\n" ); return false; } // wait for bootloader finish startup sequence // there is no way to find out when it has finished sleep( 10 ); int cnt = 10; while(cnt--) { sleep( 1 ); printf("."); fflush(stdout); } printf("\n"); return true; } bool BeBoB::BootloaderManager::startApplicationCmd() { CommandCodesGo cmd( m_protocolVersion, CommandCodesGo::eSM_Application ) ; if ( !writeRequest( cmd ) ) { debugError( "startApplicationCmd: writeRequest failed\n" ); return false; } return true; } bool BeBoB::BootloaderManager::programGUIDCmd( fb_octlet_t guid ) { CommandCodesProgramGUID cmd( m_protocolVersion, guid ); if ( !writeRequest( cmd ) ) { debugError( "programGUIDCmd: writeRequest failed\n" ); return false; } sleep( 1 ); return true; } bool BeBoB::BootloaderManager::initializePersParamCmd() { CommandCodesInitializePersParam cmd( m_protocolVersion ); if ( !writeRequest( cmd ) ) { debugError( "initializePersParamCmd: writeRequest failed\n" ); return false; } sleep( 1 ); return true; } bool BeBoB::BootloaderManager::initializeConfigToFactorySettingCmd() { CommandCodesInitializeConfigToFactorySetting cmd( m_protocolVersion ); if ( !writeRequest( cmd ) ) { debugError( "initializeConfigToFactorySettingCmd: writeRequest failed\n" ); return false; } sleep( 5 ); int cnt = 5; while(cnt--) { sleep( 1 ); printf("."); fflush(stdout); } printf("\n"); return true; } bool BeBoB::BootloaderManager::checkDeviceCompatibility( BCD& bcd ) { fb_quadlet_t vendorOUI = ( m_cachedInfoRegs.m_guid >> 40 ); if ( ( vendorOUI == bcd.getVendorOUI() ) && ( m_cachedInfoRegs.m_softwareId == bcd.getSoftwareId() ) ) { return true; } printf( "vendorOUI = 0x%08x\n", vendorOUI ); printf( "BCD vendorOUI = 0x%08x\n", bcd.getVendorOUI() ); printf( "software ID = 0x%08x\n", m_cachedInfoRegs.m_softwareId ); printf( "BCD software ID = 0x%08x\n", bcd.getSoftwareId() ); return false; } libffado-2.4.5/src/bebob/bebob_dl_mgr.h0000644000175000001440000000766114206145246017304 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_DL_MGR_H #define BEBOB_DL_MGR_H #include "bebob_dl_codes.h" #include "fbtypes.h" #include "libutil/Functors.h" #include "debugmodule/debugmodule.h" #include class Ieee1394Service; class ConfigRom; using namespace Util; namespace BeBoB { class BCD; class BootloaderManager { public: BootloaderManager( Ieee1394Service& ieee1349service, fb_nodeid_t nodeId ); ~BootloaderManager(); const ConfigRom* const getConfigRom() const { return m_configRom; } void printInfoRegisters(); bool downloadFirmware( std::string filename ); bool downloadCnE( std::string filename ); bool programGUID( octlet_t guid ); void busresetHandler(); Ieee1394Service* get1394Serivce() const { return m_ieee1394service; } bool setForceOperations( bool enabled ) { m_forceEnabled = enabled; return true; } bool setStartBootloader( bool bStartBootloader ) { m_bStartBootloader = bStartBootloader; return true; } int getSoftwareVersion() {return m_cachedInfoRegs.m_softwareVersion;}; std::string getSoftwareDate(); std::string getSoftwareTime(); protected: enum EObjectType { eOT_Application, eOT_CnE }; void waitForBusReset(); bool writeRequest( CommandCodes& cmd ); bool readResponse( CommandCodes& writeRequestCmd ); bool downloadObject( BCD& bcd, EObjectType eObject ); bool programGUIDCmd( octlet_t guid ); bool startBootloaderCmd(); bool startApplicationCmd(); bool initializePersParamCmd(); bool initializeConfigToFactorySettingCmd(); bool checkDeviceCompatibility( BCD& bcd ); private: bool cacheInfoRegisters(); bool cacheInfoRegisters( int retries ); struct info_register_t { fb_octlet_t m_manId; fb_quadlet_t m_protocolVersion; fb_quadlet_t m_bootloaderVersion; fb_octlet_t m_guid; fb_quadlet_t m_hardwareModelId; fb_quadlet_t m_hardwareRevision; fb_octlet_t m_softwareDate; fb_octlet_t m_softwareTime; fb_quadlet_t m_softwareId; fb_quadlet_t m_softwareVersion; fb_quadlet_t m_baseAddress; fb_quadlet_t m_maxImageLen; fb_octlet_t m_bootloaderDate; fb_octlet_t m_bootloaderTime; fb_octlet_t m_debuggerDate; fb_octlet_t m_debuggerTime; fb_quadlet_t m_debuggerId; fb_quadlet_t m_debuggerVersion; }; Ieee1394Service* m_ieee1394service; ConfigRom* m_configRom; EBootloaderProtocolVersion m_protocolVersion; bool m_isAppRunning; info_register_t m_cachedInfoRegs; pthread_mutex_t m_mutex; pthread_cond_t m_cond; Functor* m_functor; bool m_forceEnabled; bool m_bStartBootloader; DECLARE_DEBUG_MODULE; }; } #endif libffado-2.4.5/src/bebob/bebob_functionblock.cpp0000644000175000001440000003504514206145246021230 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "bebob/bebob_functionblock.h" #include "bebob/bebob_avdevice_subunit.h" #include "bebob/bebob_avdevice.h" #include "libieee1394/configrom.h" #include "libutil/cmd_serialize.h" using namespace AVC; namespace BeBoB { IMPL_DEBUG_MODULE( FunctionBlock, FunctionBlock, DEBUG_LEVEL_NORMAL ); FunctionBlock::FunctionBlock( AVC::Subunit& subunit, function_block_type_t type, function_block_type_t subtype, function_block_id_t id, ESpecialPurpose purpose, no_of_input_plugs_t nrOfInputPlugs, no_of_output_plugs_t nrOfOutputPlugs, int verbose ) : m_subunit( &subunit ) , m_type( type ) , m_subtype( subtype ) , m_id( id ) , m_purpose( purpose ) , m_nrOfInputPlugs( nrOfInputPlugs ) , m_nrOfOutputPlugs( nrOfOutputPlugs ) , m_verbose( verbose ) { setDebugLevel( verbose ); } FunctionBlock::FunctionBlock( const FunctionBlock& rhs ) : m_subunit( rhs.m_subunit ) , m_type( rhs.m_type ) , m_subtype( rhs.m_subtype ) , m_id( rhs.m_id ) , m_purpose( rhs.m_purpose ) , m_nrOfInputPlugs( rhs.m_nrOfInputPlugs ) , m_nrOfOutputPlugs( rhs.m_nrOfOutputPlugs ) , m_verbose( rhs.m_verbose ) { } FunctionBlock::FunctionBlock() { } FunctionBlock::~FunctionBlock() { for ( PlugVector::iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { delete *it; } } bool FunctionBlock::discover() { debugOutput( DEBUG_LEVEL_NORMAL, "discover function block %s (nr of input plugs = %d, " "nr of output plugs = %d)\n", getName(), m_nrOfInputPlugs, m_nrOfOutputPlugs ); if ( !discoverPlugs( AVC::Plug::eAPD_Input, m_nrOfInputPlugs ) ) { debugError( "Could not discover input plug for '%s'\n", getName() ); return false; } if ( !discoverPlugs( AVC::Plug::eAPD_Output, m_nrOfOutputPlugs ) ) { debugError( "Could not discover output plugs for '%s'\n", getName() ); return false; } return true; } bool FunctionBlock::discoverPlugs( AVC::Plug::EPlugDirection plugDirection, plug_id_t plugMaxId ) { for ( int plugId = 0; plugId < plugMaxId; ++plugId ) { AVC::Plug* plug = new BeBoB::Plug( &m_subunit->getUnit(), m_subunit, m_type, m_id, AVC::Plug::eAPA_FunctionBlockPlug, plugDirection, plugId); if ( !plug || !plug->discover() ) { debugError( "plug discovering failed for plug %d\n", plugId ); delete plug; return false; } debugOutput( DEBUG_LEVEL_NORMAL, "plug '%s' found\n", plug->getName() ); m_plugs.push_back( plug ); } return true; } bool FunctionBlock::discoverConnections() { debugOutput( DEBUG_LEVEL_VERBOSE, "discover connections function block %s\n", getName() ); for ( PlugVector::iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { BeBoB::Plug* plug = dynamic_cast(*it); if(!plug) { debugError("BUG: not a bebob plug\n"); return false; } if ( !plug->discoverConnections() ) { debugError( "Could not discover plug connections\n" ); return false; } } return true; } bool FunctionBlock::serialize( std::string basePath, Util::IOSerialize& ser ) const { bool result; result = ser.write( basePath + "m_type", m_type ); result &= ser.write( basePath + "m_subtype", m_subtype ); result &= ser.write( basePath + "m_id", m_id ); result &= ser.write( basePath + "m_purpose", m_purpose ); result &= ser.write( basePath + "m_nrOfInputPlugs", m_nrOfInputPlugs ); result &= ser.write( basePath + "m_nrOfOutputPlugs", m_nrOfOutputPlugs ); result &= serializePlugVector( basePath + "m_plugs", ser, m_plugs ); return result; } FunctionBlock* FunctionBlock::deserialize( std::string basePath, Util::IODeserialize& deser, AVC::Unit& unit, AVC::Subunit& subunit ) { bool result; function_block_type_t type; function_block_type_t subtype; FunctionBlock* pFB = 0; if ( !deser.isExisting( basePath + "m_type" ) ) { return 0; } result = deser.read( basePath + "m_type", type ); result &= deser.read( basePath + "m_subtype", subtype ); if ( !result ) { return 0; } switch ( type ) { case ExtendedSubunitInfoCmd::eFBT_AudioSubunitSelector: pFB = new FunctionBlockSelector; break; case ExtendedSubunitInfoCmd::eFBT_AudioSubunitFeature: pFB = new FunctionBlockFeature; break; case ExtendedSubunitInfoCmd::eFBT_AudioSubunitProcessing: if ( subtype == ExtendedSubunitInfoCmd::ePT_EnhancedMixer ) { pFB = new FunctionBlockEnhancedMixer; } else { pFB = new FunctionBlockProcessing; } break; case ExtendedSubunitInfoCmd::eFBT_AudioSubunitCodec: pFB = new FunctionBlockCodec; break; default: pFB = 0; } if ( !pFB ) { return 0; } pFB->m_subunit = &subunit; pFB->m_type = type; pFB->m_subtype = subtype; result &= deser.read( basePath + "m_id", pFB->m_id ); result &= deser.read( basePath + "m_purpose", pFB->m_purpose ); result &= deser.read( basePath + "m_nrOfInputPlugs", pFB->m_nrOfInputPlugs ); result &= deser.read( basePath + "m_nrOfOutputPlugs", pFB->m_nrOfOutputPlugs ); if ( !result ) { delete pFB; return 0; } return pFB; } bool FunctionBlock::deserializeUpdate( std::string basePath, Util::IODeserialize& deser ) { bool result; result = deserializePlugVector( basePath + "m_plugs", deser, m_subunit->getUnit().getPlugManager(), m_plugs ); return result; } /////////////////////// FunctionBlockSelector::FunctionBlockSelector( AVC::Subunit& subunit, function_block_id_t id, ESpecialPurpose purpose, no_of_input_plugs_t nrOfInputPlugs, no_of_output_plugs_t nrOfOutputPlugs, int verbose ) : FunctionBlock( subunit, eFBT_AudioSubunitSelector, 0, id, purpose, nrOfInputPlugs, nrOfOutputPlugs, verbose ) { } FunctionBlockSelector::FunctionBlockSelector( const FunctionBlockSelector& rhs ) : FunctionBlock( rhs ) { } FunctionBlockSelector::FunctionBlockSelector() : FunctionBlock() { } FunctionBlockSelector::~FunctionBlockSelector() { } const char* FunctionBlockSelector::getName() { return "Selector"; } bool FunctionBlockSelector::serializeChild( std::string basePath, Util::IOSerialize& ser ) const { return true; } bool FunctionBlockSelector::deserializeChild( std::string basePath, Util::IODeserialize& deser, AvDevice& unit ) { return true; } /////////////////////// FunctionBlockFeature::FunctionBlockFeature( AVC::Subunit& subunit, function_block_id_t id, ESpecialPurpose purpose, no_of_input_plugs_t nrOfInputPlugs, no_of_output_plugs_t nrOfOutputPlugs, int verbose ) : FunctionBlock( subunit, eFBT_AudioSubunitFeature, 0, id, purpose, nrOfInputPlugs, nrOfOutputPlugs, verbose ) { } FunctionBlockFeature::FunctionBlockFeature( const FunctionBlockFeature& rhs ) : FunctionBlock( rhs ) { } FunctionBlockFeature::FunctionBlockFeature() : FunctionBlock() { } FunctionBlockFeature::~FunctionBlockFeature() { } const char* FunctionBlockFeature::getName() { return "Feature"; } bool FunctionBlockFeature::serializeChild( std::string basePath, Util::IOSerialize& ser ) const { return true; } bool FunctionBlockFeature::deserializeChild( std::string basePath, Util::IODeserialize& deser, AvDevice& unit ) { return true; } /////////////////////// FunctionBlockEnhancedMixer::FunctionBlockEnhancedMixer( AVC::Subunit& subunit, function_block_id_t id, ESpecialPurpose purpose, no_of_input_plugs_t nrOfInputPlugs, no_of_output_plugs_t nrOfOutputPlugs, int verbose ) : FunctionBlock( subunit, eFBT_AudioSubunitProcessing, ExtendedSubunitInfoCmd::ePT_EnhancedMixer, id, purpose, nrOfInputPlugs, nrOfOutputPlugs, verbose ) { } FunctionBlockEnhancedMixer::FunctionBlockEnhancedMixer( const FunctionBlockEnhancedMixer& rhs ) : FunctionBlock( rhs ) { } FunctionBlockEnhancedMixer::FunctionBlockEnhancedMixer() : FunctionBlock() { } FunctionBlockEnhancedMixer::~FunctionBlockEnhancedMixer() { } bool FunctionBlockEnhancedMixer::discover() { if (!FunctionBlock::discover()) return false; /* * Disable discovering of enhanced mixer because all * device out there do not use, and all implementations * are buggy. So there is no point to use it. * All 'mixer' functions are implemented with selector function blocks */ AVC::FunctionBlockCmd fbCmd( m_subunit->getUnit().get1394Service(), FunctionBlockCmd::eFBT_Processing, m_id, FunctionBlockCmd::eCA_Current); fbCmd.setNodeId( m_subunit->getUnit().getConfigRom().getNodeId() ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Status ); // fbCmd.setVerboseLevel( DEBUG_LEVEL_VERY_VERBOSE ); // Ok, this enhanced mixer setting here is just a hack, we need // a sane way to set processing features (read pointer management) AVC::FunctionBlockProcessingEnhancedMixer em; delete fbCmd.m_pFBProcessing->m_pMixer; fbCmd.m_pFBProcessing->m_pMixer = 0; fbCmd.m_pFBProcessing->m_pEnhancedMixer = em.clone(); fbCmd.m_pFBProcessing->m_inputAudioChannelNumber = 0xff; fbCmd.m_pFBProcessing->m_outputAudioChannelNumber = 0xff; if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); return false; } // if ( getDebugLevel() >= DEBUG_LEVEL_NORMAL ) { // Util::Cmd::CoutSerializer se; // fbCmd.serialize( se ); // } if((fbCmd.getResponse() != AVCCommand::eR_Implemented)) { debugWarning("fbCmd.getResponse() != AVCCommand::eR_Implemented\n"); } return true; } const char* FunctionBlockEnhancedMixer::getName() { return "EnhancedMixer"; } bool FunctionBlockEnhancedMixer::serializeChild( std::string basePath, Util::IOSerialize& ser ) const { return true; } bool FunctionBlockEnhancedMixer::deserializeChild( std::string basePath, Util::IODeserialize& deser, AvDevice& unit ) { return true; } /////////////////////// FunctionBlockProcessing::FunctionBlockProcessing( AVC::Subunit& subunit, function_block_id_t id, ESpecialPurpose purpose, no_of_input_plugs_t nrOfInputPlugs, no_of_output_plugs_t nrOfOutputPlugs, int verbose ) : FunctionBlock( subunit, eFBT_AudioSubunitProcessing, 0, id, purpose, nrOfInputPlugs, nrOfOutputPlugs, verbose ) { } FunctionBlockProcessing::FunctionBlockProcessing( const FunctionBlockProcessing& rhs ) : FunctionBlock( rhs ) { } FunctionBlockProcessing::FunctionBlockProcessing() : FunctionBlock() { } FunctionBlockProcessing::~FunctionBlockProcessing() { } const char* FunctionBlockProcessing::getName() { return "Dummy Processing"; } bool FunctionBlockProcessing::serializeChild( std::string basePath, Util::IOSerialize& ser ) const { return true; } bool FunctionBlockProcessing::deserializeChild( std::string basePath, Util::IODeserialize& deser, AvDevice& unit ) { return true; } /////////////////////// FunctionBlockCodec::FunctionBlockCodec( AVC::Subunit& subunit, function_block_id_t id, ESpecialPurpose purpose, no_of_input_plugs_t nrOfInputPlugs, no_of_output_plugs_t nrOfOutputPlugs, int verbose ) : FunctionBlock( subunit, eFBT_AudioSubunitCodec, 0, id, purpose, nrOfInputPlugs, nrOfOutputPlugs, verbose ) { } FunctionBlockCodec::FunctionBlockCodec( const FunctionBlockCodec& rhs ) : FunctionBlock( rhs ) { } FunctionBlockCodec::FunctionBlockCodec() : FunctionBlock() { } FunctionBlockCodec::~FunctionBlockCodec() { } const char* FunctionBlockCodec::getName() { return "Dummy Codec"; } bool FunctionBlockCodec::serializeChild( std::string basePath, Util::IOSerialize& ser ) const { return true; } bool FunctionBlockCodec::deserializeChild( std::string basePath, Util::IODeserialize& deser, AvDevice& unit ) { return true; } } libffado-2.4.5/src/bebob/bebob_functionblock.h0000644000175000001440000002116214206145246020670 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_FUNCTION_BLOCK_H #define BEBOB_FUNCTION_BLOCK_H #include "bebob/bebob_avplug.h" #include "libavc/avc_definitions.h" #include "debugmodule/debugmodule.h" #include namespace AVC { class Subunit; } namespace BeBoB { class FunctionBlock { public: enum EFunctionBlockType { eFBT_AllFunctionBlockType = 0xff, eFBT_AudioSubunitSelector = 0x80, eFBT_AudioSubunitFeature = 0x81, eFBT_AudioSubunitProcessing = 0x82, eFBT_AudioSubunitCodec = 0x83, }; enum ESpecialPurpose { eSP_InputGain, eSP_OutputVolume, eSP_NoSpecialPurpose }; FunctionBlock( AVC::Subunit& subunit, AVC::function_block_type_t type, AVC::function_block_type_t subtype, AVC::function_block_id_t id, ESpecialPurpose purpose, AVC::no_of_input_plugs_t nrOfInputPlugs, AVC::no_of_output_plugs_t nrOfOutputPlugs, int verbose ); FunctionBlock( const FunctionBlock& rhs ); FunctionBlock(); virtual ~FunctionBlock(); virtual bool discover(); virtual bool discoverConnections(); virtual const char* getName() = 0; AVC::function_block_type_t getType() {return m_type;}; AVC::function_block_type_t getSubtype() {return m_subtype;}; AVC::function_block_id_t getId() {return m_id;}; AVC::no_of_input_plugs_t getNrOfInputPlugs() {return m_nrOfInputPlugs;}; AVC::no_of_output_plugs_t getNrOfOutputPlugs() {return m_nrOfOutputPlugs;}; bool serialize( std::string basePath, Util::IOSerialize& ser ) const; static FunctionBlock* deserialize( std::string basePath, Util::IODeserialize& deser, AVC::Unit& unit, AVC::Subunit& subunit ); bool deserializeUpdate( std::string basePath, Util::IODeserialize& deser ); protected: bool discoverPlugs( AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugMaxId ); protected: AVC::Subunit* m_subunit; AVC::function_block_type_t m_type; AVC::function_block_type_t m_subtype; AVC::function_block_id_t m_id; ESpecialPurpose m_purpose; AVC::no_of_input_plugs_t m_nrOfInputPlugs; AVC::no_of_output_plugs_t m_nrOfOutputPlugs; int m_verbose; AVC::PlugVector m_plugs; DECLARE_DEBUG_MODULE; }; typedef std::vector FunctionBlockVector; ///////////////////////////////////// ///////////////////////////////////// class FunctionBlockSelector: public FunctionBlock { public: FunctionBlockSelector(AVC::Subunit& subunit, AVC::function_block_id_t id, ESpecialPurpose purpose, AVC::no_of_input_plugs_t nrOfInputPlugs, AVC::no_of_output_plugs_t nrOfOutputPlugs, int verbose); FunctionBlockSelector( const FunctionBlockSelector& rhs ); FunctionBlockSelector(); virtual ~FunctionBlockSelector(); virtual const char* getName(); protected: virtual bool serializeChild( std::string basePath, Util::IOSerialize& ser ) const; virtual bool deserializeChild( std::string basePath, Util::IODeserialize& deser, AvDevice& avDevice ); }; ///////////////////////////////////// class FunctionBlockFeature: public FunctionBlock { public: FunctionBlockFeature(AVC::Subunit& subunit, AVC::function_block_id_t id, ESpecialPurpose purpose, AVC::no_of_input_plugs_t nrOfInputPlugs, AVC::no_of_output_plugs_t nrOfOutputPlugs, int verbose); FunctionBlockFeature( const FunctionBlockFeature& rhs ); FunctionBlockFeature(); virtual ~FunctionBlockFeature(); virtual const char* getName(); // FIXME: this is not pretty! enum EControlSelectorEncoding { eCSE_Feature_Unknown = 0x00, eCSE_Feature_Mute = 0x01, eCSE_Feature_Volume = 0x02, eCSE_Feature_LRBalance = 0x03, eCSE_Feature_FRBalance = 0x04, eCSE_Feature_Bass = 0x05, eCSE_Feature_Mid = 0x06, eCSE_Feature_Treble = 0x07, eCSE_Feature_GEQ = 0x08, eCSE_Feature_AGC = 0x09, eCSE_Feature_Delay = 0x0a, eCSE_Feature_BassBoost = 0x0b, eCSE_Feature_Loudness = 0x0c, }; protected: virtual bool serializeChild( std::string basePath, Util::IOSerialize& ser ) const; virtual bool deserializeChild( std::string basePath, Util::IODeserialize& deser, AvDevice& avDevice ); }; ///////////////////////////////////// class FunctionBlockEnhancedMixer: public FunctionBlock { public: FunctionBlockEnhancedMixer( AVC::Subunit& subunit, AVC::function_block_id_t id, ESpecialPurpose purpose, AVC::no_of_input_plugs_t nrOfInputPlugs, AVC::no_of_output_plugs_t nrOfOutputPlugs, int verbose ); FunctionBlockEnhancedMixer(); FunctionBlockEnhancedMixer( const FunctionBlockEnhancedMixer& rhs ); virtual ~FunctionBlockEnhancedMixer(); virtual bool discover(); virtual const char* getName(); protected: virtual bool serializeChild( std::string basePath, Util::IOSerialize& ser ) const; virtual bool deserializeChild( std::string basePath, Util::IODeserialize& deser, AvDevice& avDevice ); }; ///////////////////////////////////// class FunctionBlockProcessing: public FunctionBlock { public: FunctionBlockProcessing( AVC::Subunit& subunit, AVC::function_block_id_t id, ESpecialPurpose purpose, AVC::no_of_input_plugs_t nrOfInputPlugs, AVC::no_of_output_plugs_t nrOfOutputPlugs, int verbose ); FunctionBlockProcessing( const FunctionBlockProcessing& rhs ); FunctionBlockProcessing(); virtual ~FunctionBlockProcessing(); virtual const char* getName(); protected: virtual bool serializeChild( std::string basePath, Util::IOSerialize& ser ) const; virtual bool deserializeChild( std::string basePath, Util::IODeserialize& deser, AvDevice& avDevice ); }; ///////////////////////////////////// class FunctionBlockCodec: public FunctionBlock { public: FunctionBlockCodec(AVC::Subunit& subunit, AVC::function_block_id_t id, ESpecialPurpose purpose, AVC::no_of_input_plugs_t nrOfInputPlugs, AVC::no_of_output_plugs_t nrOfOutputPlugs, int verbose); FunctionBlockCodec( const FunctionBlockCodec& rhs ); FunctionBlockCodec(); virtual ~FunctionBlockCodec(); virtual const char* getName(); protected: virtual bool serializeChild( std::string basePath, Util::IOSerialize& ser ) const; virtual bool deserializeChild( std::string basePath, Util::IODeserialize& deser, AvDevice& avDevice ); }; } #endif libffado-2.4.5/src/bebob/bebob_mixer.cpp0000644000175000001440000003041014206145246017503 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "bebob/bebob_mixer.h" #include "bebob/bebob_avdevice.h" #include "bebob/bebob_avdevice_subunit.h" #include "libavc/audiosubunit/avc_function_block.h" #include "libutil/cmd_serialize.h" #include "libcontrol/BasicElements.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/configrom.h" #include #include #include #include using namespace AVC; namespace BeBoB { template bool from_string(T& t, const std::string& s, std::ios_base& (*f)(std::ios_base&)) { std::istringstream iss(s); return !(iss >> f >> t).fail(); } IMPL_DEBUG_MODULE( Mixer, Mixer, DEBUG_LEVEL_NORMAL ); Mixer::Mixer(Device &d) : Control::Container(&d) , m_device(d) { addElementForAllFunctionBlocks(); if (!d.addElement(this)) { debugWarning("Could not add myself to Control::Container\n"); } } Mixer::~Mixer() { debugOutput(DEBUG_LEVEL_VERBOSE,"Unregistering from Control::Container...\n"); if (!m_device.deleteElement(this)) { debugWarning("Could not delete myself from Control::Container\n"); } // delete all our elements since we should have created // them ourselves. for ( Control::ElementVectorIterator it = m_Children.begin(); it != m_Children.end(); ++it ) { debugOutput(DEBUG_LEVEL_VERBOSE,"deleting %s...\n", (*it)->getName().c_str()); delete *it; } } bool Mixer::deleteElement(Element *e) { if (Control::Container::deleteElement(e)) { delete e; return true; } else return false; } bool Mixer::clearElements() { // delete all our elements since we should have created // them ourselves. for ( Control::ElementVectorIterator it = m_Children.begin(); it != m_Children.end(); ++it ) { delete *it; } m_Children.clear(); return true; } template bool Mixer::addElementForFunctionBlock(FBType& b) { Control::Element *e = new MixerType(*this, b); if (!e) { debugError("Control element creation failed\n"); return false; } e->setVerboseLevel(getDebugLevel()); return Control::Container::addElement(e); } bool Mixer::addElementForAllFunctionBlocks() { debugOutput(DEBUG_LEVEL_VERBOSE,"Adding elements for functionblocks...\n"); bool retval = true; BeBoB::SubunitAudio *asu = dynamic_cast(m_device.getAudioSubunit(0)); if(asu == NULL) { debugWarning("No BeBoB audio subunit found\n"); return false; } FunctionBlockVector functions=asu->getFunctionBlocks(); for ( FunctionBlockVector::iterator it = functions.begin(); it != functions.end(); ++it ) { FunctionBlock *pfb = *it; FunctionBlockSelector *ps; FunctionBlockFeature *pf; FunctionBlockEnhancedMixer *pm; if ((ps = dynamic_cast(pfb))) { debugOutput( DEBUG_LEVEL_VERBOSE, "FB is a SelectorFunctionBlock\n"); retval = addElementForFunctionBlock(*ps); } else if ((pf = dynamic_cast(pfb))) { // We might should check if really both feature function // blocks exists and only then announce them. The FA-101, // FA-66 and the Ref BCO Audio5 do have both. debugOutput( DEBUG_LEVEL_VERBOSE, "FB is a FeatureFunctionBlock\n"); retval = addElementForFunctionBlock(*pf); retval &= addElementForFunctionBlock(*pf); } else if ((pm = dynamic_cast(pfb))) { // All BeBoB devices lock the mixer feature function // block. The AV/C model for this mixer is just to // complex and the BridgeCo guys decided to use the above // function feature blocks (level and balance) to achive // the same. debugOutput( DEBUG_LEVEL_VERBOSE, "FB is a FunctionBlockEnhancedMixer\n"); retval = addElementForFunctionBlock(*pm); } if (!retval) { std::ostringstream ostrm; ostrm << (*it)->getName() << " " << (int)((*it)->getId()); debugWarning("Failed to add element for function block %s\n", ostrm.str().c_str()); }; } return retval; } // --- element implementation classes MixerFBFeatureVolume::MixerFBFeatureVolume(Mixer& parent, FunctionBlockFeature& s) : Control::Continuous(&parent) , m_Parent(parent) , m_Slave(s) { std::ostringstream ostrm; ostrm << s.getName() << "_Volume_" << (int)(s.getId()); Control::Continuous::setName(ostrm.str()); ostrm.str(""); ostrm << "Label for " << s.getName() << "_Volume " << (int)(s.getId()); setLabel(ostrm.str()); ostrm.str(""); ostrm << "Description for " << s.getName() << "_Volume " << (int)(s.getId()); setDescription(ostrm.str()); } MixerFBFeatureVolume::~MixerFBFeatureVolume() { } bool MixerFBFeatureVolume::setValue(double v) { return setValue(0, v); } bool MixerFBFeatureVolume::setValue(int idx, double v) { int volume=(int)v; debugOutput(DEBUG_LEVEL_NORMAL,"Set feature volume %d to %d...\n", m_Slave.getId(), volume); return m_Parent.getParent().setFeatureFBVolumeCurrent(m_Slave.getId(), idx, volume); } double MixerFBFeatureVolume::getValue() { return getValue(0); } double MixerFBFeatureVolume::getValue(int idx) { debugOutput(DEBUG_LEVEL_NORMAL,"Get feature volume %d...\n", m_Slave.getId()); return m_Parent.getParent().getFeatureFBVolumeCurrent(m_Slave.getId(), idx); } double MixerFBFeatureVolume::getMinimum() { debugOutput(DEBUG_LEVEL_NORMAL,"Get feature minimum volume %d...\n", m_Slave.getId()); return m_Parent.getParent().getFeatureFBVolumeMinimum(m_Slave.getId(), 0); } double MixerFBFeatureVolume::getMaximum() { debugOutput(DEBUG_LEVEL_NORMAL,"Get feature maximum volume %d...\n", m_Slave.getId()); return m_Parent.getParent().getFeatureFBVolumeMaximum(m_Slave.getId(), 0); } // --- element implementation classes MixerFBFeatureLRBalance::MixerFBFeatureLRBalance(Mixer& parent, FunctionBlockFeature& s) : Control::Continuous(&parent) , m_Parent(parent) , m_Slave(s) { std::ostringstream ostrm; ostrm << s.getName() << "_LRBalance_" << (int)(s.getId()); Control::Continuous::setName(ostrm.str()); ostrm.str(""); ostrm << "Label for " << s.getName() << "_LRBalance " << (int)(s.getId()); setLabel(ostrm.str()); ostrm.str(""); ostrm << "Description for " << s.getName() << "_LRBalance " << (int)(s.getId()); setDescription(ostrm.str()); } MixerFBFeatureLRBalance::~MixerFBFeatureLRBalance() { } bool MixerFBFeatureLRBalance::setValue(double v) { return setValue(0, v); } bool MixerFBFeatureLRBalance::setValue(int idx, double v) { int volume=(int)v; debugOutput(DEBUG_LEVEL_NORMAL,"Set feature balance %d to %d...\n", m_Slave.getId(), volume); return m_Parent.getParent().setFeatureFBLRBalanceCurrent(m_Slave.getId(), idx, volume); } double MixerFBFeatureLRBalance::getValue() { return getValue(0); } double MixerFBFeatureLRBalance::getValue(int idx) { debugOutput(DEBUG_LEVEL_NORMAL,"Get feature balance %d...\n", m_Slave.getId()); return m_Parent.getParent().getFeatureFBLRBalanceCurrent(m_Slave.getId(), idx); } double MixerFBFeatureLRBalance::getMinimum() { debugOutput(DEBUG_LEVEL_NORMAL,"Get feature balance volume %d...\n", m_Slave.getId()); return m_Parent.getParent().getFeatureFBLRBalanceMinimum(m_Slave.getId(), 0); } double MixerFBFeatureLRBalance::getMaximum() { debugOutput(DEBUG_LEVEL_NORMAL,"Get feature maximum balance %d...\n", m_Slave.getId()); return m_Parent.getParent().getFeatureFBLRBalanceMaximum(m_Slave.getId(), 0); } // --- element implementation classes /* * This function is named with 'EnhancedMixer' but current implementation is * for 'Processing'. There is several reasons. * There is no way to distinguish 'EnhancedMixer' from 'Processing', the * former is an extension of 'Processing' in AV/C audio subunit command. * The limitation of parameter of Continuous dbus interface. The ffado * developer considered to change the type of interface but it's not * reasonable in a point of cost/effect. * As of Oct 2013, the FFADO developers confirm only M-Audio BeBoB devices uses * this command to control mixer. No other devices use it. M-Audio BeBoB * devices can be controlled by single type 'Processing' command even if * they can be controlled by both single and multi type. */ EnhancedMixerFBFeature::EnhancedMixerFBFeature(Mixer& parent, FunctionBlockEnhancedMixer& s) : Control::Continuous(&parent) , m_Parent(parent) , m_Slave(s) { std::ostringstream ostrm; ostrm << s.getName() << "_" << (int)(s.getId()); Control::Continuous::setName(ostrm.str()); ostrm.str(""); ostrm << "Label for " << s.getName() << " " << (int)(s.getId()); setLabel(ostrm.str()); ostrm.str(""); ostrm << "Description for " << s.getName() << " " << (int)(s.getId()); setDescription(ostrm.str()); } EnhancedMixerFBFeature::~EnhancedMixerFBFeature() { } bool EnhancedMixerFBFeature::setValue(int idx, double v) { int iPlugNum = (idx >> 8) & 0xF; int iAChNum = (idx >> 4) & 0xF; int oAChNum = (idx >> 0) & 0xF; debugOutput(DEBUG_LEVEL_NORMAL,"Set: FBID: 0x%02X, FBPN: 0x%02X, " "ICN: 0x%02X, OCN: 0x%02X, DATA: 0x%04X\n", m_Slave.getId(), iPlugNum, iAChNum, oAChNum, (int)v); return m_Parent.getParent().setProcessingFBMixerSingleCurrent(m_Slave.getId(), iPlugNum, iAChNum, oAChNum, (int)v); } double EnhancedMixerFBFeature::getValue(int idx) { int iPlugNum = (idx >> 8) & 0xF; int iAChNum = (idx >> 4) & 0xF; int oAChNum = (idx >> 0) & 0xF; debugOutput(DEBUG_LEVEL_NORMAL,"Set: FBID: 0x%02X, FBPN: 0x%02X, " "ICN: 0x%02X, OCN: 0x%02X\n", m_Slave.getId(), iPlugNum, iAChNum, oAChNum); return m_Parent.getParent().getProcessingFBMixerSingleCurrent(m_Slave.getId(), iPlugNum, iAChNum, oAChNum); } // --- element implementation classes MixerFBSelector::MixerFBSelector(Mixer& parent, FunctionBlockSelector& s) : Control::Discrete(&parent) , m_Parent(parent) , m_Slave(s) { std::ostringstream ostrm; ostrm << s.getName() << "_" << (int)(s.getId()); Control::Discrete::setName(ostrm.str()); ostrm.str(""); ostrm << "Label for " << s.getName() << " " << (int)(s.getId()); setLabel(ostrm.str()); ostrm.str(""); ostrm << "Description for " << s.getName() << " " << (int)(s.getId()); setDescription(ostrm.str()); } MixerFBSelector::~MixerFBSelector() { } bool MixerFBSelector::setValue(int v) { debugOutput(DEBUG_LEVEL_NORMAL,"Set selector %d to %d...\n", m_Slave.getId(), v); return m_Parent.getParent().setSelectorFBValue(m_Slave.getId(), v); } int MixerFBSelector::getValue() { debugOutput(DEBUG_LEVEL_NORMAL,"Get selector %d...\n", m_Slave.getId()); return m_Parent.getParent().getSelectorFBValue(m_Slave.getId()); } } // end of namespace BeBoB libffado-2.4.5/src/bebob/bebob_mixer.h0000644000175000001440000000766014206145246017163 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFAD0_BEBOB_MIXER__ #define __FFAD0_BEBOB_MIXER__ #include "src/debugmodule/debugmodule.h" #include "libcontrol/BasicElements.h" #include namespace BeBoB { class Device; class FunctionBlock; class FunctionBlockFeature; class FunctionBlockSelector; class FunctionBlockEnhancedMixer; class Mixer : public Control::Container { public: Mixer(Device &d); virtual ~Mixer(); virtual std::string getName() { return "Mixer"; }; virtual bool setName( std::string n ) { return false; }; template bool addElementForFunctionBlock(FBType& b); bool addElementForAllFunctionBlocks(); // manipulation of the contained elements bool addElement(Control::Element *) {debugWarning("not allowed"); return false;}; bool deleteElement(Control::Element *); bool clearElements(); Device& getParent() {return m_device;}; protected: Device& m_device; protected: DECLARE_DEBUG_MODULE; }; class MixerFBFeatureVolume : public Control::Continuous { public: MixerFBFeatureVolume(Mixer& parent, FunctionBlockFeature&); virtual ~MixerFBFeatureVolume(); virtual bool setValue(double v); virtual double getValue(); virtual bool setValue(int idx, double v); virtual double getValue(int idx); virtual double getMinimum(); virtual double getMaximum(); private: Mixer& m_Parent; FunctionBlockFeature& m_Slave; }; class MixerFBFeatureLRBalance : public Control::Continuous { public: MixerFBFeatureLRBalance(Mixer& parent, FunctionBlockFeature&); virtual ~MixerFBFeatureLRBalance(); virtual bool setValue(double v); virtual double getValue(); virtual bool setValue(int idx, double v); virtual double getValue(int idx); virtual double getMinimum(); virtual double getMaximum(); private: Mixer& m_Parent; FunctionBlockFeature& m_Slave; }; class EnhancedMixerFBFeature : public Control::Continuous { public: EnhancedMixerFBFeature(Mixer& parent, FunctionBlockEnhancedMixer&); virtual ~EnhancedMixerFBFeature(); virtual bool setValue(int idx, double v); virtual double getValue(int idx); virtual bool setValue(double v) {return setValue(1, v);}; virtual double getValue() {return getValue(1);}; virtual double getMinimum() {return -32768;}; virtual double getMaximum() {return 0;}; private: Mixer& m_Parent; FunctionBlockEnhancedMixer& m_Slave; }; class MixerFBSelector : public Control::Discrete { public: MixerFBSelector(Mixer& parent, FunctionBlockSelector&); virtual ~MixerFBSelector(); virtual bool setValue(int v); virtual int getValue(); virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 0;}; private: Mixer& m_Parent; FunctionBlockSelector& m_Slave; }; } // end of namespace BeBoB #endif /* __FFAD0_BEBOB_MIXER__ */ libffado-2.4.5/src/bebob/edirol/0000755000175000001440000000000014206145612015777 5ustar jwoitheuserslibffado-2.4.5/src/bebob/edirol/edirol_fa101.cpp0000644000175000001440000000421014206145246020651 0ustar jwoitheusers/* * Copyright (C) 2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "edirol_fa101.h" namespace BeBoB { namespace Edirol { EdirolFa101Device::EdirolFa101Device( DeviceManager& d, ffado_smartptr( configRom )) : BeBoB::Device( d , configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::Edirol::EdirolFa101Device (NodeID %d)\n", getConfigRom().getNodeId() ); m_fixed_clocksource.type = FFADODevice::eCT_Auto; m_fixed_clocksource.valid = true; m_fixed_clocksource.locked = true; m_fixed_clocksource.id = 0; m_fixed_clocksource.slipping = false; m_fixed_clocksource.description = "Device Controlled"; get1394Service().setFCPResponseFiltering(true); } EdirolFa101Device::~EdirolFa101Device() { } FFADODevice::ClockSource EdirolFa101Device::getActiveClockSource() { return m_fixed_clocksource; } bool EdirolFa101Device::setActiveClockSource(ClockSource s) { // can't change, hence only succeed when identical return s.id == m_fixed_clocksource.id; } FFADODevice::ClockSourceVector EdirolFa101Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; r.push_back(m_fixed_clocksource); return r; } void EdirolFa101Device::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a BeBoB::EdirolFa101::EdirolFa101Device\n"); BeBoB::Device::showDevice(); } } } libffado-2.4.5/src/bebob/edirol/edirol_fa101.h0000644000175000001440000000260314206145246020322 0ustar jwoitheusers/* * Copyright (C) 2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_EDIROL_FA101_H #define BEBOB_EDIROL_FA101_H #include "bebob/bebob_avdevice.h" namespace BeBoB { namespace Edirol { class EdirolFa101Device : public BeBoB::Device { public: EdirolFa101Device( DeviceManager& d, ffado_smartptr( configRom )); virtual ~EdirolFa101Device(); virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); virtual void showDevice(); private: ClockSource m_fixed_clocksource; }; } } #endif libffado-2.4.5/src/bebob/edirol/edirol_fa66.cpp0000644000175000001440000000410714206145246020610 0ustar jwoitheusers/* * Copyright (C) 2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "edirol_fa66.h" namespace BeBoB { namespace Edirol { EdirolFa66Device::EdirolFa66Device( DeviceManager& d, ffado_smartptr( configRom )) : BeBoB::Device( d , configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::Edirol::EdirolFa66Device (NodeID %d)\n", getConfigRom().getNodeId() ); m_fixed_clocksource.type = FFADODevice::eCT_Auto; m_fixed_clocksource.valid = true; m_fixed_clocksource.locked = true; m_fixed_clocksource.id = 0; m_fixed_clocksource.slipping = false; m_fixed_clocksource.description = "Device Controlled"; } EdirolFa66Device::~EdirolFa66Device() { } FFADODevice::ClockSource EdirolFa66Device::getActiveClockSource() { return m_fixed_clocksource; } bool EdirolFa66Device::setActiveClockSource(ClockSource s) { // can't change, hence only succeed when identical return s.id == m_fixed_clocksource.id; } FFADODevice::ClockSourceVector EdirolFa66Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; r.push_back(m_fixed_clocksource); return r; } void EdirolFa66Device::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a BeBoB::EdirolFa66::EdirolFa66Device\n"); BeBoB::Device::showDevice(); } } } libffado-2.4.5/src/bebob/edirol/edirol_fa66.h0000644000175000001440000000257614206145246020265 0ustar jwoitheusers/* * Copyright (C) 2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_EDIROL_FA66_H #define BEBOB_EDIROL_FA66_H #include "bebob/bebob_avdevice.h" namespace BeBoB { namespace Edirol { class EdirolFa66Device : public BeBoB::Device { public: EdirolFa66Device( DeviceManager& d, ffado_smartptr( configRom )); virtual ~EdirolFa66Device(); virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); virtual void showDevice(); private: ClockSource m_fixed_clocksource; }; } } #endif libffado-2.4.5/src/bebob/esi/0000755000175000001440000000000014206145612015301 5ustar jwoitheuserslibffado-2.4.5/src/bebob/esi/quatafire610.cpp0000644000175000001440000000407714206145246020230 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "quatafire610.h" #include "debugmodule/debugmodule.h" namespace BeBoB { namespace ESI { QuataFireDevice::QuataFireDevice( DeviceManager& d, ffado_smartptr( configRom )) : BeBoB::Device( d, configRom) { m_fixed_clocksource.type = FFADODevice::eCT_Auto; m_fixed_clocksource.valid = true; m_fixed_clocksource.locked = true; m_fixed_clocksource.id = 0; m_fixed_clocksource.slipping = false; m_fixed_clocksource.description = "Autoselect"; debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::ESI::QuataFireDevice (NodeID %d)\n", getConfigRom().getNodeId() ); } QuataFireDevice::~QuataFireDevice() { } void QuataFireDevice::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a BeBoB::ESI::QuataFireDevice\n"); BeBoB::Device::showDevice(); } FFADODevice::ClockSource QuataFireDevice::getActiveClockSource() { return m_fixed_clocksource; } bool QuataFireDevice::setActiveClockSource(ClockSource s) { // can't change, hence only succeed when identical return s.id == m_fixed_clocksource.id; } FFADODevice::ClockSourceVector QuataFireDevice::getSupportedClockSources() { FFADODevice::ClockSourceVector r; r.push_back(m_fixed_clocksource); return r; } } // ESI } // BeBoB libffado-2.4.5/src/bebob/esi/quatafire610.h0000644000175000001440000000310614206145246017665 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_ESI_QUATAFIRE_DEVICE_H #define BEBOB_ESI_QUATAFIRE_DEVICE_H #include "bebob/bebob_avdevice.h" namespace BeBoB { namespace ESI { class QuataFireDevice : public BeBoB::Device { public: QuataFireDevice( DeviceManager& d, ffado_smartptr( configRom )); virtual ~QuataFireDevice(); // override these since the quatafire does not support // setting the clock source (it automatically syncs to SPDIF) virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); virtual void showDevice(); private: ClockSource m_fixed_clocksource; }; } // namespace BeBoB } // namespace ESI #endif libffado-2.4.5/src/bebob/focusrite/0000755000175000001440000000000014206145612016524 5ustar jwoitheuserslibffado-2.4.5/src/bebob/focusrite/focusrite_cmd.cpp0000644000175000001440000000435314206145246022066 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "focusrite_cmd.h" #include "libutil/ByteSwap.h" #include using namespace std; using namespace AVC; namespace BeBoB { namespace Focusrite { FocusriteVendorDependentCmd::FocusriteVendorDependentCmd(Ieee1394Service& ieee1394service) : VendorDependentCmd( ieee1394service ) , m_arg1 ( 0x03 ) , m_arg2 ( 0x01 ) , m_id ( 0x00000000 ) , m_value ( 0x00000000 ) { m_companyId=0x00130e; } FocusriteVendorDependentCmd::~FocusriteVendorDependentCmd() { } bool FocusriteVendorDependentCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= VendorDependentCmd::serialize( se ); result &= se.write(m_arg1,"FocusriteVendorDependentCmd arg1"); result &= se.write(m_arg2,"FocusriteVendorDependentCmd arg2"); // FIXME: this is not consistent, we should not have to care about CondSwapFromBus32 here result &= se.write(CondSwapToBus32(m_id),"FocusriteVendorDependentCmd ID"); result &= se.write(CondSwapToBus32(m_value),"FocusriteVendorDependentCmd value"); return result; } bool FocusriteVendorDependentCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= VendorDependentCmd::deserialize( de ); result &= de.read(&m_arg1); result &= de.read(&m_arg2); result &= de.read(&m_id); m_id=CondSwapFromBus32(m_id); result &= de.read(&m_value); m_value=CondSwapFromBus32(m_value); return result; } } } libffado-2.4.5/src/bebob/focusrite/focusrite_cmd.h0000644000175000001440000000312114206145246021523 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FOCUSRITEVENDORDEPENDENT_H #define FOCUSRITEVENDORDEPENDENT_H #include "libavc/general/avc_generic.h" #include "libutil/cmd_serialize.h" #include "libavc/general/avc_vendor_dependent_cmd.h" namespace BeBoB { namespace Focusrite { class FocusriteVendorDependentCmd: public AVC::VendorDependentCmd { public: FocusriteVendorDependentCmd(Ieee1394Service& ieee1394service); virtual ~FocusriteVendorDependentCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "FocusriteVendorDependentCmd"; } byte_t m_arg1; byte_t m_arg2; quadlet_t m_id; quadlet_t m_value; }; } } #endif // FOCUSRITEVENDORDEPENDENT_H libffado-2.4.5/src/bebob/focusrite/focusrite_generic.cpp0000644000175000001440000004140714206145246022740 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "focusrite_saffirepro.h" #include "focusrite_cmd.h" #include "libutil/ByteSwap.h" namespace BeBoB { namespace Focusrite { FocusriteDevice::FocusriteDevice( DeviceManager& d, ffado_smartptr( configRom )) : BeBoB::Device( d, configRom) , m_cmd_time_interval( 0 ) , m_earliest_next_cmd_time( 0 ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::Focusrite::FocusriteDevice (NodeID %d)\n", getConfigRom().getNodeId() ); addOption(Util::OptionContainer::Option("useAvcForParameters", false)); } void FocusriteDevice::showDevice() { debugOutput(DEBUG_LEVEL_NORMAL, "This is a BeBoB::Focusrite::FocusriteDevice\n"); BeBoB::Device::showDevice(); } void FocusriteDevice::setVerboseLevel(int l) { debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); BeBoB::Device::setVerboseLevel(l); } bool FocusriteDevice::setSpecificValue(uint32_t id, uint32_t v) { debugOutput(DEBUG_LEVEL_VERBOSE, "Writing parameter address space id 0x%08X (%u), data: 0x%08X\n", id, id, v); bool use_avc = false; if(!getOption("useAvcForParameters", use_avc)) { debugWarning("Could not retrieve useAvcForParameters parameter, defaulting to false\n"); } // rate control ffado_microsecs_t now = Util::SystemTimeSource::getCurrentTimeAsUsecs(); if(m_cmd_time_interval && (m_earliest_next_cmd_time > now)) { ffado_microsecs_t wait = m_earliest_next_cmd_time - now; debugOutput( DEBUG_LEVEL_VERBOSE, "Rate control... %" PRIu64 "\n", wait ); Util::SystemTimeSource::SleepUsecRelative(wait); } m_earliest_next_cmd_time = now + m_cmd_time_interval; if (use_avc) { return setSpecificValueAvc(id, v); } else { return setSpecificValueARM(id, v); } } bool FocusriteDevice::getSpecificValue(uint32_t id, uint32_t *v) { bool retval; bool use_avc = false; if(!getOption("useAvcForParameters", use_avc)) { debugWarning("Could not retrieve useAvcForParameters parameter, defaulting to false\n"); } // rate control ffado_microsecs_t now = Util::SystemTimeSource::getCurrentTimeAsUsecs(); if(m_cmd_time_interval && (m_earliest_next_cmd_time > now)) { ffado_microsecs_t wait = m_earliest_next_cmd_time - now; debugOutput( DEBUG_LEVEL_VERBOSE, "Rate control... %" PRIu64 "\n", wait ); Util::SystemTimeSource::SleepUsecRelative(wait); } m_earliest_next_cmd_time = now + m_cmd_time_interval; // execute if (use_avc) { retval = getSpecificValueAvc(id, v); } else { retval = getSpecificValueARM(id, v); } debugOutput(DEBUG_LEVEL_VERBOSE,"Read parameter address space id 0x%08X (%u): %08X\n", id, id, *v); return retval; } // The AV/C methods to set parameters bool FocusriteDevice::setSpecificValueAvc(uint32_t id, uint32_t v) { FocusriteVendorDependentCmd cmd( get1394Service() ); cmd.setCommandType( AVC::AVCCommand::eCT_Control ); cmd.setNodeId( getConfigRom().getNodeId() ); cmd.setSubunitType( AVC::eST_Unit ); cmd.setSubunitId( 0xff ); cmd.setVerbose( getDebugLevel() ); // cmd.setVerbose( DEBUG_LEVEL_VERY_VERBOSE ); cmd.m_id=id; cmd.m_value=v; if ( !cmd.fire() ) { debugError( "FocusriteVendorDependentCmd info command failed\n" ); return false; } return true; } bool FocusriteDevice::getSpecificValueAvc(uint32_t id, uint32_t *v) { FocusriteVendorDependentCmd cmd( get1394Service() ); cmd.setCommandType( AVC::AVCCommand::eCT_Status ); cmd.setNodeId( getConfigRom().getNodeId() ); cmd.setSubunitType( AVC::eST_Unit ); cmd.setSubunitId( 0xff ); cmd.setVerbose( getDebugLevel() ); // cmd.setVerbose( DEBUG_LEVEL_VERY_VERBOSE ); cmd.m_id=id; if ( !cmd.fire() ) { debugError( "FocusriteVendorDependentCmd info command failed\n" ); return false; } *v=cmd.m_value; return true; } // The ARM methods to set parameters bool FocusriteDevice::setSpecificValueARM(uint32_t id, uint32_t v) { fb_quadlet_t data = v; debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Writing parameter address space id 0x%08X (%u), data: 0x%08X\n", id, id, data); fb_nodeaddr_t addr = FR_PARAM_SPACE_START + (id * 4); fb_nodeid_t nodeId = getNodeId() | 0xFFC0; if(!get1394Service().write_quadlet( nodeId, addr, CondSwapToBus32(data) ) ) { debugError("Could not write to node 0x%04X addr 0x%012" PRIX64 "\n", nodeId, addr); return false; } return true; } bool FocusriteDevice::getSpecificValueARM(uint32_t id, uint32_t *v) { fb_quadlet_t result; debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Reading parameter address space id 0x%08X\n", id); fb_nodeaddr_t addr = FR_PARAM_SPACE_START + (id * 4); fb_nodeid_t nodeId = getNodeId() | 0xFFC0; if(!get1394Service().read_quadlet( nodeId, addr, &result ) ) { debugError("Could not read from node 0x%04X addr 0x%012" PRIX64 "\n", nodeId, addr); return false; } result = CondSwapFromBus32(result); debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Read result: 0x%08X\n", result); *v = result; return true; } int FocusriteDevice::convertDefToSr( uint32_t def ) { switch(def) { case FOCUSRITE_CMD_SAMPLERATE_44K1: return 44100; case FOCUSRITE_CMD_SAMPLERATE_48K: return 48000; case FOCUSRITE_CMD_SAMPLERATE_88K2: return 88200; case FOCUSRITE_CMD_SAMPLERATE_96K: return 96000; case FOCUSRITE_CMD_SAMPLERATE_176K4: return 176400; case FOCUSRITE_CMD_SAMPLERATE_192K: return 192000; default: debugWarning("Unsupported samplerate def: %08X\n", def); return 0; } } uint32_t FocusriteDevice::convertSrToDef( int sr ) { switch(sr) { case 44100: return FOCUSRITE_CMD_SAMPLERATE_44K1; case 48000: return FOCUSRITE_CMD_SAMPLERATE_48K; case 88200: return FOCUSRITE_CMD_SAMPLERATE_88K2; case 96000: return FOCUSRITE_CMD_SAMPLERATE_96K; case 176400: return FOCUSRITE_CMD_SAMPLERATE_176K4; case 192000: return FOCUSRITE_CMD_SAMPLERATE_192K; default: debugWarning("Unsupported samplerate: %d\n", sr); return 0; } } // --- element implementation classes BinaryControl::BinaryControl(FocusriteDevice& parent, int id, int bit) : Control::Discrete(&parent) , m_Parent(parent) , m_cmd_id ( id ) , m_cmd_bit ( bit ) {} BinaryControl::BinaryControl(FocusriteDevice& parent, int id, int bit, std::string name, std::string label, std::string descr) : Control::Discrete(&parent) , m_Parent(parent) , m_cmd_id ( id ) , m_cmd_bit ( bit ) { setName(name); setLabel(label); setDescription(descr); } bool BinaryControl::setValue(int v) { uint32_t reg; uint32_t old_reg; if ( !m_Parent.getSpecificValue(m_cmd_id, ®) ) { debugError( "getSpecificValue failed\n" ); return 0; } old_reg=reg; if (v) { reg |= (1< 0x%08X)\n", m_cmd_id, v, old_reg, reg); if ( !m_Parent.setSpecificValue(m_cmd_id, reg) ) { debugError( "setSpecificValue failed\n" ); return false; } else return true; } int BinaryControl::getValue() { uint32_t reg; if ( !m_Parent.getSpecificValue(m_cmd_id, ®) ) { debugError( "getSpecificValue failed\n" ); return 0; } else { bool val= (reg & (1<0x07FFF) v=0x07FFF; else if (v<0) v=0; debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for id %d to %d\n", m_cmd_id, v); if ( !m_Parent.setSpecificValue(m_cmd_id, v) ) { debugError( "setSpecificValue failed\n" ); return false; } else return true; } int VolumeControl::getValue() { uint32_t val=0; if ( !m_Parent.getSpecificValue(m_cmd_id, &val) ) { debugError( "getSpecificValue failed\n" ); return 0; } else { debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for %d = %d\n", m_cmd_id, val); return val; } } MeteringControl::MeteringControl(FocusriteDevice& parent, int id) : Control::Discrete(&parent) , m_Parent(parent) , m_cmd_id ( id ) {} MeteringControl::MeteringControl(FocusriteDevice& parent, int id, std::string name, std::string label, std::string descr) : Control::Discrete(&parent) , m_Parent(parent) , m_cmd_id ( id ) { setName(name); setLabel(label); setDescription(descr); } int MeteringControl::getValue() { uint32_t val=0; if ( !m_Parent.getSpecificValue(m_cmd_id, &val) ) { debugError( "getSpecificValue failed\n" ); return 0; } else { debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for %d = %d\n", m_cmd_id, val); return val; } } // reg control RegisterControl::RegisterControl(FocusriteDevice& parent) : Control::Register(&parent) , m_Parent(parent) {} RegisterControl::RegisterControl(FocusriteDevice& parent, std::string name, std::string label, std::string descr) : Control::Register(&parent) , m_Parent(parent) { setName(name); setLabel(label); setDescription(descr); } bool RegisterControl::setValue(uint64_t addr, uint64_t v) { debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for addr %" PRIu64 " to %" PRIu64 "\n", addr, v); if ( !m_Parent.setSpecificValue(addr, v) ) { debugError( "setSpecificValue failed\n" ); return false; } else return true; } uint64_t RegisterControl::getValue(uint64_t addr) { uint32_t val=0; if ( !m_Parent.getSpecificValue(addr, &val) ) { debugError( "getSpecificValue failed\n" ); return 0; } else { debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for %" PRIu64 " = %u\n", addr, val); return val; } } // low resolution volume control VolumeControlLowRes::VolumeControlLowRes(FocusriteDevice& parent, int id, int shift) : Control::Discrete(&parent) , m_Parent(parent) , m_cmd_id ( id ) , m_bit_shift( shift ) {} VolumeControlLowRes::VolumeControlLowRes(FocusriteDevice& parent, int id, int shift, std::string name, std::string label, std::string descr) : Control::Discrete(&parent) , m_Parent(parent) , m_cmd_id ( id ) , m_bit_shift( shift ) { setName(name); setLabel(label); setDescription(descr); } bool VolumeControlLowRes::setValue(int v) { uint32_t reg; uint32_t old_reg; if (v>0xFF) v=0xFF; else if (v<0) v=0; if ( !m_Parent.getSpecificValue(m_cmd_id, ®) ) { debugError( "getSpecificValue failed\n" ); return 0; } old_reg=reg; reg &= ~(0xFF< 0x%08X)\n", m_cmd_id, v, m_bit_shift, old_reg, reg); if ( !m_Parent.setSpecificValue(m_cmd_id, reg) ) { debugError( "setSpecificValue failed\n" ); return false; } else return true; } int VolumeControlLowRes::getValue() { uint32_t val, reg; if ( !m_Parent.getSpecificValue(m_cmd_id, ®) ) { debugError( "getSpecificValue failed\n" ); return 0; } else { val = (reg & 0xFF)>>m_bit_shift; debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for %d: reg: 0x%08X, result=%d\n", m_cmd_id, reg, val); return val; } } // hardware dial control DialPositionControl::DialPositionControl(FocusriteDevice& parent, int id, int shift) : Control::Discrete(&parent) , m_Parent(parent) , m_cmd_id ( id ) , m_shift ( shift ) {} DialPositionControl::DialPositionControl(FocusriteDevice& parent, int id, int shift, std::string name, std::string label, std::string descr) : Control::Discrete(&parent) , m_Parent(parent) , m_cmd_id ( id ) , m_shift ( shift ) { setName(name); setLabel(label); setDescription(descr); } int DialPositionControl::getValue() { uint32_t val=0; if ( !m_Parent.getSpecificValue(m_cmd_id, &val) ) { debugError( "getSpecificValue failed\n" ); return 0; } else { if (m_shift > 0) { val = val >> m_shift; } else if (m_shift < 0) { val = val << -m_shift; } debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for %d = %d\n", m_cmd_id, val); return val; } } // Saffire pro matrix mixer element FocusriteMatrixMixer::FocusriteMatrixMixer(FocusriteDevice& p) : Control::MatrixMixer(&p, "MatrixMixer") , m_Parent(p) { } FocusriteMatrixMixer::FocusriteMatrixMixer(FocusriteDevice& p,std::string n) : Control::MatrixMixer(&p, n) , m_Parent(p) { } void FocusriteMatrixMixer::addSignalInfo(std::vector &target, std::string name, std::string label, std::string descr) { struct sSignalInfo s; s.name=name; s.label=label; s.description=descr; target.push_back(s); } void FocusriteMatrixMixer::setCellInfo(int row, int col, int addr, bool valid) { struct sCellInfo c; c.row=row; c.col=col; c.valid=valid; c.address=addr; m_CellInfo.at(row).at(col) = c; } void FocusriteMatrixMixer::show() { debugOutput(DEBUG_LEVEL_NORMAL, "Focusrite Matrix mixer\n"); } std::string FocusriteMatrixMixer::getRowName( const int row ) { debugOutput(DEBUG_LEVEL_VERBOSE, "name for row %d is %s\n", row, m_RowInfo.at(row).name.c_str()); return m_RowInfo.at(row).name; } std::string FocusriteMatrixMixer::getColName( const int col ) { debugOutput(DEBUG_LEVEL_VERBOSE, "name for col %d is %s\n", col, m_ColInfo.at(col).name.c_str()); return m_ColInfo.at(col).name; } int FocusriteMatrixMixer::canWrite( const int row, const int col ) { debugOutput(DEBUG_LEVEL_VERBOSE, "canWrite for row %d col %d is %d\n", row, col, m_CellInfo.at(row).at(col).valid); return m_CellInfo.at(row).at(col).valid; } double FocusriteMatrixMixer::setValue( const int row, const int col, const double val ) { int32_t v = (int32_t)val; struct sCellInfo c = m_CellInfo.at(row).at(col); debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for id %d row %d col %d to %lf (%d)\n", c.address, row, col, val, v); if (v>0x07FFF) v=0x07FFF; else if (v<0) v=0; if ( !m_Parent.setSpecificValue(c.address, v) ) { debugError( "setSpecificValue failed\n" ); return false; } else return true; } double FocusriteMatrixMixer::getValue( const int row, const int col ) { struct sCellInfo c=m_CellInfo.at(row).at(col); uint32_t val=0; if ( !m_Parent.getSpecificValue(c.address, &val) ) { debugError( "getSpecificValue failed\n" ); return 0; } else { debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for id %d row %d col %d = %u\n", c.address, row, col, val); return val; } } int FocusriteMatrixMixer::getRowCount( ) { return m_RowInfo.size(); } int FocusriteMatrixMixer::getColCount( ) { return m_ColInfo.size(); } } // Focusrite } // BeBoB libffado-2.4.5/src/bebob/focusrite/focusrite_generic.h0000644000175000001440000001660614206145246022410 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_FOCUSRITE_GENERIC_DEVICE_H #define BEBOB_FOCUSRITE_GENERIC_DEVICE_H #include "debugmodule/debugmodule.h" #include "bebob/bebob_avdevice.h" #include "libcontrol/BasicElements.h" #include "libcontrol/MatrixMixer.h" #include "libutil/SystemTimeSource.h" #define FR_PARAM_SPACE_START 0x000100000000LL namespace BeBoB { namespace Focusrite { class FocusriteDevice; class BinaryControl : public Control::Discrete { public: BinaryControl(FocusriteDevice& parent, int id, int bit); BinaryControl(FocusriteDevice& parent, int id, int bit, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 1;}; private: FocusriteDevice& m_Parent; unsigned int m_cmd_id; unsigned int m_cmd_bit; }; class VolumeControl : public Control::Discrete { public: VolumeControl(FocusriteDevice& parent, int id); VolumeControl(FocusriteDevice& parent, int id, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 0x07FFF;}; private: FocusriteDevice& m_Parent; unsigned int m_cmd_id; }; class MeteringControl : public Control::Discrete { public: MeteringControl(FocusriteDevice& parent, int id); MeteringControl(FocusriteDevice& parent, int id, std::string name, std::string label, std::string descr); virtual bool setValue(int v) {return false;}; virtual int getValue(); virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 0x07FFF;}; private: FocusriteDevice& m_Parent; unsigned int m_cmd_id; }; class RegisterControl : public Control::Register { public: RegisterControl(FocusriteDevice& parent); RegisterControl(FocusriteDevice& parent, std::string name, std::string label, std::string descr); virtual bool setValue(uint64_t addr, uint64_t value); virtual uint64_t getValue(uint64_t addr); private: FocusriteDevice& m_Parent; }; class VolumeControlLowRes : public Control::Discrete { public: VolumeControlLowRes(FocusriteDevice& parent, int id, int shift); VolumeControlLowRes(FocusriteDevice& parent, int id, int shift, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 0x0FF;}; private: FocusriteDevice& m_Parent; unsigned int m_cmd_id; unsigned int m_bit_shift; }; class DialPositionControl : public Control::Discrete { public: DialPositionControl(FocusriteDevice& parent, int id, int shift); DialPositionControl(FocusriteDevice& parent, int id, int shift, std::string name, std::string label, std::string descr); virtual bool setValue(int v) {return false;}; virtual int getValue(); virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 0x07FFF;}; private: FocusriteDevice& m_Parent; unsigned int m_cmd_id; int m_shift; }; class FocusriteMatrixMixer : public Control::MatrixMixer { public: FocusriteMatrixMixer(FocusriteDevice& parent); FocusriteMatrixMixer(FocusriteDevice& parent, std::string n); virtual ~FocusriteMatrixMixer() {}; virtual void show(); virtual int getRowCount( ); virtual int getColCount( ); virtual int canWrite( const int, const int ); virtual double setValue( const int, const int, const double ); virtual double getValue( const int, const int ); // full map updates are unsupported virtual bool getCoefficientMap(int &) {return false;}; virtual bool storeCoefficientMap(int &) {return false;}; bool hasNames() const { return true; } virtual std::string getRowName( const int ); virtual std::string getColName( const int ); bool canConnect() const { return false; } protected: struct sSignalInfo { std::string name; std::string label; std::string description; }; struct sCellInfo { int row; int col; // indicates whether a cell can be valid, this // doesn't mean that it is writable. Just that it can be. bool valid; // the address to use when manipulating this cell int address; }; virtual void init() = 0; virtual void addSignalInfo(std::vector &target, std::string name, std::string label, std::string descr); virtual void setCellInfo(int row, int col, int addr, bool valid); std::vector m_RowInfo; std::vector m_ColInfo; std::vector< std::vector > m_CellInfo; FocusriteDevice& m_Parent; }; class FocusriteDevice : public BeBoB::Device { public: FocusriteDevice(DeviceManager& d, ffado_smartptr( configRom )); virtual ~FocusriteDevice() {}; virtual void showDevice(); virtual void setVerboseLevel(int l); public: bool setSpecificValue(uint32_t id, uint32_t v); bool getSpecificValue(uint32_t id, uint32_t *v); protected: int convertDefToSr( uint32_t def ); uint32_t convertSrToDef( int sr ); private: bool setSpecificValueAvc(uint32_t id, uint32_t v); bool getSpecificValueAvc(uint32_t id, uint32_t *v); bool setSpecificValueARM(uint32_t id, uint32_t v); bool getSpecificValueARM(uint32_t id, uint32_t *v); protected: ffado_microsecs_t m_cmd_time_interval; ffado_microsecs_t m_earliest_next_cmd_time; }; } // namespace Focusrite } // namespace BeBoB #endif libffado-2.4.5/src/bebob/focusrite/focusrite_saffire.cpp0000644000175000001440000010200114206145246022727 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "focusrite_saffire.h" #include "focusrite_cmd.h" #include "devicemanager.h" namespace BeBoB { namespace Focusrite { SaffireDevice::SaffireDevice( DeviceManager& d, ffado_smartptr( configRom )) : FocusriteDevice( d, configRom) , m_MixerContainer( NULL ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::Focusrite::SaffireDevice (NodeID %d)\n", getConfigRom().getNodeId() ); if(getConfigRom().getGuid() < 0x130e0100040000LL) { m_isSaffireLE = false; } else { m_isSaffireLE = true; } // find the configured delay time for this device Util::Configuration &config = d.getConfiguration(); int delaytime = 0; if(config.getValueForDeviceSetting(getConfigRom().getNodeVendorId(), getConfigRom().getModelId(), "cmd_interval_time", delaytime)) { m_cmd_time_interval = delaytime; debugOutput( DEBUG_LEVEL_VERBOSE, "Setting command interval time to %" PRIu64 "\n", m_cmd_time_interval ); } else { m_cmd_time_interval = 10000; debugOutput( DEBUG_LEVEL_VERBOSE, "No command interval time setting found, defaulting to %" PRIu64 "\n", m_cmd_time_interval ); } } bool SaffireDevice::buildMixer() { bool result=true; debugOutput(DEBUG_LEVEL_VERBOSE, "Building a Focusrite Saffire mixer...\n"); destroyMixer(); // create the mixer object container m_MixerContainer = new Control::Container(this, "Mixer"); if (!m_MixerContainer) { debugError("Could not create mixer container...\n"); return false; } if(m_isSaffireLE) { // create control objects for the saffire LE result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_SPDIF_TRANSPARENT, 0, "SpdifTransparent", "S/PDIF Transparent", "S/PDIF Transparent")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_MIDITHRU, 0, "MidiThru", "MIDI Thru", "MIDI Thru")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_SAVE_SETTINGS, 0, "SaveSettings", "Save Settings", "Save Settings")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_HIGH_GAIN_LINE3, 0, "HighGainLine3", "High Gain Line-in 3", "High Gain Line-in 3")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_HIGH_GAIN_LINE4, 0, "HighGainLine4", "High Gain Line-in 4", "High Gain Line-in 4")); // output mute controls result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_BITFIELD_OUT12, FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_MUTE, "Out12Mute", "Out1/2 Mute", "Output 1/2 Mute")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_BITFIELD_OUT34, FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_MUTE, "Out34Mute", "Out3/4 Mute", "Output 3/4 Mute")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_BITFIELD_OUT56, FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_MUTE, "Out56Mute", "Out5/6 Mute", "Output 5/6 Mute")); // output front panel hw volume control result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_BITFIELD_OUT12, FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_HWCTRL, "Out12HwCtrl", "Out1/2 HwCtrl", "Output 1/2 Front Panel Hardware volume control")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_BITFIELD_OUT34, FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_HWCTRL, "Out34HwCtrl", "Out3/4 HwCtrl", "Output 3/4 Front Panel Hardware volume control")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_BITFIELD_OUT56, FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_HWCTRL, "Out56HwCtrl", "Out5/6 HwCtrl", "Output 5/6 Front Panel Hardware volume control")); // dac ignore result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_BITFIELD_OUT12, FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_DACIGNORE, "Out12DacIgnore", "Out1/2 Dac Ignore", "Output 1/2 Dac Ignore")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_BITFIELD_OUT34, FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_DACIGNORE, "Out34DacIgnore", "Out3/4 Dac Ignore", "Output 3/4 Dac Ignore")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_BITFIELD_OUT56, FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_DACIGNORE, "Out56DacIgnore", "Out5/6 Dac Ignore", "Output 5/6 Dac Ignore")); // output level controls result &= m_MixerContainer->addElement( new VolumeControlLowRes(*this, FR_SAFFIRELE_CMD_ID_BITFIELD_OUT12, FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_DAC, "Out12Level", "Out1/2 Level", "Output 1/2 Level")); result &= m_MixerContainer->addElement( new VolumeControlLowRes(*this, FR_SAFFIRELE_CMD_ID_BITFIELD_OUT34, FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_DAC, "Out34Level", "Out3/4 Level", "Output 3/4 Level")); result &= m_MixerContainer->addElement( new VolumeControlLowRes(*this, FR_SAFFIRELE_CMD_ID_BITFIELD_OUT56, FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_DAC, "Out56Level", "Out5/6 Level", "Output 5/6 Level")); } else { // create control objects for the saffire result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_INPUT_SOURCE, 0, "SpdifSwitch", "S/PDIF Switch", "S/PDIF Switch")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_MONO_MODE, 0, "MonoMode", "Mono Mode", "Toggle Mono Mode")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_DEVICE_MODE, 0, "DeviceMode", "Device Mode", "Toggle Device Mode")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_EXTERNAL_LOCK, 0, "ExternalLock", "External Lock", "Has external lock?")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_AUDIO_ON_STATUS, 0, "AudioOnStatus", "Audio On Status", "Audio On Status")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_SAVE_SETTINGS, 0, "SaveSettings", "Save Settings", "Save Settings")); // output mute controls result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT12, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_MUTE, "Out12Mute", "Out1/2 Mute", "Output 1/2 Mute")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT34, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_MUTE, "Out34Mute", "Out3/4 Mute", "Output 3/4 Mute")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT56, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_MUTE, "Out56Mute", "Out5/6 Mute", "Output 5/6 Mute")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT78, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_MUTE, "Out78Mute", "Out7/8 Mute", "Output 7/8 Mute")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT910, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_MUTE, "Out910Mute", "Out9/10 Mute", "Output 9/10 Mute")); // output front panel hw volume control result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT12, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_HWCTRL, "Out12HwCtrl", "Out1/2 HwCtrl", "Output 1/2 Front Panel Hardware volume control")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT34, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_HWCTRL, "Out34HwCtrl", "Out3/4 HwCtrl", "Output 3/4 Front Panel Hardware volume control")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT56, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_HWCTRL, "Out56HwCtrl", "Out5/6 HwCtrl", "Output 5/6 Front Panel Hardware volume control")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT78, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_HWCTRL, "Out78HwCtrl", "Out7/8 HwCtrl", "Output 7/8 Front Panel Hardware volume control")); // output level dim result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT12, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_DIM, "Out12Dim", "Out1/2 Dim", "Output 1/2 Level Dim")); // dac ignore result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT12, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_DACIGNORE, "Out12DacIgnore", "Out1/2 Dac Ignore", "Output 1/2 Dac Ignore")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT34, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_DACIGNORE, "Out34DacIgnore", "Out3/4 Dac Ignore", "Output 3/4 Dac Ignore")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT56, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_DACIGNORE, "Out56DacIgnore", "Out5/6 Dac Ignore", "Output 5/6 Dac Ignore")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT78, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_DACIGNORE, "Out78DacIgnore", "Out7/8 Dac Ignore", "Output 7/8 Dac Ignore")); // output level controls result &= m_MixerContainer->addElement( new VolumeControlLowRes(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT12, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_DAC, "Out12Level", "Out1/2 Level", "Output 1/2 Level")); result &= m_MixerContainer->addElement( new VolumeControlLowRes(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT34, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_DAC, "Out34Level", "Out3/4 Level", "Output 3/4 Level")); result &= m_MixerContainer->addElement( new VolumeControlLowRes(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT56, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_DAC, "Out56Level", "Out5/6 Level", "Output 5/6 Level")); result &= m_MixerContainer->addElement( new VolumeControlLowRes(*this, FR_SAFFIRE_CMD_ID_BITFIELD_OUT78, FR_SAFFIRE_CMD_ID_BITFIELD_BIT_DAC, "Out78Level", "Out7/8 Level", "Output 7/8 Level")); result &= m_MixerContainer->addElement( new DialPositionControl(*this, FR_SAFFIRE_CMD_ID_MONITOR_DIAL, 0, "MonitorDial", "Monitor Dial", "Monitor Dial Value")); // metering result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_IN1, "MeteringIn1", "Metering Input 1", "Metering on Input 1")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_IN2, "MeteringIn2", "Metering Input 2", "Metering on Input 2")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_IN3, "MeteringIn3", "Metering Input 3", "Metering on Input 3")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_IN4, "MeteringIn4", "Metering Input 4", "Metering on Input 4")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_PC1, "MeteringPc1", "Metering PC 1", "Metering on PC Channel 1")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_PC2, "MeteringPc2", "Metering PC 2", "Metering on PC Channel 2")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_PC3, "MeteringPc3", "Metering PC 3", "Metering on PC Channel 3")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_PC4, "MeteringPc4", "Metering PC 4", "Metering on PC Channel 4")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_PC5, "MeteringPc5", "Metering PC 5", "Metering on PC Channel 5")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_PC6, "MeteringPc6", "Metering PC 6", "Metering on PC Channel 6")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_PC7, "MeteringPc7", "Metering PC 7", "Metering on PC Channel 7")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_PC8, "MeteringPc8", "Metering PC 8", "Metering on PC Channel 8")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_PC9, "MeteringPc9", "Metering PC 9", "Metering on PC Channel 9")); result &= m_MixerContainer->addElement( new MeteringControl(*this, FR_SAFFIRE_CMD_ID_METERING_PC10, "MeteringPc10", "Metering PC 10", "Metering on PC Channel 10")); } // matrix mix controls if(m_isSaffireLE) { result &= m_MixerContainer->addElement( new SaffireMatrixMixer(*this, SaffireMatrixMixer::eMMT_LEMix48, "LEMix48")); result &= m_MixerContainer->addElement( new SaffireMatrixMixer(*this, SaffireMatrixMixer::eMMT_LEMix96, "LEMix96")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_SWAP_OUT4_OUT1_48K, 0, "Swap41_48", "Swap41_48", "Swap41_48")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIRELE_CMD_ID_SWAP_OUT4_OUT1_96K, 0, "Swap41_96", "Swap41_96", "Swap41_96")); } else { result &= m_MixerContainer->addElement( new SaffireMatrixMixer(*this, SaffireMatrixMixer::eMMT_SaffireStereoMatrixMix, "MatrixMixerStereo")); result &= m_MixerContainer->addElement( new SaffireMatrixMixer(*this, SaffireMatrixMixer::eMMT_SaffireMonoMatrixMix, "MatrixMixerMono")); } if (!result) { debugWarning("One or more control elements could not be created."); // clean up those that were created destroyMixer(); return false; } if (!addElement(m_MixerContainer)) { debugWarning("Could not register mixer to device\n"); // clean up destroyMixer(); return false; } // add a direct register access element if (!addElement(new RegisterControl(*this, "Register", "Register Access", "Direct register access"))) { debugWarning("Could not create register control element."); // clean up those that were created destroyMixer(); return false; } return true; } bool SaffireDevice::destroyMixer() { debugOutput(DEBUG_LEVEL_VERBOSE, "destroy mixer...\n"); if (m_MixerContainer == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "no mixer to destroy...\n"); return true; } if (!deleteElement(m_MixerContainer)) { debugError("Mixer present but not registered to the avdevice\n"); return false; } // remove and delete (as in free) child control elements m_MixerContainer->clearElements(true); delete m_MixerContainer; return true; } std::vector SaffireDevice::getSupportedSamplingFrequencies() { std::vector frequencies; frequencies.push_back(44100); frequencies.push_back(48000); frequencies.push_back(88200); frequencies.push_back(96000); return frequencies; } void SaffireDevice::showDevice() { if(m_isSaffireLE) { debugOutput(DEBUG_LEVEL_NORMAL, "This is a BeBoB::Focusrite::SaffireDevice (Saffire LE)\n"); } else { debugOutput(DEBUG_LEVEL_NORMAL, "This is a BeBoB::Focusrite::SaffireDevice (Saffire)\n"); } FocusriteDevice::showDevice(); } void SaffireDevice::setVerboseLevel(int l) { debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); FocusriteDevice::setVerboseLevel(l); } // Saffire pro matrix mixer element SaffireMatrixMixer::SaffireMatrixMixer(SaffireDevice& p, enum eMatrixMixerType type) : FocusriteMatrixMixer(p, "MatrixMixer") , m_type(type) { init(); } SaffireMatrixMixer::SaffireMatrixMixer(SaffireDevice& p, enum eMatrixMixerType type, std::string n) : FocusriteMatrixMixer(p, n) , m_type(type) { init(); } void SaffireMatrixMixer::init() { if (m_type==eMMT_SaffireStereoMatrixMix) { m_RowInfo.clear(); addSignalInfo(m_RowInfo, "PC910", "PC 9/10", "PC Channel 9/10"); addSignalInfo(m_RowInfo, "PC12", "PC 1/2", "PC Channel 1/2"); addSignalInfo(m_RowInfo, "PC34", "PC 3/4", "PC Channel 3/4"); addSignalInfo(m_RowInfo, "PC56", "PC 5/6", "PC Channel 5/6"); addSignalInfo(m_RowInfo, "PC78", "PC 7/8", "PC Channel 7/8"); addSignalInfo(m_RowInfo, "IN12", "Input 1/2", "Hardware Inputs 1/2"); addSignalInfo(m_RowInfo, "IN34", "Input 3/4", "Hardware Inputs 3/4 (dry / S/PDIF)"); addSignalInfo(m_RowInfo, "FX", "Effect return", "Effect return"); m_ColInfo.clear(); addSignalInfo(m_ColInfo, "OUT910", "OUT 9/10", "Output 9/10"); addSignalInfo(m_ColInfo, "OUT12", "OUT 1/2", "Output 1/2"); addSignalInfo(m_ColInfo, "OUT34", "OUT 3/4", "Output 3/4"); addSignalInfo(m_ColInfo, "OUT56", "OUT 5/6", "Output 5/6 (HP1)"); addSignalInfo(m_ColInfo, "OUT78", "OUT 7/8", "Output 7/8 (HP2)"); // init the cell matrix #define FOCUSRITE_SAFFIRE_STEREO_MATRIXMIX_NB_COLS 5 #define FOCUSRITE_SAFFIRE_STEREO_MATRIXMIX_NB_ROWS 8 #define FOCUSRITE_SAFFIRE_STEREO_MATRIXMIX_OFFSET 0 std::vector tmp_cols( FOCUSRITE_SAFFIRE_STEREO_MATRIXMIX_NB_COLS ); std::vector< std::vector > tmp_all(FOCUSRITE_SAFFIRE_STEREO_MATRIXMIX_NB_ROWS, tmp_cols); m_CellInfo = tmp_all; struct sCellInfo c; c.row=-1; c.col=-1; c.valid=false; c.address=0; // all cells are valid for (int i=0; i < FOCUSRITE_SAFFIRE_STEREO_MATRIXMIX_NB_ROWS; i++) { for (int j=0; j < FOCUSRITE_SAFFIRE_STEREO_MATRIXMIX_NB_COLS; j++) { c.row = i; c.col = j; c.valid = true; c.address = FOCUSRITE_SAFFIRE_STEREO_MATRIXMIX_OFFSET + c.row * FOCUSRITE_SAFFIRE_STEREO_MATRIXMIX_NB_COLS + c.col; m_CellInfo.at(i).at(j) = c; } } } else if (m_type==eMMT_SaffireMonoMatrixMix) { m_RowInfo.clear(); addSignalInfo(m_RowInfo, "IN1", "Input 1", "Hardware Inputs 1"); addSignalInfo(m_RowInfo, "IN3", "Input 3", "Hardware Inputs 3"); addSignalInfo(m_RowInfo, "FX1", "Effect return 1", "Effect return 1"); addSignalInfo(m_RowInfo, "IN2", "Input 2", "Hardware Inputs 2"); addSignalInfo(m_RowInfo, "IN4", "Input 4", "Hardware Inputs 4"); addSignalInfo(m_RowInfo, "FX2", "Effect return 2", "Effect return 2"); addSignalInfo(m_RowInfo, "PC910", "PC 9/10", "PC Channel 9/10"); addSignalInfo(m_RowInfo, "PC12", "PC 1/2", "PC Channel 1/2"); addSignalInfo(m_RowInfo, "PC34", "PC 3/4", "PC Channel 3/4"); addSignalInfo(m_RowInfo, "PC56", "PC 5/6", "PC Channel 5/6"); addSignalInfo(m_RowInfo, "PC78", "PC 7/8", "PC Channel 7/8"); m_ColInfo.clear(); addSignalInfo(m_ColInfo, "OUT910", "OUT 9/10", "Output 9/10"); addSignalInfo(m_ColInfo, "OUT12", "OUT 1/2", "Output 1/2"); addSignalInfo(m_ColInfo, "OUT34", "OUT 3/4", "Output 3/4"); addSignalInfo(m_ColInfo, "OUT56", "OUT 5/6", "Output 5/6 (HP1)"); addSignalInfo(m_ColInfo, "OUT78", "OUT 7/8", "Output 7/8 (HP2)"); // init the cell matrix #define FOCUSRITE_SAFFIRE_MONO_MATRIXMIX_NB_COLS 5 #define FOCUSRITE_SAFFIRE_MONO_MATRIXMIX_NB_ROWS 11 #define FOCUSRITE_SAFFIRE_MONO_MATRIXMIX_OFFSET 0 std::vector tmp_cols( FOCUSRITE_SAFFIRE_MONO_MATRIXMIX_NB_COLS ); std::vector< std::vector > tmp_all(FOCUSRITE_SAFFIRE_MONO_MATRIXMIX_NB_ROWS, tmp_cols); m_CellInfo = tmp_all; struct sCellInfo c; c.row=-1; c.col=-1; c.valid=false; c.address=0; // all cells are valid for (int i=0; i < FOCUSRITE_SAFFIRE_MONO_MATRIXMIX_NB_ROWS; i++) { for (int j=0; j < FOCUSRITE_SAFFIRE_MONO_MATRIXMIX_NB_COLS; j++) { c.row = i; c.col = j; c.valid = true; c.address = FOCUSRITE_SAFFIRE_MONO_MATRIXMIX_OFFSET + c.row * FOCUSRITE_SAFFIRE_MONO_MATRIXMIX_NB_COLS + c.col; m_CellInfo.at(i).at(j) = c; } } } else if (m_type == eMMT_LEMix48) { addSignalInfo(m_RowInfo, "IN1", "Input 1", "Analog Input 1"); addSignalInfo(m_RowInfo, "IN2", "Input 2", "Analog Input 2"); addSignalInfo(m_RowInfo, "IN3", "Input 3", "Analog Input 3"); addSignalInfo(m_RowInfo, "IN4", "Input 4", "Analog Input 4"); addSignalInfo(m_RowInfo, "SPDIFL", "SPDIF L", "S/PDIF Left Input"); addSignalInfo(m_RowInfo, "SPDIFR", "SPDIF R", "S/PDIF Right Input"); addSignalInfo(m_RowInfo, "PC1", "PC 1", "PC Channel 1"); addSignalInfo(m_RowInfo, "PC2", "PC 2", "PC Channel 2"); addSignalInfo(m_RowInfo, "PC3", "PC 3", "PC Channel 3"); addSignalInfo(m_RowInfo, "PC4", "PC 4", "PC Channel 4"); addSignalInfo(m_RowInfo, "PC5", "PC 5", "PC Channel 5"); addSignalInfo(m_RowInfo, "PC6", "PC 6", "PC Channel 6"); addSignalInfo(m_RowInfo, "PC7", "PC 7", "PC Channel 7"); addSignalInfo(m_RowInfo, "PC8", "PC 8", "PC Channel 8"); addSignalInfo(m_ColInfo, "OUT1", "OUT 1", "Output 1"); addSignalInfo(m_ColInfo, "OUT2", "OUT 2", "Output 2"); addSignalInfo(m_ColInfo, "OUT3", "OUT 3", "Output 3"); addSignalInfo(m_ColInfo, "OUT4", "OUT 4", "Output 4"); // init the cell matrix #define FOCUSRITE_SAFFIRELE_48KMIX_NB_COLS 4 #define FOCUSRITE_SAFFIRELE_48KMIX_NB_ROWS 14 std::vector tmp_cols( FOCUSRITE_SAFFIRELE_48KMIX_NB_COLS ); std::vector< std::vector > tmp_all(FOCUSRITE_SAFFIRELE_48KMIX_NB_ROWS,tmp_cols); m_CellInfo = tmp_all; struct sCellInfo c; c.row=-1; c.col=-1; c.valid=false; c.address=0; for (int i=0;i tmp_cols( FOCUSRITE_SAFFIRELE_96KMIX_NB_COLS ); std::vector< std::vector > tmp_all(FOCUSRITE_SAFFIRELE_96KMIX_NB_ROWS,tmp_cols); m_CellInfo = tmp_all; struct sCellInfo c; c.row=-1; c.col=-1; c.valid=false; c.address=0; for (int i=0;i. * */ #ifndef BEBOB_FOCUSRITE_SAFFIRE_DEVICE_H #define BEBOB_FOCUSRITE_SAFFIRE_DEVICE_H #include "focusrite_generic.h" #include "libcontrol/BasicElements.h" #include "libcontrol/MatrixMixer.h" // -- Original Saffire -- // no need for control id's, we can directly compute the addresses of the matrix mixer /* MIXER LAYOUT (): OFFSET: 30 |-- Out9/10--| |-- Out1/2 --| |-- Out3/4 --| |-- Out5/6 --| |-- Out7/8 --| P5 0: 0/ 0 1: 110/ 110 2: 0/ 0 3: 0/ 0 4: 0/ 0 P1 5: 0/ 0 6:32767/32767 7: 0/ 0 8: 0/ 0 9: 0/ 0 P2 10: 0/ 0 11: 0/ 0 12:32767/32767 13: 0/ 0 14: 0/ 0 P3 15: 0/ 0 16: 0/ 0 17: 0/ 0 18:32767/32767 19: 0/ 0 P4 20: 0/ 0 21: 0/ 0 22: 0/ 0 23: 0/ 0 24:32767/32767 R1 25: 0/ 0 26: 0/ 0 27: 0/ 0 28: 0/ 0 29: 0/ 0 R2 30: 0/ 0 31: 0/ 0 32: 0/ 0 33: 0/ 0 34: 0/ 0 Fx 35: 0/ 0 36: 0/ 0 37: 0/ 0 38: 0/ 0 39: 0/ 0 P5: DAW ch 9/10 P1: DAW ch 1/2 P2: DAW ch 3/4 P3: DAW ch 5/6 P4: DAW ch 7/8 R1: HW INPUT ch 1/2 / Reverb ch 1 R2: HW INPUT ch 3/4 / Reverb ch 2 Fx: reverb/fx return? / input mix */ // the control ID's #define FR_SAFFIRE_CMD_ID_BITFIELD_OUT12 55 #define FR_SAFFIRE_CMD_ID_BITFIELD_OUT34 56 #define FR_SAFFIRE_CMD_ID_BITFIELD_OUT56 57 #define FR_SAFFIRE_CMD_ID_BITFIELD_OUT78 58 #define FR_SAFFIRE_CMD_ID_BITFIELD_OUT910 59 #define FR_SAFFIRE_CMD_ID_BITFIELD_BIT_DIM 24 #define FR_SAFFIRE_CMD_ID_BITFIELD_BIT_MUTE 25 #define FR_SAFFIRE_CMD_ID_BITFIELD_BIT_DACIGNORE 26 #define FR_SAFFIRE_CMD_ID_BITFIELD_BIT_HWCTRL 27 #define FR_SAFFIRE_CMD_ID_BITFIELD_BIT_DAC 0 // other stuff #define FR_SAFFIRE_CMD_ID_MONITOR_DIAL 61 #define FR_SAFFIRE_CMD_ID_INPUT_SOURCE 62 #define FR_SAFFIRE_CMD_ID_INPUT_SOURCE_SPDIF 1 #define FR_SAFFIRE_CMD_ID_INPUT_SOURCE_ANALOG 0 #define FR_SAFFIRE_CMD_ID_MONO_MODE 63 #define FR_SAFFIRE_CMD_ID_MONO_MODE_STEREO 0 #define FR_SAFFIRE_CMD_ID_MONO_MODE_MONO 1 #define FR_SAFFIRE_CMD_ID_METERING_IN1 64 #define FR_SAFFIRE_CMD_ID_METERING_IN2 65 #define FR_SAFFIRE_CMD_ID_METERING_IN3 66 #define FR_SAFFIRE_CMD_ID_METERING_IN4 67 #define FR_SAFFIRE_CMD_ID_METERING_PC1 68 #define FR_SAFFIRE_CMD_ID_METERING_PC2 69 #define FR_SAFFIRE_CMD_ID_METERING_PC3 70 #define FR_SAFFIRE_CMD_ID_METERING_PC4 71 #define FR_SAFFIRE_CMD_ID_METERING_PC5 72 #define FR_SAFFIRE_CMD_ID_METERING_PC6 73 #define FR_SAFFIRE_CMD_ID_METERING_PC7 74 #define FR_SAFFIRE_CMD_ID_METERING_PC8 75 #define FR_SAFFIRE_CMD_ID_METERING_PC9 76 #define FR_SAFFIRE_CMD_ID_METERING_PC10 77 #define FR_SAFFIRE_CMD_ID_DEVICE_MODE 78 #define FR_SAFFIRE_CMD_ID_DEVICE_MODE_NORMAL 0 #define FR_SAFFIRE_CMD_ID_DEVICE_MODE_SCARD 1 #define FR_SAFFIRE_CMD_ID_EXTERNAL_LOCK 79 #define FR_SAFFIRE_CMD_ID_AUDIO_ON_STATUS 80 #define FR_SAFFIRE_CMD_ID_SAVE_SETTINGS 82 #define FR_SAFFIRELE_CMD_ID_DSP_REVISION 1022 #define FR_SAFFIRELE_CMD_ID_DSP_VERSION 1023 // -- Saffire LE -- // The ID's for the hardware input matrix mixer #define FR_SAFFIRELE_CMD_ID_PC1_TO_OUT1 0 #define FR_SAFFIRELE_CMD_ID_PC1_TO_OUT3 1 #define FR_SAFFIRELE_CMD_ID_PC1_TO_OUT2 2 #define FR_SAFFIRELE_CMD_ID_PC1_TO_OUT4 3 #define FR_SAFFIRELE_CMD_ID_PC3_TO_OUT1 4 #define FR_SAFFIRELE_CMD_ID_PC3_TO_OUT3 5 #define FR_SAFFIRELE_CMD_ID_PC3_TO_OUT2 6 #define FR_SAFFIRELE_CMD_ID_PC3_TO_OUT4 7 #define FR_SAFFIRELE_CMD_ID_PC5_TO_OUT1 8 #define FR_SAFFIRELE_CMD_ID_PC5_TO_OUT3 9 #define FR_SAFFIRELE_CMD_ID_PC5_TO_OUT2 10 #define FR_SAFFIRELE_CMD_ID_PC5_TO_OUT4 11 #define FR_SAFFIRELE_CMD_ID_PC7_TO_OUT1 12 #define FR_SAFFIRELE_CMD_ID_PC7_TO_OUT3 13 #define FR_SAFFIRELE_CMD_ID_PC7_TO_OUT2 14 #define FR_SAFFIRELE_CMD_ID_PC7_TO_OUT4 15 #define FR_SAFFIRELE_CMD_ID_PC2_TO_OUT1 16 #define FR_SAFFIRELE_CMD_ID_PC2_TO_OUT3 17 #define FR_SAFFIRELE_CMD_ID_PC2_TO_OUT2 18 #define FR_SAFFIRELE_CMD_ID_PC2_TO_OUT4 19 #define FR_SAFFIRELE_CMD_ID_PC4_TO_OUT1 20 #define FR_SAFFIRELE_CMD_ID_PC4_TO_OUT3 21 #define FR_SAFFIRELE_CMD_ID_PC4_TO_OUT2 22 #define FR_SAFFIRELE_CMD_ID_PC4_TO_OUT4 23 #define FR_SAFFIRELE_CMD_ID_PC6_TO_OUT1 24 #define FR_SAFFIRELE_CMD_ID_PC6_TO_OUT3 25 #define FR_SAFFIRELE_CMD_ID_PC6_TO_OUT2 26 #define FR_SAFFIRELE_CMD_ID_PC6_TO_OUT4 27 #define FR_SAFFIRELE_CMD_ID_PC8_TO_OUT1 28 #define FR_SAFFIRELE_CMD_ID_PC8_TO_OUT3 29 #define FR_SAFFIRELE_CMD_ID_PC8_TO_OUT2 30 #define FR_SAFFIRELE_CMD_ID_PC8_TO_OUT4 31 #define FR_SAFFIRELE_CMD_ID_IN1_TO_OUT1 32 #define FR_SAFFIRELE_CMD_ID_IN1_TO_OUT3 33 #define FR_SAFFIRELE_CMD_ID_IN1_TO_OUT2 34 #define FR_SAFFIRELE_CMD_ID_IN1_TO_OUT4 35 #define FR_SAFFIRELE_CMD_ID_IN3_TO_OUT1 36 #define FR_SAFFIRELE_CMD_ID_IN3_TO_OUT3 37 #define FR_SAFFIRELE_CMD_ID_IN3_TO_OUT2 38 #define FR_SAFFIRELE_CMD_ID_IN3_TO_OUT4 39 #define FR_SAFFIRELE_CMD_ID_SPDIF1_TO_OUT1 40 #define FR_SAFFIRELE_CMD_ID_SPDIF1_TO_OUT3 41 #define FR_SAFFIRELE_CMD_ID_SPDIF1_TO_OUT2 42 #define FR_SAFFIRELE_CMD_ID_SPDIF1_TO_OUT4 43 #define FR_SAFFIRELE_CMD_ID_IN2_TO_OUT1 44 #define FR_SAFFIRELE_CMD_ID_IN2_TO_OUT3 45 #define FR_SAFFIRELE_CMD_ID_IN2_TO_OUT2 46 #define FR_SAFFIRELE_CMD_ID_IN2_TO_OUT4 47 #define FR_SAFFIRELE_CMD_ID_IN4_TO_OUT1 48 #define FR_SAFFIRELE_CMD_ID_IN4_TO_OUT3 49 #define FR_SAFFIRELE_CMD_ID_IN4_TO_OUT2 50 #define FR_SAFFIRELE_CMD_ID_IN4_TO_OUT4 51 #define FR_SAFFIRELE_CMD_ID_SPDIF2_TO_OUT1 52 #define FR_SAFFIRELE_CMD_ID_SPDIF2_TO_OUT3 53 #define FR_SAFFIRELE_CMD_ID_SPDIF2_TO_OUT2 54 #define FR_SAFFIRELE_CMD_ID_SPDIF2_TO_OUT4 55 #define FR_SAFFIRELE_CMD_ID_SWAP_OUT4_OUT1_48K 64 // 96kHz controls #define FR_SAFFIRELE_CMD_ID_IN1_TO_RECMIX_96K 66 #define FR_SAFFIRELE_CMD_ID_IN3_TO_RECMIX_96K 67 #define FR_SAFFIRELE_CMD_ID_SPDIF1_TO_RECMIX_96K 68 #define FR_SAFFIRELE_CMD_ID_IN2_TO_RECMIX_96K 69 #define FR_SAFFIRELE_CMD_ID_IN4_TO_RECMIX_96K 70 #define FR_SAFFIRELE_CMD_ID_SPDIF2_TO_RECMIX_96K 71 #define FR_SAFFIRELE_CMD_ID_RECMIX_TO_OUT1_96K 72 #define FR_SAFFIRELE_CMD_ID_PC1_TO_OUT1_96K 73 #define FR_SAFFIRELE_CMD_ID_PC2_TO_OUT1_96K 74 #define FR_SAFFIRELE_CMD_ID_RECMIX_TO_OUT3_96K 75 #define FR_SAFFIRELE_CMD_ID_PC1_TO_OUT3_96K 76 #define FR_SAFFIRELE_CMD_ID_PC2_TO_OUT3_96K 77 #define FR_SAFFIRELE_CMD_ID_RECMIX_TO_OUT2_96K 78 #define FR_SAFFIRELE_CMD_ID_PC1_TO_OUT2_96K 79 #define FR_SAFFIRELE_CMD_ID_PC2_TO_OUT2_96K 80 #define FR_SAFFIRELE_CMD_ID_RECMIX_TO_OUT4_96K 81 #define FR_SAFFIRELE_CMD_ID_PC1_TO_OUT4_96K 82 #define FR_SAFFIRELE_CMD_ID_PC2_TO_OUT4_96K 83 #define FR_SAFFIRELE_CMD_ID_SWAP_OUT4_OUT1_96K 84 // metering #define FR_SAFFIRELE_CMD_ID_METERING_IN1 90 #define FR_SAFFIRELE_CMD_ID_METERING_IN3 91 #define FR_SAFFIRELE_CMD_ID_METERING_SPDIF1 92 #define FR_SAFFIRELE_CMD_ID_METERING_IN2 93 #define FR_SAFFIRELE_CMD_ID_METERING_IN4 94 #define FR_SAFFIRELE_CMD_ID_METERING_SPDIF2 95 #define FR_SAFFIRELE_CMD_ID_METERING_OUT1 96 #define FR_SAFFIRELE_CMD_ID_METERING_OUT3 97 #define FR_SAFFIRELE_CMD_ID_METERING_OUT5 98 #define FR_SAFFIRELE_CMD_ID_METERING_OUT7 99 #define FR_SAFFIRELE_CMD_ID_METERING_OUT2 100 #define FR_SAFFIRELE_CMD_ID_METERING_OUT4 101 #define FR_SAFFIRELE_CMD_ID_METERING_OUT6 102 #define FR_SAFFIRELE_CMD_ID_METERING_OUT8 103 #define FR_SAFFIRELE_CMD_ID_METERING_PC1 104 #define FR_SAFFIRELE_CMD_ID_METERING_PC3 105 #define FR_SAFFIRELE_CMD_ID_METERING_PC2 106 #define FR_SAFFIRELE_CMD_ID_METERING_PC4 107 // other stuff #define FR_SAFFIRELE_CMD_ID_HIGH_GAIN_LINE3 85 #define FR_SAFFIRELE_CMD_ID_HIGH_GAIN_LINE4 86 #define FR_SAFFIRELE_CMD_ID_BITFIELD_OUT12 87 #define FR_SAFFIRELE_CMD_ID_BITFIELD_OUT34 88 #define FR_SAFFIRELE_CMD_ID_BITFIELD_OUT56 89 #define FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_DIM 24 #define FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_MUTE 25 #define FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_DACIGNORE 26 #define FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_HWCTRL 27 #define FR_SAFFIRELE_CMD_ID_BITFIELD_BIT_DAC 0 #define FR_SAFFIRELE_CMD_ID_EXT_CLOCK_LOCK 108 #define FR_SAFFIRELE_CMD_ID_AUDIO_ON 109 #define FR_SAFFIRELE_CMD_ID_SAVE_SETTINGS 110 #define FR_SAFFIRELE_CMD_ID_MIDITHRU 111 #define FR_SAFFIRELE_CMD_ID_SPDIF_TRANSPARENT 112 namespace BeBoB { namespace Focusrite { class SaffireDevice; class SaffireMatrixMixer : public FocusriteMatrixMixer { public: enum eMatrixMixerType { eMMT_SaffireStereoMatrixMix, eMMT_SaffireMonoMatrixMix, eMMT_LEMix48, eMMT_LEMix96, }; public: SaffireMatrixMixer(SaffireDevice& parent, enum eMatrixMixerType type); SaffireMatrixMixer(SaffireDevice& parent, enum eMatrixMixerType type, std::string n); virtual ~SaffireMatrixMixer() {}; virtual void show(); protected: void init(); enum eMatrixMixerType m_type; }; class SaffireDevice : public FocusriteDevice { public: SaffireDevice(DeviceManager& d, ffado_smartptr( configRom )); virtual ~SaffireDevice() {}; virtual void showDevice(); virtual void setVerboseLevel(int l); virtual bool buildMixer(); virtual bool destroyMixer(); virtual std::vector getSupportedSamplingFrequencies(); private: Control::Container *m_MixerContainer; bool m_isSaffireLE; }; } // namespace Focusrite } // namespace BeBoB #endif libffado-2.4.5/src/bebob/focusrite/focusrite_saffirepro.cpp0000644000175000001440000014762014206145246023470 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "focusrite_saffirepro.h" #include "focusrite_cmd.h" #include "devicemanager.h" #include "libutil/SystemTimeSource.h" #include "libutil/ByteSwap.h" #include namespace BeBoB { namespace Focusrite { SaffireProDevice::SaffireProDevice( DeviceManager& d, ffado_smartptr( configRom )) : FocusriteDevice( d, configRom ) , m_MixerContainer( NULL ) , m_ControlContainer( NULL ) , m_deviceNameControl( NULL ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::Focusrite::SaffireProDevice (NodeID %d)\n", getConfigRom().getNodeId() ); addOption(Util::OptionContainer::Option("rebootOnSamplerateChange", true)); updateClockSources(); } SaffireProDevice::~SaffireProDevice() { destroyMixer(); } bool SaffireProDevice::buildMixer() { bool result=true; debugOutput(DEBUG_LEVEL_VERBOSE, "Building a Focusrite SaffirePro mixer...\n"); destroyMixer(); // create the mixer object container m_MixerContainer = new Control::Container(this, "Mixer"); if (!m_MixerContainer) { debugError("Could not create mixer container...\n"); return false; } // output mute controls result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT12, FR_SAFFIREPRO_CMD_BITFIELD_BIT_MUTE, "Out12Mute", "Out1/2 Mute", "Output 1/2 Mute")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT34, FR_SAFFIREPRO_CMD_BITFIELD_BIT_MUTE, "Out34Mute", "Out3/4 Mute", "Output 3/4 Mute")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT56, FR_SAFFIREPRO_CMD_BITFIELD_BIT_MUTE, "Out56Mute", "Out5/6 Mute", "Output 5/6 Mute")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT78, FR_SAFFIREPRO_CMD_BITFIELD_BIT_MUTE, "Out78Mute", "Out7/8 Mute", "Output 7/8 Mute")); // output front panel hw volume control result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT12, FR_SAFFIREPRO_CMD_BITFIELD_BIT_HWCTRL, "Out12HwCtrl", "Out1/2 HwCtrl", "Output 1/2 Front Panel Hardware volume control")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT34, FR_SAFFIREPRO_CMD_BITFIELD_BIT_HWCTRL, "Out34HwCtrl", "Out3/4 HwCtrl", "Output 3/4 Front Panel Hardware volume control")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT56, FR_SAFFIREPRO_CMD_BITFIELD_BIT_HWCTRL, "Out56HwCtrl", "Out5/6 HwCtrl", "Output 5/6 Front Panel Hardware volume control")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT78, FR_SAFFIREPRO_CMD_BITFIELD_BIT_HWCTRL, "Out78HwCtrl", "Out7/8 HwCtrl", "Output 7/8 Front Panel Hardware volume control")); // output active monitor padding result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT12, FR_SAFFIREPRO_CMD_BITFIELD_BIT_PAD, "Out12Pad", "Out1/2 Pad", "Output 1/2 Active Monitor Pad")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT34, FR_SAFFIREPRO_CMD_BITFIELD_BIT_PAD, "Out34Pad", "Out3/4 Pad", "Output 3/4 Active Monitor Pad")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT56, FR_SAFFIREPRO_CMD_BITFIELD_BIT_PAD, "Out56Pad", "Out5/6 Pad", "Output 5/6 Active Monitor Pad")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT78, FR_SAFFIREPRO_CMD_BITFIELD_BIT_PAD, "Out78Pad", "Out7/8 Pad", "Output 7/8 Active Monitor Pad")); // output level dim result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT12, FR_SAFFIREPRO_CMD_BITFIELD_BIT_DIM, "Out12Dim", "Out1/2 Dim", "Output 1/2 Level Dim")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT34, FR_SAFFIREPRO_CMD_BITFIELD_BIT_DIM, "Out34Dim", "Out3/4 Dim", "Output 3/4 Level Dim")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT56, FR_SAFFIREPRO_CMD_BITFIELD_BIT_DIM, "Out56Dim", "Out5/6 Dim", "Output 5/6 Level Dim")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT78, FR_SAFFIREPRO_CMD_BITFIELD_BIT_DIM, "Out78Dim", "Out7/8 Dim", "Output 7/8 Level Dim")); // front panel dial position result &= m_MixerContainer->addElement( new DialPositionControl(*this, FR_SAFFIREPRO_CMD_ID_MONITOR_DIAL, 0, "MonitorDial", "Monitor Dial", "Monitor Dial Value")); // direct monitoring controls result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_BITFIELD, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH1, "DirectMonitorCH1", "Direct Monitor CH1", "Enable Direct Monitor on Channel 1")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_BITFIELD, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH2, "DirectMonitorCH2", "Direct Monitor CH2", "Enable Direct Monitor on Channel 2")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_BITFIELD, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH3, "DirectMonitorCH3", "Direct Monitor CH3", "Enable Direct Monitor on Channel 3")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_BITFIELD, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH4, "DirectMonitorCH4", "Direct Monitor CH4", "Enable Direct Monitor on Channel 4")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_BITFIELD, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH5, "DirectMonitorCH5", "Direct Monitor CH5", "Enable Direct Monitor on Channel 5")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_BITFIELD, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH6, "DirectMonitorCH6", "Direct Monitor CH6", "Enable Direct Monitor on Channel 6")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_BITFIELD, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH7, "DirectMonitorCH7", "Direct Monitor CH7", "Enable Direct Monitor on Channel 7")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_BITFIELD, FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH8, "DirectMonitorCH8", "Direct Monitor CH8", "Enable Direct Monitor on Channel 8")); // output level controls result &= m_MixerContainer->addElement( new VolumeControlLowRes(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT12, 0, "Out12Level", "Out1/2 Level", "Output 1/2 Level")); result &= m_MixerContainer->addElement( new VolumeControlLowRes(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT34, 0, "Out34Level", "Out3/4 Level", "Output 3/4 Level")); result &= m_MixerContainer->addElement( new VolumeControlLowRes(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT56, 0, "Out56Level", "Out5/6 Level", "Output 5/6 Level")); result &= m_MixerContainer->addElement( new VolumeControlLowRes(*this, FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT78, 0, "Out78Level", "Out7/8 Level", "Output 7/8 Level")); // indicators result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_MUTE_INDICATOR, 0, "Out12MuteInd", "Out1/2 Mute Ind", "Output 1/2 Mute Indicator")); result &= m_MixerContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_DIM_INDICATOR, 0, "Out12DimInd", "Out1/2 Dim Ind", "Output 1/2 Level Dim Indicator")); // matrix mix controls result &= m_MixerContainer->addElement( new SaffireProMatrixMixer(*this, SaffireProMatrixMixer::eMMT_InputMix, "InputMix")); result &= m_MixerContainer->addElement( new SaffireProMatrixMixer(*this, SaffireProMatrixMixer::eMMT_OutputMix, "OutputMix")); if (!result) { debugWarning("One or more mixer control elements could not be created."); // clean up those that couldn't be created destroyMixer(); return false; } if (!addElement(m_MixerContainer)) { debugWarning("Could not register mixer to device\n"); // clean up destroyMixer(); return false; } // special controls m_ControlContainer = new Control::Container(this, "Control"); if (!m_ControlContainer) { debugError("Could not create mixer container...\n"); return false; } // create control objects for the saffire pro result &= m_ControlContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_PHANTOM14, 0, "Phantom_1to4", "Phantom 1-4", "Switch Phantom Power on channels 1-4")); result &= m_ControlContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_PHANTOM58, 0, "Phantom_5to8", "Phantom 5-8", "Switch Phantom Power on channels 5-8")); result &= m_ControlContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_INSERT1, 0, "Insert1", "Insert 1", "Switch Insert on Channel 1")); result &= m_ControlContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_INSERT2, 0, "Insert2", "Insert 2", "Switch Insert on Channel 2")); result &= m_ControlContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_AC3_PASSTHROUGH, 0, "AC3pass", "AC3 Passtrough", "Enable AC3 Passthrough")); result &= m_ControlContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_MIDI_TRU, 0, "MidiTru", "Midi Tru", "Enable Midi Tru")); result &= m_ControlContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_AVC_MODEL, 0, "ADATDisable", "ADAT Disable", "Disable the ADAT I/O's")); result &= m_ControlContainer->addElement( new BinaryControl(*this, FR_SAFFIREPRO_CMD_ID_AVC_MODEL_MIDI, 0, "MIDIEnable", "MIDI Enable", "Enable the MIDI I/O's")); result &= m_ControlContainer->addElement( new SaffireProDeviceStandaloneEnum(*this, "StandaloneConfig", "Standalone Config", "Choose Standalone Configuration")); result &= m_ControlContainer->addElement( new SaffireProMultiControl(*this, SaffireProMultiControl::eTCT_Reboot, "Reboot", "Reboot", "Reboot Device")); result &= m_ControlContainer->addElement( new SaffireProMultiControl(*this, SaffireProMultiControl::eTCT_FlashLed, "FlashLed", "Flash Led", "Flash power led")); result &= m_ControlContainer->addElement( new SaffireProMultiControl(*this, SaffireProMultiControl::eTCT_UseHighVoltageRail, "UseHighVoltageRail", "Use High Supply", "Prefer the high voltage power supply rail")); result &= m_ControlContainer->addElement( new SaffireProMultiControl(*this, SaffireProMultiControl::eTCT_ExitStandalone, "ExitStandalone", "Exit Standalone mode", "Try to leave standalonbe mode")); result &= m_ControlContainer->addElement( new SaffireProMultiControl(*this, SaffireProMultiControl::eTCT_PllLockRange, "PllLockRange", "PLL Lock Range", "Get/Set PLL Lock range")); result &= m_ControlContainer->addElement( new SaffireProMultiControl(*this, SaffireProMultiControl::eTCT_SaveSettings, "SaveSettings", "Save settings to Flash", "Save the current mixer settings to flash memory")); result &= m_ControlContainer->addElement( new SaffireProMultiControl(*this, SaffireProMultiControl::eTCT_EnableADAT1, "EnableAdat1", "Enable ADAT 1", "Enable/disable ADAT channel 1")); result &= m_ControlContainer->addElement( new SaffireProMultiControl(*this, SaffireProMultiControl::eTCT_EnableADAT2, "EnableAdat2", "Enable ADAT 2", "Enable/disable ADAT channel 2")); result &= m_ControlContainer->addElement( new SaffireProMultiControl(*this, SaffireProMultiControl::eTCT_EnableSPDIF, "EnableSPDIF1", "Enable S/PDIF 1", "Enable/disable S/PDIF channel")); m_deviceNameControl = new SaffireProDeviceNameControl(*this, "DeviceName", "Flash Device Name", "Device name stored in flash memory"); result &= m_ControlContainer->addElement(m_deviceNameControl); // add a direct register access element result &= addElement(new RegisterControl(*this, "Register", "Register Access", "Direct register access")); if (!result) { debugWarning("One or more device control elements could not be created."); // clean up those that couldn't be created destroyMixer(); return false; } if (!addElement(m_ControlContainer)) { debugWarning("Could not register controls to device\n"); // clean up destroyMixer(); return false; } return true; } bool SaffireProDevice::destroyMixer() { debugOutput(DEBUG_LEVEL_VERBOSE, "destroy mixer...\n"); if (m_MixerContainer == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "no mixer to destroy...\n"); return true; } if (!deleteElement(m_MixerContainer)) { debugError("Mixer present but not registered to the avdevice\n"); return false; } // remove and delete (as in free) child control elements m_MixerContainer->clearElements(true); delete m_MixerContainer; m_MixerContainer = NULL; // remove control container if (m_ControlContainer == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "no controls to destroy...\n"); return true; } if (!deleteElement(m_ControlContainer)) { debugError("Controls present but not registered to the avdevice\n"); return false; } // remove and delete (as in free) child control elements m_ControlContainer->clearElements(true); delete m_ControlContainer; m_ControlContainer = NULL; return true; } void SaffireProDevice::updateClockSources() { m_active_clocksource = &m_internal_clocksource; m_internal_clocksource.type = FFADODevice::eCT_Internal; m_internal_clocksource.active = false; m_internal_clocksource.valid = true; m_internal_clocksource.locked = true; m_internal_clocksource.id = FR_SAFFIREPRO_CMD_SYNC_CONFIG_INTERNAL; m_internal_clocksource.slipping = false; m_internal_clocksource.description = "Internal"; m_spdif_clocksource.type = FFADODevice::eCT_SPDIF; m_spdif_clocksource.active = false; m_spdif_clocksource.valid = true; m_spdif_clocksource.locked = false; m_spdif_clocksource.id = FR_SAFFIREPRO_CMD_SYNC_CONFIG_SPDIF; m_spdif_clocksource.slipping = false; m_spdif_clocksource.description = "S/PDIF"; m_wordclock_clocksource.type = FFADODevice::eCT_WordClock; m_wordclock_clocksource.active = false; m_wordclock_clocksource.valid = true; m_wordclock_clocksource.locked = false; m_wordclock_clocksource.id = FR_SAFFIREPRO_CMD_SYNC_CONFIG_WORDCLOCK; m_wordclock_clocksource.slipping = false; m_wordclock_clocksource.description = "WordClock"; if(isPro26()) { m_adat1_clocksource.type = FFADODevice::eCT_ADAT; m_adat1_clocksource.active = false; m_adat1_clocksource.valid = true; m_adat1_clocksource.locked = false; m_adat1_clocksource.id = FR_SAFFIREPRO_CMD_SYNC_CONFIG_ADAT1; m_adat1_clocksource.slipping = false; m_adat1_clocksource.description = "ADAT 1"; m_adat2_clocksource.type = FFADODevice::eCT_ADAT; m_adat2_clocksource.active = false; m_adat2_clocksource.valid = true; m_adat2_clocksource.locked = false; m_adat2_clocksource.id = FR_SAFFIREPRO_CMD_SYNC_CONFIG_ADAT2; m_adat2_clocksource.slipping = false; m_adat2_clocksource.description = "ADAT 2"; } // figure out the active source uint32_t sync; if ( !getSpecificValue(FR_SAFFIREPRO_CMD_ID_SYNC_CONFIG, &sync ) ){ debugError( "getSpecificValue failed\n" ); m_internal_clocksource.active=true; return; } debugOutput(DEBUG_LEVEL_VERBOSE, "SYNC_CONFIG field value: %08X\n", sync ); switch(sync & FR_SAFFIREPRO_CMD_ID_SYNC_CONFIG_MASK) { default: debugWarning( "Unexpected SYNC_CONFIG field value: %08X\n", sync ); case FR_SAFFIREPRO_CMD_SYNC_CONFIG_INTERNAL: m_internal_clocksource.active=true; m_active_clocksource = &m_internal_clocksource; break; case FR_SAFFIREPRO_CMD_SYNC_CONFIG_SPDIF: m_spdif_clocksource.active=true; m_active_clocksource = &m_spdif_clocksource; break; case FR_SAFFIREPRO_CMD_SYNC_CONFIG_ADAT1: m_adat1_clocksource.active=true; m_active_clocksource = &m_adat1_clocksource; break; case FR_SAFFIREPRO_CMD_SYNC_CONFIG_ADAT2: m_adat2_clocksource.active=true; m_active_clocksource = &m_adat2_clocksource; break; case FR_SAFFIREPRO_CMD_SYNC_CONFIG_WORDCLOCK: m_wordclock_clocksource.active=true; m_active_clocksource = &m_wordclock_clocksource; break; } switch((sync & FR_SAFFIREPRO_CMD_ID_SYNC_LOCK_MASK) >> 8) { case FR_SAFFIREPRO_CMD_SYNC_CONFIG_INTERNAL: // always locked break; case FR_SAFFIREPRO_CMD_SYNC_CONFIG_SPDIF: m_spdif_clocksource.locked=true; break; case FR_SAFFIREPRO_CMD_SYNC_CONFIG_ADAT1: m_adat1_clocksource.locked=true; break; case FR_SAFFIREPRO_CMD_SYNC_CONFIG_ADAT2: m_adat2_clocksource.locked=true; break; case FR_SAFFIREPRO_CMD_SYNC_CONFIG_WORDCLOCK: m_wordclock_clocksource.locked=true; break; default: debugWarning( "Unexpected SYNC_CONFIG_STATE field value: %08X\n", sync ); } } FFADODevice::ClockSource SaffireProDevice::getActiveClockSource() { updateClockSources(); return *m_active_clocksource; } bool SaffireProDevice::setActiveClockSource(ClockSource s) { // prevent busresets from being handled immediately getDeviceManager().lockBusResetHandler(); unsigned int gen_before = get1394Service().getGeneration(); debugOutput(DEBUG_LEVEL_VERBOSE, "set active source to %d...\n", s.id); if ( !setSpecificValue(FR_SAFFIREPRO_CMD_ID_SYNC_CONFIG, s.id ) ){ debugError( "setSpecificValue failed\n" ); getDeviceManager().unlockBusResetHandler(); return false; } // the device can do a bus reset at this moment Util::SystemTimeSource::SleepUsecRelative(1000 * 1000); if(!get1394Service().waitForBusResetStormToEnd(10, 2000)) { debugWarning("Device doesn't stop bus-resetting\n"); } unsigned int gen_after = get1394Service().getGeneration(); debugOutput(DEBUG_LEVEL_VERBOSE, " gen: %d=>%d\n", gen_before, gen_after); getDeviceManager().unlockBusResetHandler(); return true; } FFADODevice::ClockSourceVector SaffireProDevice::getSupportedClockSources() { debugOutput(DEBUG_LEVEL_VERBOSE, "listing...\n"); FFADODevice::ClockSourceVector r; r.push_back(m_internal_clocksource); r.push_back(m_spdif_clocksource); r.push_back(m_wordclock_clocksource); if(isPro26()) { r.push_back(m_adat1_clocksource); r.push_back(m_adat2_clocksource); } return r; } std::vector SaffireProDevice::getSupportedSamplingFrequencies() { std::vector frequencies; frequencies.push_back(44100); frequencies.push_back(48000); frequencies.push_back(88200); frequencies.push_back(96000); frequencies.push_back(176400); frequencies.push_back(192000); return frequencies; } uint16_t SaffireProDevice::getConfigurationIdSyncMode() { uint32_t sync; if ( !getSpecificValue(FR_SAFFIREPRO_CMD_ID_SYNC_CONFIG, &sync ) ){ debugError( "getSpecificValue failed\n" ); return 0xFFFF; } return sync & 0xFFFF; } uint64_t SaffireProDevice::getConfigurationId() { // have the generic mechanism create a unique configuration id. uint64_t id = BeBoB::Device::getConfigurationId(); // there are some parts that can be enabled/disabled and // that have influence on the AV/C model and channel config // so add them to the config id #if 0 // FIXME: doesn't seem to be working, but the channel count // makes that it's not that important if(getEnableDigitalChannel(eDC_SPDIF)) { id |= 1ULL << 40; } if(isPro26()) { if(getEnableDigitalChannel(eDC_ADAT1)) { id |= 1ULL << 41; } if(getEnableDigitalChannel(eDC_ADAT2)) { id |= 1ULL << 42; } } #endif return id; } bool SaffireProDevice::setNickname( std::string name) { if(m_deviceNameControl) { return m_deviceNameControl->setValue(name); } else return false; } std::string SaffireProDevice::getNickname() { if(m_deviceNameControl) { return m_deviceNameControl->getValue(); } else return "Unknown"; } bool SaffireProDevice::canChangeNickname() { return true; } void SaffireProDevice::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a BeBoB::Focusrite::SaffireProDevice\n"); FocusriteDevice::showDevice(); } void SaffireProDevice::setVerboseLevel(int l) { debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); if (m_MixerContainer) m_MixerContainer->setVerboseLevel(l); // FIXME: add the other elements here too FocusriteDevice::setVerboseLevel(l); } int SaffireProDevice::getSamplingFrequency( ) { uint32_t sr; if ( !getSpecificValue(FR_SAFFIREPRO_CMD_ID_SAMPLERATE, &sr ) ) { debugError( "getSpecificValue failed\n" ); return 0; } debugOutput( DEBUG_LEVEL_VERBOSE, "getSampleRate: %d\n", sr ); return convertDefToSr(sr); } bool SaffireProDevice::setSamplingFrequencyDo( uint32_t value ) { if ( !setSpecificValue(FR_SAFFIREPRO_CMD_ID_SAMPLERATE, value) ) { debugError( "setSpecificValue failed\n" ); return false; } return true; } bool SaffireProDevice::setSamplingFrequencyDoNoReboot( uint32_t value ) { if ( !setSpecificValue(FR_SAFFIREPRO_CMD_ID_SAMPLERATE_NOREBOOT, value) ) { debugError( "setSpecificValue failed\n" ); return false; } return true; } bool SaffireProDevice::setSamplingFrequency( int s ) { bool snoopMode=false; if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } bool rebootOnSamplerateChange=false; if(!getOption("rebootOnSamplerateChange", rebootOnSamplerateChange)) { debugWarning("Could not retrieve rebootOnSamplerateChange parameter, defauling to false\n"); } if(snoopMode) { if (s != getSamplingFrequency()) { debugError("In snoop mode it is impossible to set the sample rate.\n"); debugError("Please start the client with the correct setting.\n"); return false; } return true; } else { uint32_t value = convertSrToDef(s); if ( value == 0 ) { debugError("Unsupported samplerate: %u\n", s); return false; } if (s == getSamplingFrequency()) { debugOutput( DEBUG_LEVEL_VERBOSE, "No need to change samplerate\n"); return true; } const int max_tries = 2; int ntries = max_tries+1; // the device behaves like a pig when changing samplerate, // generating a bunch of bus-resets. // we don't want the busreset handler to run while we are // changing the samplerate. however it has to run after the // device finished, since the bus resets might have influenced // other attached devices. getDeviceManager().lockBusResetHandler(); unsigned int gen_before = get1394Service().getGeneration(); while(--ntries) { if (rebootOnSamplerateChange) { debugOutput( DEBUG_LEVEL_VERBOSE, "Setting samplerate with reboot\n"); if(!setSamplingFrequencyDo( value )) { debugWarning("setSamplingFrequencyDo failed\n"); } debugOutput( DEBUG_LEVEL_VERBOSE, "Waiting for device to finish rebooting...\n"); // the device needs quite some time to reboot Util::SystemTimeSource::SleepUsecRelative(2 * 1000 * 1000); int timeout = 5; // multiples of 1s // wait for a busreset to occur while ((gen_before == get1394Service().getGeneration()) && --timeout) { // wait for a while Util::SystemTimeSource::SleepUsecRelative(1000 * 1000); } if (!timeout) { debugOutput(DEBUG_LEVEL_VERBOSE, "Device did not reset itself, forcing reboot...\n"); rebootDevice(); // the device needs quite some time to reboot Util::SystemTimeSource::SleepUsecRelative(6 * 1000 * 1000); // wait for the device to finish the reboot timeout = 10; // multiples of 1s while ((gen_before == get1394Service().getGeneration()) && --timeout) { // wait for a while Util::SystemTimeSource::SleepUsecRelative(1000 * 1000); } if (!timeout) { debugError( "Device did not reset itself after forced reboot...\n"); getDeviceManager().unlockBusResetHandler(); return false; } } // so we know the device is rebooting // now wait until it stops generating busresets if(!get1394Service().waitForBusResetStormToEnd(20, 4000)) { debugError("The device keeps behaving like a pig...\n"); getDeviceManager().unlockBusResetHandler(); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, "Device available (gen: %u => %u)...\n", gen_before, get1394Service().getGeneration()); // wait some more Util::SystemTimeSource::SleepUsecRelative(1 * 1000 * 1000); // update the generation of the 1394 service get1394Service().updateGeneration(); // update our config rom since it might have changed // if this fails it means we have disappeared from the bus // that's bad. if(!getConfigRom().updatedNodeId()) { debugError("Could not update node id\n"); getDeviceManager().unlockBusResetHandler(); return false; } // we have to rediscover the device if (discover()) break; } else { debugOutput( DEBUG_LEVEL_VERBOSE, "Setting samplerate without reboot\n"); if(!setSamplingFrequencyDoNoReboot( value )) { debugWarning("setSamplingFrequencyDoNoReboot failed\n"); } } int verify = getSamplingFrequency(); debugOutput( DEBUG_LEVEL_VERBOSE, "setSampleRate (try %d): requested samplerate %d, device now has %d\n", max_tries-ntries, s, verify ); if (s == verify) { break; } debugOutput( DEBUG_LEVEL_VERBOSE, "setSampleRate (try %d) failed. Try again...\n", ntries); } // make the busreset handlers run getDeviceManager().unlockBusResetHandler(); if (ntries==0) { debugError("Setting samplerate failed...\n"); return false; } return true; } // not executable return false; } void SaffireProDevice::rebootDevice() { debugOutput( DEBUG_LEVEL_VERBOSE, "rebooting device...\n" ); if ( !setSpecificValue(FR_SAFFIREPRO_CMD_ID_REBOOT, FR_SAFFIREPRO_CMD_REBOOT_CODE ) ) { debugError( "setSpecificValue failed\n" ); } } void SaffireProDevice::exitStandalone() { debugOutput( DEBUG_LEVEL_VERBOSE, "exit standalone mode...\n" ); if ( !setSpecificValue(FR_SAFFIREPRO_CMD_ID_EXIT_STANDALONE, FR_SAFFIREPRO_CMD_EXIT_STANDALONE_CODE ) ) { debugError( "setSpecificValue failed\n" ); } } void SaffireProDevice::saveSettings() { debugOutput( DEBUG_LEVEL_VERBOSE, "saving settings on device...\n" ); if ( !setSpecificValue(FR_SAFFIREPRO_CMD_ID_SAVE_SETTINGS, FR_SAFFIREPRO_CMD_REBOOT_CODE ) ) { // FIXME: is this correct? debugError( "setSpecificValue failed\n" ); } } void SaffireProDevice::flashLed() { int ledFlashDuration = 2; if(!getOption("ledFlashDuration", ledFlashDuration)) { debugOutput( DEBUG_LEVEL_VERBOSE, "Could not retrieve ledFlashDuration parameter, defaulting to 2sec\n"); } int ledFlashFrequency = 10; if(!getOption("ledFlashFrequency", ledFlashFrequency)) { debugOutput( DEBUG_LEVEL_VERBOSE, "Could not retrieve ledFlashFrequency parameter, defaulting to 10Hz\n"); } uint32_t reg = 0; debugOutput( DEBUG_LEVEL_VERBOSE, "flashing led ...\n" ); reg = FR_SAFFIREPRO_CMD_SET_FLASH_SECS(reg, ledFlashDuration); reg = FR_SAFFIREPRO_CMD_SET_FLASH_FREQ(reg, ledFlashFrequency); if ( !setSpecificValue(FR_SAFFIREPRO_CMD_ID_FLASH_LED, reg ) ) { debugError( "setSpecificValue failed\n" ); } } bool SaffireProDevice::isAudioOn() { uint32_t ready; if ( !getSpecificValue(FR_SAFFIREPRO_CMD_ID_AUDIO_ON, &ready ) ) { debugError( "getSpecificValue failed\n" ); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "isAudioOn: %d\n", ready!=0 ); return ready != 0; } bool SaffireProDevice::isExtClockLocked() { uint32_t ready; if ( !getSpecificValue(FR_SAFFIREPRO_CMD_ID_EXT_CLOCK_LOCK, &ready ) ) { debugError( "getSpecificValue failed\n" ); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "isExtClockLocked: %d\n", ready!=0 ); return ready != 0; } uint32_t SaffireProDevice::getCount32() { uint32_t v; if ( !getSpecificValue(FR_SAFFIREPRO_CMD_ID_PLAYBACK_COUNT, &v ) ) { debugError( "getSpecificValue failed\n" ); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "getCount32: %08X\n", v ); return v; } void SaffireProDevice::useHighVoltageRail(bool useIt) { uint32_t reg=useIt; debugOutput( DEBUG_LEVEL_VERBOSE, "%s high voltage rail ...\n", (useIt?"Using":"Not using") ); if ( !setSpecificValue(FR_SAFFIREPRO_CMD_ID_USE_HIGHVOLTAGE_RAIL, reg ) ) { debugError( "setSpecificValue failed\n" ); } } bool SaffireProDevice::usingHighVoltageRail() { uint32_t retval; if ( !getSpecificValue(FR_SAFFIREPRO_CMD_ID_USING_HIGHVOLTAGE_RAIL, &retval ) ) { debugError( "getSpecificValue failed\n" ); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "usingHighVoltageRail: %d\n", retval!=0 ); return retval != 0; } void SaffireProDevice::setPllLockRange(unsigned int i) { uint32_t reg=i; debugOutput( DEBUG_LEVEL_VERBOSE, "set PLL lock range: %d ...\n", i ); if ( !setSpecificValue(FR_SAFFIREPRO_CMD_ID_PLL_LOCK_RANGE, reg ) ) { debugError( "setSpecificValue failed\n" ); } } unsigned int SaffireProDevice::getPllLockRange() { uint32_t retval; if ( !getSpecificValue(FR_SAFFIREPRO_CMD_ID_PLL_LOCK_RANGE, &retval ) ) { debugError( "getSpecificValue failed\n" ); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "PLL lock range: %d\n", retval ); return retval; } bool SaffireProDevice::isMidiEnabled() { uint32_t ready; if ( !getSpecificValue(FR_SAFFIREPRO_CMD_ID_AVC_MODEL_MIDI, &ready ) ) { debugError( "getSpecificValue failed\n" ); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "isMidiEnabled: %d\n", ready != 0 ); return ready != 0; } unsigned int SaffireProDevice::getEnableDigitalChannel(enum eDigitalChannel c) { uint32_t retval; unsigned int id; switch(c) { default: case eDC_ADAT1: id=FR_SAFFIREPRO_CMD_ID_ENABLE_ADAT1_INPUT; break; case eDC_ADAT2: id=FR_SAFFIREPRO_CMD_ID_ENABLE_ADAT2_INPUT; break; case eDC_SPDIF: id=FR_SAFFIREPRO_CMD_ID_ENABLE_SPDIF_INPUT; break; } if ( !getSpecificValue(id, &retval ) ) { debugError( "getSpecificValue failed\n" ); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "get dig channel %d: %d\n", c, retval); return retval; } void SaffireProDevice::setEnableDigitalChannel(enum eDigitalChannel c, unsigned int i) { uint32_t reg=i; unsigned int id; switch(c) { default: case eDC_ADAT1: id=FR_SAFFIREPRO_CMD_ID_ENABLE_ADAT1_INPUT; break; case eDC_ADAT2: id=FR_SAFFIREPRO_CMD_ID_ENABLE_ADAT2_INPUT; break; case eDC_SPDIF: id=FR_SAFFIREPRO_CMD_ID_ENABLE_SPDIF_INPUT; break; } debugOutput( DEBUG_LEVEL_VERBOSE, "set dig channel %d: %d...\n", c, i ); if ( !setSpecificValue(id, reg ) ) { debugError( "setSpecificValue failed\n" ); } } bool SaffireProDevice::setDeviceName(std::string n) { debugOutput( DEBUG_LEVEL_VERBOSE, "set device name : %s ...\n", n.c_str() ); uint32_t tmp; char name[16]; // the device name field length is fixed memset(name, 0, 16); unsigned int nb_chars = n.size(); if(nb_chars > 16) { debugWarning("Specified name too long: %s\n", n.c_str()); nb_chars = 16; } unsigned int i; for(i=0; i1) { debugError("Index (%d) out of range\n", idx); return false; } if(!m_Parent.setSpecificValue(FR_SAFFIREPRO_CMD_ID_STANDALONE_MODE, idx)) { debugError("Could not set selected mode\n"); return false; } else { return true; } } int SaffireProDeviceStandaloneEnum::selected() { uint32_t sel=0; if(!m_Parent.getSpecificValue(FR_SAFFIREPRO_CMD_ID_STANDALONE_MODE, &sel)) { debugError("Could not get selected mode\n"); return -1; } else { return sel; } } int SaffireProDeviceStandaloneEnum::count() { return 2; } std::string SaffireProDeviceStandaloneEnum::getEnumLabel(int idx) { switch(idx) { case 0: return "Mixing"; case 1: return "Tracking"; default: debugError("Index (%d) out of range\n", idx); return "Error"; } } // Saffire pro matrix mixer element SaffireProMatrixMixer::SaffireProMatrixMixer(SaffireProDevice& p, enum eMatrixMixerType type) : FocusriteMatrixMixer(p, "MatrixMixer") , m_type(type) { init(); } SaffireProMatrixMixer::SaffireProMatrixMixer(SaffireProDevice& p, enum eMatrixMixerType type, std::string n) : FocusriteMatrixMixer(p, n) , m_type(type) { init(); } void SaffireProMatrixMixer::init() { if (m_type==eMMT_OutputMix) { addSignalInfo(m_RowInfo, "PC1", "PC 1", "PC Channel 1"); addSignalInfo(m_RowInfo, "PC2", "PC 2", "PC Channel 2"); addSignalInfo(m_RowInfo, "PC3", "PC 3", "PC Channel 3"); addSignalInfo(m_RowInfo, "PC4", "PC 4", "PC Channel 4"); addSignalInfo(m_RowInfo, "PC5", "PC 5", "PC Channel 5"); addSignalInfo(m_RowInfo, "PC6", "PC 6", "PC Channel 6"); addSignalInfo(m_RowInfo, "PC7", "PC 7", "PC Channel 7"); addSignalInfo(m_RowInfo, "PC8", "PC 8", "PC Channel 8"); addSignalInfo(m_RowInfo, "PC9", "PC 9", "PC Channel 9"); addSignalInfo(m_RowInfo, "PC10", "PC 10", "PC Channel 10"); addSignalInfo(m_RowInfo, "IMIXL", "IMix L", "Input Mix Left"); addSignalInfo(m_RowInfo, "IMIXR", "IMix R", "Input Mix Right"); addSignalInfo(m_ColInfo, "OUT1", "OUT 1", "Output Channel 1"); addSignalInfo(m_ColInfo, "OUT2", "OUT 2", "Output Channel 2"); addSignalInfo(m_ColInfo, "OUT3", "OUT 3", "Output Channel 3"); addSignalInfo(m_ColInfo, "OUT4", "OUT 4", "Output Channel 4"); addSignalInfo(m_ColInfo, "OUT5", "OUT 5", "Output Channel 5"); addSignalInfo(m_ColInfo, "OUT6", "OUT 6", "Output Channel 6"); addSignalInfo(m_ColInfo, "OUT7", "OUT 7", "Output Channel 7"); addSignalInfo(m_ColInfo, "OUT8", "OUT 8", "Output Channel 8"); addSignalInfo(m_ColInfo, "OUT9", "OUT 9", "Output Channel 9"); addSignalInfo(m_ColInfo, "OUT10", "OUT 10", "Output Channel 10"); // init the cell matrix #define FOCUSRITE_SAFFIRE_PRO_OUTMIX_NB_COLS 10 #define FOCUSRITE_SAFFIRE_PRO_OUTMIX_NB_ROWS 12 std::vector tmp_cols( FOCUSRITE_SAFFIRE_PRO_OUTMIX_NB_COLS ); std::vector< std::vector > tmp_all(FOCUSRITE_SAFFIRE_PRO_OUTMIX_NB_ROWS, tmp_cols); m_CellInfo = tmp_all; struct sCellInfo c; c.row=-1; c.col=-1; c.valid=false; c.address=0; for (int i=0;i tmp_cols( FOCUSRITE_SAFFIRE_PRO_INMIX_NB_COLS ); std::vector< std::vector > tmp_all(FOCUSRITE_SAFFIRE_PRO_INMIX_NB_ROWS,tmp_cols); m_CellInfo = tmp_all; struct sCellInfo c; c.row=-1; c.col=-1; c.valid=false; c.address=0; for (int i=0;i. * */ #ifndef BEBOB_FOCUSRITE_SAFFIRE_PRO_DEVICE_H #define BEBOB_FOCUSRITE_SAFFIRE_PRO_DEVICE_H #include "debugmodule/debugmodule.h" #include "focusrite_generic.h" #include "libcontrol/BasicElements.h" #include #include // MIXER ID's #define FR_SAFFIREPRO_CMD_ID_AN1_TO_IMIXL 0 #define FR_SAFFIREPRO_CMD_ID_AN1_TO_IMIXR 1 #define FR_SAFFIREPRO_CMD_ID_AN2_TO_IMIXL 2 #define FR_SAFFIREPRO_CMD_ID_AN2_TO_IMIXR 3 #define FR_SAFFIREPRO_CMD_ID_AN3_TO_IMIXL 4 #define FR_SAFFIREPRO_CMD_ID_AN3_TO_IMIXR 5 #define FR_SAFFIREPRO_CMD_ID_AN4_TO_IMIXL 6 #define FR_SAFFIREPRO_CMD_ID_AN4_TO_IMIXR 7 #define FR_SAFFIREPRO_CMD_ID_AN5_TO_IMIXL 8 #define FR_SAFFIREPRO_CMD_ID_AN5_TO_IMIXR 9 #define FR_SAFFIREPRO_CMD_ID_AN6_TO_IMIXL 10 #define FR_SAFFIREPRO_CMD_ID_AN6_TO_IMIXR 11 #define FR_SAFFIREPRO_CMD_ID_AN7_TO_IMIXL 12 #define FR_SAFFIREPRO_CMD_ID_AN7_TO_IMIXR 13 #define FR_SAFFIREPRO_CMD_ID_AN8_TO_IMIXL 14 #define FR_SAFFIREPRO_CMD_ID_AN8_TO_IMIXR 15 #define FR_SAFFIREPRO_CMD_ID_SPDIFL_TO_IMIXL 16 #define FR_SAFFIREPRO_CMD_ID_SPDIFL_TO_IMIXR 17 #define FR_SAFFIREPRO_CMD_ID_SPDIFR_TO_IMIXL 18 #define FR_SAFFIREPRO_CMD_ID_SPDIFR_TO_IMIXR 19 #define FR_SAFFIREPRO_CMD_ID_ADAT11_TO_IMIXL 20 #define FR_SAFFIREPRO_CMD_ID_ADAT11_TO_IMIXR 21 #define FR_SAFFIREPRO_CMD_ID_ADAT12_TO_IMIXL 22 #define FR_SAFFIREPRO_CMD_ID_ADAT12_TO_IMIXR 23 #define FR_SAFFIREPRO_CMD_ID_ADAT13_TO_IMIXL 24 #define FR_SAFFIREPRO_CMD_ID_ADAT13_TO_IMIXR 25 #define FR_SAFFIREPRO_CMD_ID_ADAT14_TO_IMIXL 26 #define FR_SAFFIREPRO_CMD_ID_ADAT14_TO_IMIXR 27 #define FR_SAFFIREPRO_CMD_ID_ADAT15_TO_IMIXL 28 #define FR_SAFFIREPRO_CMD_ID_ADAT15_TO_IMIXR 29 #define FR_SAFFIREPRO_CMD_ID_ADAT16_TO_IMIXL 30 #define FR_SAFFIREPRO_CMD_ID_ADAT16_TO_IMIXR 31 #define FR_SAFFIREPRO_CMD_ID_ADAT17_TO_IMIXL 32 #define FR_SAFFIREPRO_CMD_ID_ADAT17_TO_IMIXR 33 #define FR_SAFFIREPRO_CMD_ID_ADAT18_TO_IMIXL 34 #define FR_SAFFIREPRO_CMD_ID_ADAT18_TO_IMIXR 35 #define FR_SAFFIREPRO_CMD_ID_ADAT21_TO_IMIXL 36 #define FR_SAFFIREPRO_CMD_ID_ADAT21_TO_IMIXR 37 #define FR_SAFFIREPRO_CMD_ID_ADAT22_TO_IMIXL 38 #define FR_SAFFIREPRO_CMD_ID_ADAT22_TO_IMIXR 39 #define FR_SAFFIREPRO_CMD_ID_ADAT23_TO_IMIXL 40 #define FR_SAFFIREPRO_CMD_ID_ADAT23_TO_IMIXR 41 #define FR_SAFFIREPRO_CMD_ID_ADAT24_TO_IMIXL 42 #define FR_SAFFIREPRO_CMD_ID_ADAT24_TO_IMIXR 43 #define FR_SAFFIREPRO_CMD_ID_ADAT25_TO_IMIXL 44 #define FR_SAFFIREPRO_CMD_ID_ADAT25_TO_IMIXR 45 #define FR_SAFFIREPRO_CMD_ID_ADAT26_TO_IMIXL 46 #define FR_SAFFIREPRO_CMD_ID_ADAT26_TO_IMIXR 47 #define FR_SAFFIREPRO_CMD_ID_ADAT27_TO_IMIXL 48 #define FR_SAFFIREPRO_CMD_ID_ADAT27_TO_IMIXR 49 #define FR_SAFFIREPRO_CMD_ID_ADAT28_TO_IMIXL 50 #define FR_SAFFIREPRO_CMD_ID_ADAT28_TO_IMIXR 51 #define FR_SAFFIREPRO_CMD_ID_PC1_TO_OUT1 52 #define FR_SAFFIREPRO_CMD_ID_PC2_TO_OUT2 54 #define FR_SAFFIREPRO_CMD_ID_MIX1_TO_OUT1 53 #define FR_SAFFIREPRO_CMD_ID_MIX2_TO_OUT2 55 #define FR_SAFFIREPRO_CMD_ID_PC1_TO_OUT3 56 #define FR_SAFFIREPRO_CMD_ID_PC2_TO_OUT4 59 #define FR_SAFFIREPRO_CMD_ID_PC3_TO_OUT3 57 #define FR_SAFFIREPRO_CMD_ID_PC4_TO_OUT4 60 #define FR_SAFFIREPRO_CMD_ID_MIX1_TO_OUT3 58 #define FR_SAFFIREPRO_CMD_ID_MIX2_TO_OUT4 61 #define FR_SAFFIREPRO_CMD_ID_PC1_TO_OUT5 62 #define FR_SAFFIREPRO_CMD_ID_PC2_TO_OUT6 65 #define FR_SAFFIREPRO_CMD_ID_PC5_TO_OUT5 63 #define FR_SAFFIREPRO_CMD_ID_PC6_TO_OUT6 66 #define FR_SAFFIREPRO_CMD_ID_MIX1_TO_OUT5 64 #define FR_SAFFIREPRO_CMD_ID_MIX2_TO_OUT6 67 #define FR_SAFFIREPRO_CMD_ID_PC1_TO_OUT7 68 #define FR_SAFFIREPRO_CMD_ID_PC2_TO_OUT8 71 #define FR_SAFFIREPRO_CMD_ID_PC7_TO_OUT7 69 #define FR_SAFFIREPRO_CMD_ID_PC8_TO_OUT8 72 #define FR_SAFFIREPRO_CMD_ID_MIX1_TO_OUT7 70 #define FR_SAFFIREPRO_CMD_ID_MIX2_TO_OUT8 73 #define FR_SAFFIREPRO_CMD_ID_PC1_TO_OUT9 74 #define FR_SAFFIREPRO_CMD_ID_PC2_TO_OUT10 77 #define FR_SAFFIREPRO_CMD_ID_PC9_TO_OUT9 75 #define FR_SAFFIREPRO_CMD_ID_PC10_TO_OUT10 78 #define FR_SAFFIREPRO_CMD_ID_MIX1_TO_OUT9 76 #define FR_SAFFIREPRO_CMD_ID_MIX2_TO_OUT10 79 // SAMPLERATE related ID's #define FR_SAFFIREPRO_CMD_ID_SAMPLERATE 84 #define FR_SAFFIREPRO_CMD_ID_SAMPLERATE_NOREBOOT 115 #define FOCUSRITE_CMD_SAMPLERATE_44K1 1 #define FOCUSRITE_CMD_SAMPLERATE_48K 2 #define FOCUSRITE_CMD_SAMPLERATE_88K2 3 #define FOCUSRITE_CMD_SAMPLERATE_96K 4 #define FOCUSRITE_CMD_SAMPLERATE_176K4 5 #define FOCUSRITE_CMD_SAMPLERATE_192K 6 // OTHER CONFIG id's #define FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT12 80 #define FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT34 81 #define FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT56 82 #define FR_SAFFIREPRO_CMD_ID_BITFIELD_OUT78 83 #define FR_SAFFIREPRO_CMD_BITFIELD_BIT_MUTE 24 #define FR_SAFFIREPRO_CMD_BITFIELD_BIT_DAC_IGNORE 25 #define FR_SAFFIREPRO_CMD_BITFIELD_BIT_HWCTRL 26 #define FR_SAFFIREPRO_CMD_BITFIELD_BIT_PAD 27 #define FR_SAFFIREPRO_CMD_BITFIELD_BIT_DIM 28 #define FR_SAFFIREPRO_CMD_ID_SWITCH_CONFIG 85 #define FR_SAFFIREPRO_CMD_SWITCH_CONFIG_DIM12 (1<<0) #define FR_SAFFIREPRO_CMD_SWITCH_CONFIG_DIM34 (1<<1) #define FR_SAFFIREPRO_CMD_SWITCH_CONFIG_DIM56 (1<<2) #define FR_SAFFIREPRO_CMD_SWITCH_CONFIG_DIM78 (1<<3) #define FR_SAFFIREPRO_CMD_SWITCH_CONFIG_MUTE12 (1<<4) #define FR_SAFFIREPRO_CMD_SWITCH_CONFIG_MUTE34 (1<<5) #define FR_SAFFIREPRO_CMD_SWITCH_CONFIG_MUTE56 (1<<6) #define FR_SAFFIREPRO_CMD_SWITCH_CONFIG_MUTE78 (1<<7) #define FR_SAFFIREPRO_CMD_SWITCH_CONFIG_MUTE910 (1<<8) #define FR_SAFFIREPRO_CMD_ID_MONITOR_DIAL 86 #define FR_SAFFIREPRO_CMD_ID_DIM_INDICATOR 87 #define FR_SAFFIREPRO_CMD_ID_MUTE_INDICATOR 88 #define FR_SAFFIREPRO_CMD_ID_EXT_CLOCK_LOCK 89 #define FR_SAFFIREPRO_CMD_ID_AUDIO_ON 90 #define FR_SAFFIREPRO_CMD_ID_USE_HIGHVOLTAGE_RAIL 91 #define FR_SAFFIREPRO_CMD_ID_USING_HIGHVOLTAGE_RAIL 92 #define FR_SAFFIREPRO_CMD_ID_SYNC_CONFIG_MASK 0x000000FF #define FR_SAFFIREPRO_CMD_ID_SYNC_LOCK_MASK 0x0000FF00 #define FR_SAFFIREPRO_CMD_ID_SYNC_CONFIG 93 #define FR_SAFFIREPRO_CMD_SYNC_CONFIG_INTERNAL 0 #define FR_SAFFIREPRO_CMD_SYNC_CONFIG_SPDIF 2 #define FR_SAFFIREPRO_CMD_SYNC_CONFIG_ADAT1 3 #define FR_SAFFIREPRO_CMD_SYNC_CONFIG_ADAT2 4 #define FR_SAFFIREPRO_CMD_SYNC_CONFIG_WORDCLOCK 5 #define FR_SAFFIREPRO_CMD_ID_DEVICE_NAME_1 94 #define FR_SAFFIREPRO_CMD_ID_DEVICE_NAME_2 95 #define FR_SAFFIREPRO_CMD_ID_DEVICE_NAME_3 96 #define FR_SAFFIREPRO_CMD_ID_DEVICE_NAME_4 97 #define FR_SAFFIREPRO_CMD_ID_PHANTOM14 98 #define FR_SAFFIREPRO_CMD_ID_PHANTOM58 99 #define FR_SAFFIREPRO_CMD_ID_INSERT1 100 #define FR_SAFFIREPRO_CMD_ID_INSERT2 101 // led flashing #define FR_SAFFIREPRO_CMD_ID_FLASH_LED 102 #define FR_SAFFIREPRO_CMD_FLASH_MASK_SECS 0x00FF #define FR_SAFFIREPRO_CMD_FLASH_MASK_FREQ 0xFF00 #define FR_SAFFIREPRO_CMD_SET_FLASH_SECS(reg, secs) \ (((reg) & ~FR_SAFFIREPRO_CMD_FLASH_MASK_SECS) | \ (((secs) & 0xFF))) #define FR_SAFFIREPRO_CMD_GET_FLASH_SECS(reg) \ ((reg) & FR_SAFFIREPRO_CMD_FLASH_MASK_SECS) #define FR_SAFFIREPRO_CMD_SET_FLASH_FREQ(reg, freq) \ (((reg) & ~FR_SAFFIREPRO_CMD_FLASH_MASK_FREQ) | \ (((freq) & 0xFF) << 8)) #define FR_SAFFIREPRO_CMD_GET_FLASH_FREQ(reg) \ ((reg) & FR_SAFFIREPRO_CMD_FLASH_MASK_FREQ) #define FR_SAFFIREPRO_CMD_ID_AC3_PASSTHROUGH 103 #define FR_SAFFIREPRO_CMD_ID_MIDI_TRU 104 #define FR_SAFFIREPRO_CMD_ID_ENABLE_SPDIF_INPUT 105 #define FR_SAFFIREPRO_CMD_ID_ENABLE_ADAT1_INPUT 106 #define FR_SAFFIREPRO_CMD_ID_ENABLE_ADAT2_INPUT 107 #define FR_SAFFIREPRO_CMD_ID_SAVE_SETTINGS 108 #define FR_SAFFIREPRO_CMD_ID_REBOOT 109 #define FR_SAFFIREPRO_CMD_REBOOT_CODE 0xA5A5 #define FR_SAFFIREPRO_CMD_ID_PLAYBACK_COUNT 110 #define FR_SAFFIREPRO_CMD_PLAYBACK_COUNT_REC_OK 0 #define FR_SAFFIREPRO_CMD_PLAYBACK_COUNT_PBK_OK 1 #define FR_SAFFIREPRO_CMD_PLAYBACK_COUNT_RX_READY 2 #define FR_SAFFIREPRO_CMD_PLAYBACK_COUNT_STANDALONE 3 #define FR_SAFFIREPRO_CMD_PLAYBACK_COUNT_SPDIFIN_ON (1<<8) #define FR_SAFFIREPRO_CMD_PLAYBACK_COUNT_SPDIFIN_ERR (1<<9) #define FR_SAFFIREPRO_CMD_PLAYBACK_COUNT_ADAT1IN_ON (1<<16) #define FR_SAFFIREPRO_CMD_PLAYBACK_COUNT_ADAT1IN_ERR (1<<17) #define FR_SAFFIREPRO_CMD_PLAYBACK_COUNT_ADAT2IN_ON (1<<24) #define FR_SAFFIREPRO_CMD_PLAYBACK_COUNT_ADAT2IN_ERR (1<<25) #define FR_SAFFIREPRO_CMD_ID_STANDALONE_MODE 111 #define FR_SAFFIREPRO_CMD_ID_AVC_MODEL 112 #define FR_SAFFIREPRO_CMD_AVC_MODEL_LARGE 0 #define FR_SAFFIREPRO_CMD_AVC_MODEL_SMALL 1 #define FR_SAFFIREPRO_CMD_ID_PLL_LOCK_RANGE 113 #define FR_SAFFIREPRO_CMD_ID_EXIT_STANDALONE 114 #define FR_SAFFIREPRO_CMD_EXIT_STANDALONE_CODE 0xA5A5 #define FR_SAFFIREPRO_CMD_ID_AVC_MODEL_MIDI 116 #define FR_SAFFIREPRO_CMD_AVC_MODEL_MIDI_ON 1 #define FR_SAFFIREPRO_CMD_AVC_MODEL_MIDI_OFF 0 #define FR_SAFFIREPRO_CMD_ID_DIM_LEVEL 117 #define FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_BITFIELD 118 #define FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH1 0 #define FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH2 1 #define FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH3 2 #define FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH4 3 #define FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH5 4 #define FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH6 5 #define FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH7 6 #define FR_SAFFIREPRO_CMD_ID_DIRECT_MONITORING_CH8 7 namespace BeBoB { namespace Focusrite { class SaffireProDevice; // swiss army knife control class to serve // specific device control commands class SaffireProMultiControl : public Control::Discrete { public: enum eMultiControlType { eTCT_Reboot, eTCT_FlashLed, eTCT_UseHighVoltageRail, eTCT_ExitStandalone, eTCT_PllLockRange, eTCT_SaveSettings, eTCT_EnableADAT1, eTCT_EnableADAT2, eTCT_EnableSPDIF, }; public: SaffireProMultiControl(SaffireProDevice& parent, enum eMultiControlType); SaffireProMultiControl(SaffireProDevice& parent, enum eMultiControlType, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 0;}; private: SaffireProDevice& m_Parent; enum eMultiControlType m_type; }; class SaffireProMatrixMixer : public FocusriteMatrixMixer { public: enum eMatrixMixerType { eMMT_InputMix, eMMT_OutputMix }; public: SaffireProMatrixMixer(SaffireProDevice& parent, enum eMatrixMixerType type); SaffireProMatrixMixer(SaffireProDevice& parent, enum eMatrixMixerType type, std::string n); virtual ~SaffireProMatrixMixer() {}; virtual void show(); protected: void init(); enum eMatrixMixerType m_type; }; // -- wrapper for the name stored on the device class SaffireProDeviceNameControl : public Control::Text { public: SaffireProDeviceNameControl(SaffireProDevice& parent); SaffireProDeviceNameControl(SaffireProDevice& parent, std::string name, std::string label, std::string descr); virtual bool setValue(std::string); virtual std::string getValue(); private: SaffireProDevice& m_Parent; }; // -- wrapper for the standalone config of the device class SaffireProDeviceStandaloneEnum : public Control::Enum { public: SaffireProDeviceStandaloneEnum(SaffireProDevice& parent); SaffireProDeviceStandaloneEnum(SaffireProDevice& parent, std::string name, std::string label, std::string descr); virtual bool select(int idx); virtual int selected(); virtual int count(); virtual std::string getEnumLabel(int idx); private: SaffireProDevice& m_Parent; }; // -- the actual device class SaffireProDevice : public FocusriteDevice { // we want all outside control to be done by this class friend class SaffireProMultiControl; friend class SaffireProDeviceNameControl; public: SaffireProDevice( DeviceManager& d, ffado_smartptr( configRom )); virtual ~SaffireProDevice(); virtual void showDevice(); virtual void setVerboseLevel(int l); virtual bool setSamplingFrequency( int ); virtual int getSamplingFrequency( ); virtual std::vector getSupportedSamplingFrequencies(); virtual bool buildMixer(); virtual bool destroyMixer(); virtual std::string getNickname(); virtual bool setNickname(std::string name); virtual bool canChangeNickname(); protected: void rebootDevice(); void flashLed(); bool isAudioOn(); bool isMidiEnabled(); bool isExtClockLocked(); uint32_t getCount32(); void exitStandalone(); void useHighVoltageRail(bool useIt); bool usingHighVoltageRail(); unsigned int getPllLockRange(); void setPllLockRange(unsigned int); void saveSettings(); bool setDeviceName(std::string n); std::string getDeviceName(); enum eDigitalChannel { eDC_ADAT1, eDC_ADAT2, eDC_SPDIF }; unsigned int getEnableDigitalChannel(enum eDigitalChannel); void setEnableDigitalChannel(enum eDigitalChannel, unsigned int); bool isPro10() {return getConfigRom().getModelId() == 0x00000006;}; bool isPro26() {return getConfigRom().getModelId() == 0x00000003;}; public: // override these since we want to use the focusrite way of setting // the clock virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); protected: virtual uint16_t getConfigurationIdSyncMode(); virtual uint64_t getConfigurationId(); private: virtual bool setSamplingFrequencyDo( uint32_t ); virtual bool setSamplingFrequencyDoNoReboot( uint32_t ); private: void updateClockSources(); ClockSource m_internal_clocksource; ClockSource m_spdif_clocksource; ClockSource m_wordclock_clocksource; ClockSource m_adat1_clocksource; ClockSource m_adat2_clocksource; ClockSource *m_active_clocksource; Control::Container *m_MixerContainer; Control::Container *m_ControlContainer; SaffireProDeviceNameControl *m_deviceNameControl; }; } // namespace Focusrite } // namespace BeBoB #endif libffado-2.4.5/src/bebob/mackie/0000755000175000001440000000000014206145612015752 5ustar jwoitheuserslibffado-2.4.5/src/bebob/mackie/onyxmixer.cpp0000644000175000001440000000411214206145246020521 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "onyxmixer.h" #include "debugmodule/debugmodule.h" namespace BeBoB { namespace Mackie { OnyxMixerDevice::OnyxMixerDevice( DeviceManager& d, ffado_smartptr( configRom )) : BeBoB::Device( d, configRom) { m_fixed_clocksource.type = FFADODevice::eCT_Internal; m_fixed_clocksource.valid = true; m_fixed_clocksource.locked = true; m_fixed_clocksource.id = 0; m_fixed_clocksource.slipping = false; m_fixed_clocksource.description = "Internal"; debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::Mackie::OnyxMixerDevice (NodeID %d)\n", getConfigRom().getNodeId() ); } OnyxMixerDevice::~OnyxMixerDevice() { } void OnyxMixerDevice::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a BeBoB::Mackie::OnyxMixerDevice\n"); BeBoB::Device::showDevice(); } FFADODevice::ClockSource OnyxMixerDevice::getActiveClockSource() { return m_fixed_clocksource; } bool OnyxMixerDevice::setActiveClockSource(ClockSource s) { // can't change, hence only succeed when identical return s.id == m_fixed_clocksource.id; } FFADODevice::ClockSourceVector OnyxMixerDevice::getSupportedClockSources() { FFADODevice::ClockSourceVector r; r.push_back(m_fixed_clocksource); return r; } } // Mackie } // BeBoB libffado-2.4.5/src/bebob/mackie/onyxmixer.h0000644000175000001440000000310214206145246020164 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_ONYX_MIXER_DEVICE_H #define BEBOB_ONYX_MIXER_DEVICE_H #include "bebob/bebob_avdevice.h" namespace BeBoB { namespace Mackie { class OnyxMixerDevice : public BeBoB::Device { public: OnyxMixerDevice( DeviceManager& d, ffado_smartptr( configRom )); virtual ~OnyxMixerDevice(); // override these since the mackie does not report // any the clock sources (it only supports internal clocking) virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); virtual void showDevice(); private: ClockSource m_fixed_clocksource; }; } // namespace Mackie } // namespace BeBoB #endif libffado-2.4.5/src/bebob/maudio/0000755000175000001440000000000014206145612015777 5ustar jwoitheuserslibffado-2.4.5/src/bebob/maudio/normal_avdevice.cpp0000644000175000001440000001514314206145246021650 0ustar jwoitheusers/* * Copyright (C) 2013 by Takashi Sakamoto * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "normal_avdevice.h" namespace BeBoB { namespace MAudio { namespace Normal { Device::Device(DeviceManager& d, ffado_smartptr( configRom ), unsigned int modelId) : BeBoB::Device( d, configRom) { switch ( modelId ) { case 0x00010046: // fw410 m_id = FW_410; break; case 0x00010060: // Audiophile m_id = FW_AUDIOPHILE; break; case 0x00010062: // Solo m_id = FW_SOLO; break; case 0x0000000a: m_id = FW_OZONIC; break; } updateClkSrc(); debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::MAudio::Normal::Device (NodeID %d)\n", getConfigRom().getNodeId() ); } Device::~Device() { } void Device::showDevice() { debugOutput(DEBUG_LEVEL_NORMAL, "This is a BeBoB::MAudio::Normal::Device\n"); BeBoB::Device::showDevice(); } bool Device::updateClkSrc() { int plugid; m_internal_clksrc.type = FFADODevice::eCT_Internal; m_internal_clksrc.active = false; m_internal_clksrc.valid = true; m_internal_clksrc.locked = true; m_internal_clksrc.id = 0x01; m_internal_clksrc.slipping = false; m_internal_clksrc.description = "Internal"; m_spdif_clksrc.type = FFADODevice::eCT_SPDIF; m_spdif_clksrc.active = false; m_spdif_clksrc.valid = false; m_spdif_clksrc.locked = false; m_spdif_clksrc.id = 0; m_spdif_clksrc.slipping = false; m_spdif_clksrc.description = "S/PDIF (Coaxial)"; m_adat_clksrc.type = FFADODevice::eCT_ADAT; m_adat_clksrc.active = false; m_adat_clksrc.valid = false; m_adat_clksrc.locked = false; m_adat_clksrc.id = 0; m_adat_clksrc.slipping = false; m_adat_clksrc.description = "ADAT (Optical)"; switch (m_id) { case FW_410: // fw410 m_spdif_clksrc.active = true; m_spdif_clksrc.valid = true; m_spdif_clksrc.id = 0x82; m_adat_clksrc.active = true; m_adat_clksrc.valid = true; m_adat_clksrc.id = 0x83; break; case FW_AUDIOPHILE: // Audiophile m_spdif_clksrc.active = true; m_spdif_clksrc.valid = true; m_spdif_clksrc.id = 0x82; break; case FW_SOLO: // Solo m_spdif_clksrc.active = true; m_spdif_clksrc.valid = true; m_spdif_clksrc.id = 0x81; break; case FW_OZONIC: /* internal only */ m_active_clksrc = &m_internal_clksrc; return true; } plugid = getClkSrc(); if (plugid < 0) return false; if (plugid == 0x01) { m_internal_clksrc.active = true; m_active_clksrc = &m_internal_clksrc; } else if (plugid == 0x83) { m_adat_clksrc.active = true; m_active_clksrc = &m_adat_clksrc; } else { m_spdif_clksrc.active = true; m_active_clksrc = &m_spdif_clksrc; } return true; } int Device::getClkSrc() { AVC::SignalSourceCmd cmd( get1394Service() ); cmd.setCommandType( AVC::AVCCommand::eCT_Status ); cmd.setNodeId( getNodeId() ); cmd.setSubunitType( AVC::eST_Unit ); cmd.setSubunitId( 0xff ); cmd.setVerbose( getDebugLevel() ); AVC::SignalSubunitAddress dst; dst.m_subunitType = AVC::eST_Music; dst.m_subunitId = 0x00; dst.m_plugId = 0x01; cmd.setSignalDestination( dst ); if ( !cmd.fire() ) { debugError( "Signal source command failed\n" ); return -1; } AVC::SignalAddress* pSyncPlugSignalAddress = cmd.getSignalSource(); AVC::SignalSubunitAddress* pSyncPlugSubunitAddress = dynamic_cast( pSyncPlugSignalAddress ); if ( pSyncPlugSubunitAddress ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Sync mode 0x%02x\n", ( pSyncPlugSubunitAddress->m_subunitType << 3 | pSyncPlugSubunitAddress->m_subunitId ) << 8 | pSyncPlugSubunitAddress->m_plugId ); return pSyncPlugSubunitAddress->m_plugId; } AVC::SignalUnitAddress* pSyncPlugUnitAddress = dynamic_cast( pSyncPlugSignalAddress ); if ( pSyncPlugUnitAddress ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Sync mode 0x%02x\n", 0xff << 8 | pSyncPlugUnitAddress->m_plugId ); return pSyncPlugUnitAddress->m_plugId; } debugError( "Could not retrieve sync mode\n" ); return -1; } FFADODevice::ClockSourceVector Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; r.push_back(m_internal_clksrc); if (m_spdif_clksrc.active) r.push_back(m_spdif_clksrc); if (m_adat_clksrc.active) r.push_back(m_adat_clksrc); return r; } FFADODevice::ClockSource Device::getActiveClockSource() { if (!updateClkSrc()) { ClockSource s; s.type = eCT_Invalid; return s; } return *m_active_clksrc; } bool Device::setActiveClockSource(ClockSource s) { AVC::SignalSourceCmd cmd( get1394Service() ); cmd.setCommandType( AVC::AVCCommand::eCT_Control ); cmd.setNodeId( getNodeId() ); cmd.setSubunitType( AVC::eST_Unit ); cmd.setSubunitId( 0xff ); cmd.setVerbose( getDebugLevel() ); AVC::SignalSubunitAddress dst; dst.m_subunitType = AVC::eST_Music; dst.m_subunitId = 0x00; dst.m_plugId = 0x01; cmd.setSignalDestination( dst ); if (s.id == 0x01) { AVC::SignalSubunitAddress src; src.m_subunitType = AVC::eST_Music; src.m_subunitId = 0x00; src.m_plugId = 0x01; cmd.setSignalSource( src ); } else { AVC::SignalUnitAddress src; src.m_plugId = s.id; cmd.setSignalSource( src ); } if ( !cmd.fire() ) { debugError( "Signal source command failed\n" ); return false; } return true; } } // namespace Normal } // namespace MAudio } // namespace BeBoB libffado-2.4.5/src/bebob/maudio/normal_avdevice.h0000644000175000001440000000350014206145246021307 0ustar jwoitheusers/* * Copyright (C) 2013 by Takashi Sakamoto * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_MAUDIO_NORMAL_DEVICE_H #define BEBOB_MAUDIO_NORMAL_DEVICE_H #include "debugmodule/debugmodule.h" #include "bebob/bebob_avdevice.h" namespace BeBoB { namespace MAudio { namespace Normal { enum DeviceID { FW_410, FW_AUDIOPHILE, FW_SOLO, FW_OZONIC }; class Device : public BeBoB::Device { public: Device( DeviceManager& d, ffado_smartptr( configRom ), unsigned int modelId); virtual ~Device(); virtual void showDevice(); virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); private: bool updateClkSrc(); int getClkSrc(); ClockSource m_internal_clksrc; ClockSource m_spdif_clksrc; ClockSource m_adat_clksrc; ClockSource *m_active_clksrc; enum DeviceID m_id; }; } // namespace Normal } // namespace MAudio } // namespace BeBoB #endif libffado-2.4.5/src/bebob/maudio/special_avdevice.cpp0000644000175000001440000001316114206145246021776 0ustar jwoitheusers/* * Copyright (C) 2014 by Takashi Sakamoto * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "libutil/Configuration.h" #include "libutil/ByteSwap.h" #include "libutil/SystemTimeSource.h" #include #include #include "bebob/maudio/special_avdevice.h" namespace BeBoB { namespace MAudio { namespace Special { Device::Device(DeviceManager& d, ffado_smartptr(configRom)) : BeBoB::Device(d, configRom) { is1814 = (getConfigRom().getModelId() == 0x00010071); debugOutput(DEBUG_LEVEL_VERBOSE, "Created BeBoB::MAudio::Device (NodeID %d)\n", getConfigRom().getNodeId()); updateClockSources(); } void Device::showDevice() { debugOutput(DEBUG_LEVEL_NORMAL, "This is a BeBoB::MAudio::Device\n"); } /* * Discoveing plugs: * The firmware can't respond any commands related to plug info. */ bool Device::discover() { /* keep track of the config id of this discovery */ m_last_discovery_config_id = getConfigurationId(); return true; } /* don't lock to prevent from streaming */ bool Device::lock() { return false; } bool Device::Unlock() { return false; } /* don't cache */ bool Device::loadFromCache() { return buildMixer(); } bool Device::saveCache() { return true; } bool Device::buildMixer() { debugOutput(DEBUG_LEVEL_VERBOSE, "Building a maudio special mixer...\n"); delete m_special_mixer; m_special_mixer = new Mixer(*this); if (m_special_mixer) m_special_mixer->setVerboseLevel(getDebugLevel()); return (m_special_mixer != NULL); } bool Device::destroyMixer() { delete m_special_mixer; return true; } /* * Sampling frequency support: * set: Input/Output Signal Format command * get: Input Signal Format command (or metering information) * * TODO */ std::vector Device::getSupportedSamplingFrequencies() { std::vector freqs; freqs.push_back(0); return freqs; } bool Device::supportsSamplingFrequency(int s) { return true; } int Device::getSamplingFrequency() { return 0; } /* * Clock Source support: * TODO: use ALSA control interface * */ void Device::updateClockSources() { m_fixed_clksrc.type = FFADODevice::eCT_Internal; m_fixed_clksrc.active = true; m_fixed_clksrc.valid = true; m_fixed_clksrc.locked = true; m_fixed_clksrc.id = 0; m_fixed_clksrc.slipping = false; m_fixed_clksrc.description = "Controlled by ALSA"; } FFADODevice::ClockSourceVector Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; r.push_back(m_fixed_clksrc); return r; } FFADODevice::ClockSource Device::getActiveClockSource() { return m_fixed_clksrc; } bool Device::setActiveClockSource(ClockSource s) { return true; } /* * Streaming State: * The firmware can't respond against frequent requests. * TODO: how to void frequent transaction? */ enum FFADODevice::eStreamingState Device::getStreamingState() { return eSS_Both; } /* * The firmware can't speak: * 'Extended Plug Info command' (BridgeCo) * 'Connection and Compatibility Management' (1394TA) */ uint8_t Device::getConfigurationIdSampleRate() { return 1; } uint8_t Device::getConfigurationIdNumberOfChannel( AVC::PlugAddress::EPlugDirection ePlugDirection ) { return 2; } uint16_t Device::getConfigurationIdSyncMode() { return 3; } bool Device::readReg(uint64_t offset, uint32_t *data) { Util::MutexLockHelper lock(m_DeviceMutex); /* read cache because it's 'write-only' */ *data = m_regs[offset / 4]; return true; } bool Device::writeReg(uint64_t offset, uint32_t data) { int trials; fb_nodeid_t nodeId; fb_nodeaddr_t addr; fb_quadlet_t quad; Util::MutexLockHelper lock(m_DeviceMutex); nodeId = getNodeId() | 0xffc0; addr = MAUDIO_SPECIFIC_ADDRESS + MAUDIO_CONTROL_OFFSET + offset; quad = CondSwapToBus32(data); /* cache because it's 'write-only' */ m_regs[offset / 4] = data; trials = 0; do { if (get1394Service().write_quadlet(nodeId, addr, quad)) break; Util::SystemTimeSource::SleepUsecRelative(100); } while (++trials < 4); return true; } bool Device::writeBlk(uint64_t offset, unsigned int size, uint32_t *data) { fb_nodeid_t nodeId; fb_nodeaddr_t addr; unsigned int i, length, trials; nodeId = getNodeId() | 0xffc0; addr = MAUDIO_SPECIFIC_ADDRESS + MAUDIO_CONTROL_OFFSET + offset; length = size /4; for (i = 0; i < length; i++) { /* the device applies the setting even if the device don't respond */ m_regs[i] = data[i]; data[i] = CondSwapToBus32(data[i]); } trials = 0; do { if (get1394Service().write(nodeId, addr, length, (fb_quadlet_t*)data)) break; Util::SystemTimeSource::SleepUsecRelative(100); } while (++trials < 4); return true; } } // namespace Special } // namespace MAudio } // namespace BeBoB libffado-2.4.5/src/bebob/maudio/special_avdevice.h0000644000175000001440000001342614206145246021447 0ustar jwoitheusers/* * Copyright (C) 2014 by Takashi Sakamoto * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_MAUDIO_SPECIAL_DEVICE_H #define BEBOB_MAUDIO_SPECIAL_DEVICE_H #include "debugmodule/debugmodule.h" #include "bebob/bebob_avdevice.h" #include "bebob/maudio/special_mixer.h" /* * FireWire 1814 and ProjectMix I/O uses special firmware. It will be freezed * if receiving any commands which the firmware can't understand. These devices * utilize completely different system to control. It is read/write transaction * directly into a certain address. */ #define MAUDIO_SPECIFIC_ADDRESS 0xffc700000000 /* * FW1814/ProjectMix don't use AVC for control. The driver cannot refer to * current parameters by asynchronous transaction. The driver is allowed to * write transaction so MUST remember the current values. */ #define MAUDIO_CONTROL_OFFSET 0x00700000 /* * GAIN for inputs: * Write 32bit. upper 16bit for left chennal and lower 16bit for right. * The value is between 0x8000(low) to 0x0000(high) as the same as '10.3.1 * Volume Control' in 'AV/C Audio Subunit Specification 1.0 (1394TA 1999008)'. */ #define GAIN_STM_12_IN 0x00 #define GAIN_STM_34_IN 0x04 #define GAIN_ANA_12_OUT 0x08 #define GAIN_ANA_34_OUT 0x0c #define GAIN_ANA_12_IN 0x10 #define GAIN_ANA_34_IN 0x14 #define GAIN_ANA_56_IN 0x18 #define GAIN_ANA_78_IN 0x1c #define GAIN_SPDIF_12_IN 0x20 #define GAIN_ADAT_12_IN 0x24 #define GAIN_ADAT_34_IN 0x28 #define GAIN_ADAT_56_IN 0x2c #define GAIN_ADAT_78_IN 0x30 #define GAIN_AUX_12_OUT 0x34 #define GAIN_HP_12_OUT 0x38 #define GAIN_HP_34_OUT 0x3c /* * LR balance: * Write 32 bit, upper 16bit for left channel and lower 16bit for right. * The value is between 0x800(L) to 0x7FFE(R) as the same as '10.3.3 LR Balance * Control' in 'AV/C Audio Subunit Specification 1.0 (1394TA 1999008)'. */ #define LR_ANA_12_IN 0x40 #define LR_ANA_34_IN 0x44 #define LR_ANA_56_IN 0x48 #define LR_ANA_78_IN 0x4c #define LR_SPDIF_12_IN 0x50 #define LR_ADAT_12_IN 0x54 #define LR_ADAT_34_IN 0x58 #define LR_ADAT_56_IN 0x5c #define LR_ADAT_78_IN 0x60 /* * AUX inputs: * This is the same as 'gain' control above. */ #define AUX_STM_12_IN 0x64 #define AUX_STM_34_IN 0x68 #define AUX_ANA_12_IN 0x6c #define AUX_ANA_34_IN 0x70 #define AUX_ANA_56_IN 0x74 #define AUX_ANA_78_IN 0x78 #define AUX_SPDIF_12_IN 0x7c #define AUX_ADAT_12_IN 0x80 #define AUX_ADAT_34_IN 0x84 #define AUX_ADAT_56_IN 0x88 #define AUX_ADAT_78_IN 0x8c /* * MIXER inputs: * There are bit flags. If flag is 0x01, it means on. * * MIX_ANA_DIG_IN: * Write 32bits, upper 16bit for digital inputs and lowe 16bit for analog inputs. * Digital inputs: * Lower 2bits are used. upper for 'to Mix3/4' and lower for 'to Mix1/2'. * Analog inputs: * Lower 8bits are used. upper 4bits for 'to Mix3/4' and lower for 'to * Mix1/2'. Inner the 4bit, for 'from Ana7/8', for 'from Ana5/6', for 'from * Ana3/4', for 'from Ana1/2'. * * MIX_STM_IN: * Write 32bits, lower 4bits are used. upper 2bits for 'from Stm1/2' and lower * for 'from Stm3/4'. Inner the 2bits, for 'to Mix3/4' and for 'to * Mix1/2'. */ #define MIX_ANA_DIG_IN 0x90 #define MIX_STM_IN 0x94 /* * SRC for output: * Write 32bit. There are bit flags. If the flag is 0x01, it means on. * * SRC_HP_OUT: * Lower 3bits are used, 'from Aux12', 'from Mix34', 'from * Mix12'. * * SRC_ANA_OUT: * Lower 2 bits are used, 'to Ana34', 'to Ana12'. If bit is 0x01, it * means 'from Aux12' else 'From Mix12 (or Mix34)'. */ #define SRC_HP_OUT 0x98 #define SRC_ANA_OUT 0x9c namespace BeBoB { namespace MAudio { namespace Special { class Device : public BeBoB::Device { public: Device(DeviceManager& d, ffado_smartptr(configRom)); virtual ~Device() {}; virtual void showDevice(); bool lock(); bool Unlock(); bool loadFromCache(); bool saveCache(); virtual bool discover(); virtual bool buildMixer(); virtual bool destroyMixer(); std::vector getSupportedSamplingFrequencies(); bool supportsSamplingFrequency( int s ); int getSamplingFrequency(); virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); virtual enum FFADODevice::eStreamingState getStreamingState(); virtual bool readReg(uint64_t addr, uint32_t *data); virtual bool writeReg(uint64_t addr, uint32_t data); bool writeBlk(uint64_t offset, unsigned int length, uint32_t *data); protected: virtual uint8_t getConfigurationIdSampleRate(); virtual uint8_t getConfigurationIdNumberOfChannel(AVC::PlugAddress::EPlugDirection ePlugDirection); virtual uint16_t getConfigurationIdSyncMode(); private: void updateClockSources(); ClockSource m_fixed_clksrc; Mixer *m_special_mixer; bool is1814; /* cache for mixer settings */ uint32_t m_regs[(0x9c + 4) / 4]; }; } // namespace Special } // namespace MAudio } // namespace BeBoB #endif /* BEBOB_MAUDIO_SPECIAL_DEVICE_H */ libffado-2.4.5/src/bebob/maudio/special_mixer.cpp0000644000175000001440000003110414206145246021331 0ustar jwoitheusers/* * Copyright (C) 2014 by Takashi Sakamoto * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include "libieee1394/ieee1394service.h" #include "libieee1394/configrom.h" #include "libcontrol/BasicElements.h" #include "libutil/ByteSwap.h" #include "bebob/bebob_avdevice.h" #include "bebob/bebob_avdevice_subunit.h" #include "bebob/maudio/special_mixer.h" #include "bebob/maudio/special_avdevice.h" using namespace AVC; namespace BeBoB { namespace MAudio { namespace Special { class Device; IMPL_DEBUG_MODULE(Mixer, Mixer, DEBUG_LEVEL_NORMAL); Mixer::Mixer(Device &dev) : Control::Container((Device *)&dev) , m_dev((Device *) &dev) { int i; for (i = 1; i < 28; i++) addElement(new Volume(dev, i)); for (i = 1; i < 10; i++) addElement(new LRBalance(dev, i)); for (i = 1; i < 5; i++) addElement(new Selector(dev, i)); for (i = 1; i < 3; i++) addElement(new Processing(dev, i)); if (!initialize(dev)) debugWarning("Could not initialize mixer settings\n"); if (!((Device *) &dev)->addElement(this)) debugWarning("Could not add BeBoB::MAudio::Special::Mixer to Control::Container\n"); } bool Mixer::initialize(Device &dev) { uint32_t *data; unsigned int i; bool err; data = (uint32_t *)malloc(0x9c + 4); if (data == NULL) return false; /* inputs volumes */ for (i = 0; i < 16; i++) data[i] = 0x00000000; /* inputs panning */ for (i = 16; i < 25; i++) data[i] = 0x7ffe8000; /* aux inputs volume */ for (i = 25; i < 36; i++) data[i] = 0x00000000; /* analog inputs to mixer */ data[36] = 0x00000000; /* stream inputs to mixer */ data[37] = 0x00000000; /* headphone sources */ data[38] = 0x00000000; /* output sources */ data[39] = 0x00000000; err = dev.writeBlk(0x00, 0x9c + 4, data); free(data); return err; } /* * Output source control: * Path Target Offset * /Mixer/Selector_1 HP 1/2 out 0x98 * /Mixer/Selector_2 HP 3/4 out 0x98 * /Mixer/Selector_3 Analog 1/2 out 0x9C * /Mixer/Selector_4 Analog 3/4 out 0x9C * There seems not to be for digital output. */ Selector::Selector(Device& dev, unsigned int id) : Control::Discrete((Device *)&dev) , m_dev(&dev) , m_id(id) { std::ostringstream ostrm; ostrm << "Selector_" << id; Control::Discrete::setName(ostrm.str()); ostrm.str(""); ostrm << "Label for Selector " << id; setLabel(ostrm.str()); ostrm.str(""); ostrm << "Description for Selector " << id; setDescription(ostrm.str()); } uint64_t Selector::getOffset() { if (m_id < 3) return 0x98; else return 0x9c; } bool Selector::setValue(int id, int v) { uint32_t data, value; if (!m_dev->readReg(getOffset(), &data)) return false; /* for headphone out */ if (m_id < 3) { if (v == 2) value = 0x04; else if (v == 1) value = 0x02; else value = 0x01; if (m_id == 1) data = (data & 0xffff0000) | value; else data = (value << 16) | (data & 0xffff); } else { value = 0x01 & v; if (m_id == 3) data = (data & 0x02) | value; else data = (value << 1) | (data & 0x01); } return m_dev->writeReg(getOffset(), data); } int Selector::getValue(int id) { uint32_t data, value; if (!m_dev->readReg(getOffset(), &data)) return 0; if (m_id < 3) { if (m_id == 1) data = data & 0xffff; else data = data >> 16; if (data & 0x04) value = 2; else if (data & 0x02) value = 1; else value = 0; } else if (m_id == 3) value = 0x01 & data; else value = (0x02 & data) >> 1; return value; } /* * Input volume control: * Path Target Offset * /Mixer/Feature_Volume_1 Analog 1/2 in 0x10 * /Mixer/Feature_Volume_2 Analog 3/4 in 0x14 * /Mixer/Feature_Volume_3 Analog 5/6 in 0x18 * /Mixer/Feature_Volume_4 Analog 7/8 in 0x1c * /Mixer/Feature_Volume_5 SPDIF 1/2 in 0x20 * /Mixer/Feature_Volume_6 ADAT 1/2 in 0x24 * /Mixer/Feature_Volume_7 ADAT 3/4 in 0x28 * /Mixer/Feature_Volume_8 ADAT 5/6 in 0x2c * /Mixer/Feature_Volume_9 ADAT 7/8 in 0x30 * /Mixer/Feature_Volume_10 Stream 1/2 in 0x00 * /Mixer/Feature_Volume_11 Stream 3/4 in 0x04 * * Output volume control: * /Mixer/Feature_Volume_12 Analog 1/2 out 0x08 * /Mixer/Feature_Volume_13 Analog 3/4 out 0x0c * /Mixer/Feature_Volume_14 AUX 1/2 out 0x34 * /Mixer/Feature_Volume_15 HP 1/2 out 0x38 * /Mixer/Feature_Volume_16 HP 3/4 out 0x3c * * Aux input control: * Path Target Offset * /Mixer/Feature_Volume_17 Stream 1/2 in 0x64 * /Mixer/Feature_Volume_18 Stream 3/4 in 0x68 * /Mixer/Feature_Volume_19 Analog 1/2 in 0x6c * /Mixer/Feature_Volume_20 Analog 3/4 in 0x70 * /Mixer/Feature_Volume_21 Analog 5/6 in 0x74 * /Mixer/Feature_Volume_22 Analog 7/8 in 0x78 * /Mixer/Feature_Volume_23 S/PDIF 1/2 in 0x7c * /Mixer/Feature_Volume_24 ADAT 1/2 in 0x80 * /Mixer/Feature_Volume_25 ADAT 3/4 in 0x84 * /Mixer/Feature_Volume_26 ADAT 5/6 in 0x88 * /Mixer/Feature_Volume_27 ADAT 7/8 in 0x8c */ Volume::Volume(Device &dev, unsigned int id) : Control::Continuous((Device *)&dev) , m_dev(&dev) , m_id(id) { std::ostringstream ostrm; ostrm << "Feature_Volume_" << id; Control::Continuous::setName(ostrm.str()); ostrm.str(""); ostrm << "Label for Feature Volume" << id; setLabel(ostrm.str()); ostrm.str(""); ostrm << "Description for Feature Volume " << id; setDescription(ostrm.str()); } uint64_t Volume::getOffset() { if ((m_id > 0) && (m_id < 10)) return (m_id - 1) * 4 + 0x10; else if (m_id < 12) return (m_id - 10) * 4; else if (m_id < 14) return (m_id - 12) * 4 + 0x08; else if (m_id < 17) return (m_id - 14) * 4 + 0x34; else return (m_id - 17) * 4 + 0x64; } bool Volume::setValue(int idx, double v) { uint32_t data; if (!m_dev->readReg(getOffset(), &data)) return false; /* mute */ if (v == 0x8000) { data = 0x80008000; /* unmute */ } else if (v == 0x0000) { data = 0x00000000; /* others */ } else { if (idx > 1) data = (data & 0xffff0000) | ((uint32_t)v & 0xffff); else data = (((uint32_t)v & 0xffff) << 16) | (data & 0xffff); } return m_dev->writeReg(getOffset(), data); } double Volume::getValue(int idx) { uint32_t data; if (!m_dev->readReg(getOffset(), &data)) return 0; if (idx > 1) return data & 0xffff; else return data >> 16; } /* * Input L/R balance control: * Path Target Offset * /Mixer/Feature_LRBalance_1 Analog 1/2 in 0x40 * /Mixer/Feature_LRBalance_2 Analog 3/4 in 0x44 * /Mixer/Feature_LRBalance_3 Analog 5/6 in 0x48 * /Mixer/Feature_LRBalance_4 Analog 7/8 in 0x4c * /Mixer/Feature_LRBalance_5 S/PDIF 1/2 in 0x50 * /Mixer/Feature_LRBalance_6 ADAT 1/2 in 0x54 * /Mixer/Feature_LRBalance_7 ADAT 3/4 in 0x58 * /Mixer/Feature_LRBalance_8 ADAT 5/6 in 0x5c * /Mixer/Feature_LRBalance_9 ADAT 7/8 in 0x60 */ LRBalance::LRBalance(Device &dev, unsigned int id) : Control::Continuous((Device *)&dev) , m_dev(&dev) , m_id(id) { std::ostringstream ostrm; ostrm << "Feature_LRBalance_" << id; Control::Continuous::setName(ostrm.str()); ostrm.str(""); ostrm << "Label for L/R Balance " << id; setLabel(ostrm.str()); ostrm.str(""); ostrm << "Description for L/R Balance " << id; setDescription(ostrm.str()); } uint64_t LRBalance::getOffset() { return (m_id - 1) * 4 + 0x40; } bool LRBalance::setValue(int idx, double v) { uint32_t data; if (!m_dev->readReg(getOffset(), &data)) return false; if (idx > 1) data = (data & 0xffff0000) | ((uint32_t)v & 0xffff); else data = (data & 0xffff) | (((uint32_t)v & 0xffff) << 16); return m_dev->writeReg(getOffset(), data); } double LRBalance::getValue(int idx) { uint32_t data; int16_t val; if (!m_dev->readReg(getOffset(), &data)) return 0; if (idx > 1) val = data & 0xff00; else val = (data >> 16) & 0xff00; return val; } /* * Mixer input control: * Path Target Offset (Ana/Strm) * /Mixer/EnhancedMixer_1 Mixer 1/2 out 0x90/0x9c * /Mixer/EnhancedMixer_2 Mixer 3/4 out 0x90/0x9c */ Processing::Processing(Device& dev, unsigned int id) : Control::Continuous((Device *)&dev) , m_dev(&dev) , m_id(id) { std::ostringstream ostrm; ostrm << "EnhancedMixer_" << id; Control::Continuous::setName(ostrm.str()); ostrm.str(""); ostrm << "Label for EnhancedMixer " << id; setLabel(ostrm.str()); ostrm.str(""); ostrm << "Description for EnhancedMixer " << id; setDescription(ostrm.str()); } uint64_t Processing::getOffset(int iPlugNum) { /* Stream inputs */ if (iPlugNum != 2) return 0x90; /* Analog inputs */ /* S/PDIF inputs */ /* ADAT inputs */ else return 0x94; } bool Processing::setValue(int idx, double v) { int iPlugNum, iAChNum; uint64_t offset; uint32_t data, value, mask; iPlugNum = (idx >> 8) & 0x0f; iAChNum = (idx >> 4) & 0x0f; offset = getOffset(iPlugNum); if (!m_dev->readReg(offset, &data)) return false; if (v != 0x00) value = 0; else value = 1; /* analog inputs */ if (iPlugNum == 1) { mask = 0x01 << (iAChNum / 2); value = value << (iAChNum / 2); /* mixer 3/4 out */ if (m_id > 1) { mask <<= 4; value <<= 4; } /* stream inputs */ } else if (iPlugNum == 2) { mask = 0x01; if (iAChNum > 1) { mask <<= 2; value <<= 2; } if (m_id > 1) { mask <<= 1; value <<= 1; } /* S/PDIF inputs */ } else if (iPlugNum == 3) { mask = 0x01 << (iAChNum / 2); value = value << (iAChNum / 2); mask <<= 16; value <<= 16; /* mixer 3/4 out */ if (m_id > 1) { mask <<= 1; value <<= 1; } /* ADAT inputs */ } else { mask = 0x01 << (iAChNum / 2); value = value << (iAChNum / 2); mask <<= 8; value <<= 8; /* mixer 3/4 out */ if (m_id > 1) { mask <<= 4; value <<= 4; } } data &= ~mask; data |= value; return m_dev->writeReg(offset, data); } double Processing::getValue(int idx) { int iPlugNum, iAChNum, shift; uint64_t offset; uint32_t data; double value; iPlugNum = (idx >> 8) & 0xF; iAChNum = (idx >> 4) & 0xF; offset = getOffset(iPlugNum); if (!m_dev->readReg(offset, &data)) return false; /* analog inputs */ if (iPlugNum == 1) { shift = iAChNum / 2; if (m_id > 1) shift += 4; /* stream inputs */ } else if (iPlugNum == 2) { shift = 0; if (iAChNum > 1) shift += 1; if (m_id > 1) shift += 2; /* S/PDIF inputs */ } else if (iPlugNum == 3) { shift = iAChNum / 2; shift += 16; /* ADAT inputs */ } else { shift = iAChNum / 2; shift += 8; if (m_id > 1) shift += 4; } if ((data >> shift) & 0x01) value = 0x0000; else value = 0x8000; return value; } } // namespace Special } // namespace MAudio } // namespace BeBoB libffado-2.4.5/src/bebob/maudio/special_mixer.h0000644000175000001440000000721714206145246021006 0ustar jwoitheusers/* * Copyright (C) 2014 by Takashi Sakamoto * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_MAUDIO_SPECIAL_MIXER_H #define BEBOB_MAUDIO_SPECIAL_MIXER_H #include "src/debugmodule/debugmodule.h" #include "libcontrol/BasicElements.h" namespace BeBoB { namespace MAudio { namespace Special { class Device; class Mixer : public Control::Container { public: Mixer(Device &dev); virtual ~Mixer() {}; bool initialize(Device &dev); virtual std::string getName() {return "Mixer";}; virtual bool setName(std::string n) {return false;}; protected: DECLARE_DEBUG_MODULE; private: Device *m_dev; }; class Selector : public Control::Discrete { public: Selector(Device& dev, unsigned int id); virtual ~Selector() {}; virtual bool setValue(int idx, int v); virtual int getValue(int idx); virtual bool setValue(int v) {return setValue(1, v);}; virtual int getValue() {return getValue(1);}; virtual int getMinimum() {return 1;}; virtual int getMaximum() {return 4;}; private: uint64_t getOffset(); Device *m_dev; unsigned int m_id; }; class Volume : public Control::Continuous { public: Volume(Device& m_dev, unsigned int id); virtual ~Volume() {}; virtual bool setValue(int idx, double v); virtual double getValue(int idx); virtual bool setValue(double v) {return setValue(1, v);}; virtual double getValue() {return getValue(1);}; virtual double getMinimum() {return 0x8000;}; virtual double getMaximum() {return 0x0000;}; private: uint64_t getOffset(); Device *m_dev; unsigned int m_id; }; class LRBalance : public Control::Continuous { public: LRBalance(Device& dev, unsigned int id); virtual ~LRBalance() {}; virtual bool setValue(int idx, double v); virtual double getValue(int idx); virtual bool setValue(double v) {return setValue(1, v);}; virtual double getValue() {return getValue(1);}; virtual double getMinimum() {return -32766;}; virtual double getMaximum() {return 32768;}; private: uint64_t getOffset(); Device *m_dev; unsigned int m_id; }; class Processing : public Control::Continuous { public: Processing(Device& dev, unsigned int id); virtual ~Processing() {}; virtual bool setValue(int idx, double v); virtual double getValue(int idx); virtual bool setValue(double v) {return setValue(1, v);}; virtual double getValue() {return getValue(1);}; virtual double getMinimum() {return 0x8000;}; virtual double getMaximum() {return 0x0000;}; private: uint64_t getOffset(int iPlugNum); Device *m_dev; unsigned int m_id; }; } // namespace Special } // namespace MAudio } // namespace BeBoB #endif /* BEBOB_MAUDIO_SPECIAL_MIXER_H */ libffado-2.4.5/src/bebob/maudio/fw410.xml0000644000175000001440000001560612233357644017402 0ustar jwoitheusers 0 Connection Information for M-Audio, FW 410 configuration M-Audio FW 410 0x000d6c0100698b96 1 0 1 0 8 48000 -1 0 0 1 6 3 0 LineOut1 L 4 2 6 3 0 LineOut1 R 1 1 6 3 0 LineOut2 L 5 2 6 3 0 LineOut2 R 2 1 6 3 0 LineOut3 L 6 2 6 3 0 LineOut3 R 3 1 6 4 0 LineOut4 L 7 2 6 4 0 LineOut4 R 0 1 1 2 48000 -1 0 0 1 6 4 1 SPDIFOut L 1 2 6 4 1 SPDIFOut R 0 0 1 0 2 48000 -1 1 0 1 6 3 0 LineIn1 L 1 2 6 3 0 LineIn1 R 0 1 2 2 48000 -1 0 0 1 13 10 2 MIDIIn 1 2 6 4 2 Dummy 1 48000 10 0 0 48000 2 2 libffado-2.4.5/src/bebob/maudio/refdesign.xml0000644000175000001440000001326512233357644020506 0ustar jwoitheusers 1 Connection Information for bridgeCo AG, Switzerland , Ref-Design_Audio1 configuration bridgeCo AG, Switzerland Ref-Design_Audio1 0007f51000000042 1 0 1 9 48000 -1 0 1 6 3 0 LineOut1 L 4 2 6 3 0 LineOut1 R 1 1 6 3 0 LineOut2 L 5 2 6 3 0 LineOut2 R 2 1 6 3 0 LineOut3 L 6 2 6 3 0 LineOut3 R 3 1 6 4 0 SPDIFOut L 7 2 6 4 0 SPDIFOut R 8 9 13 10 0 MidiOut 1 0 0 1 5 48000 -1 1 1 6 3 0 LineIn1 L 3 2 6 3 0 LineIn1 R 0 1 6 4 0 SPDIFIn L 2 2 6 4 0 SPDIFIn R 4 5 13 10 0 MidiIn 1 1 44100 8 1 48000 8 1 88200 8 1 96000 8 1 0 44100 4 1 48000 4 1 88200 4 1 96000 4 1 libffado-2.4.5/src/bebob/maudio/fwap.xml0000644000175000001440000001055612233357644017475 0ustar jwoitheusers 0 Connection Information for M-Audio, FW Audiophile configuration M-Audio FW Audiophile 0x000D6C031088FA68 1 0 1 0 7 44100 -1 0 2 1 6 3 0 LineOut1 L 4 2 6 3 0 LineOut1 R 3 1 6 3 0 LineOut2 L 5 2 6 3 0 LineOut2 R 0 1 6 4 1 SPDIFOut L 1 2 6 4 1 SPDIFOut R 6 0 13 10 0 MidiPort 0 0 1 0 5 44100 -1 1 0 1 6 3 1 SPDIFIn1 L 1 2 6 3 1 SPDIFIn1 R 2 1 6 3 0 LineIn1 L 3 2 6 3 0 LineIn1 R 4 0 13 10 0 MidiPort 1 44100 6 1 0 44100 4 1 libffado-2.4.5/src/bebob/presonus/0000755000175000001440000000000014206145612016377 5ustar jwoitheuserslibffado-2.4.5/src/bebob/presonus/firebox_avdevice.cpp0000644000175000001440000001153614206145246022420 0ustar jwoitheusers/* * Copyright (C) 2013 by Takashi Sakamoto * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "firebox_avdevice.h" namespace BeBoB { namespace Presonus { namespace Firebox { Device::Device(DeviceManager& d, ffado_smartptr(configRom)) : BeBoB::Device( d, configRom) { m_intl_clksrc.type = FFADODevice::eCT_Internal; m_intl_clksrc.valid = true; m_intl_clksrc.locked = true; m_intl_clksrc.id = 0x00; m_intl_clksrc.slipping = false; m_intl_clksrc.description = "Internal"; m_spdif_clksrc.type = FFADODevice::eCT_SPDIF; m_spdif_clksrc.valid = true; m_spdif_clksrc.locked = true; m_spdif_clksrc.id = 0x01; m_spdif_clksrc.slipping = false; m_spdif_clksrc.description = "S/PDIF (Coaxial)"; debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::Presonus::Firebox::Device (NodeID %d)\n", getConfigRom().getNodeId() ); } Device::~Device() { } void Device::showDevice() { debugOutput(DEBUG_LEVEL_NORMAL, "This is a BeBoB::Presonus::Firebox::Device\n"); BeBoB::Device::showDevice(); } FFADODevice::ClockSourceVector Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; r.push_back(m_intl_clksrc); r.push_back(m_spdif_clksrc); return r; } enum FFADODevice::eClockSourceType Device::getClkSrc() { AVC::SignalSourceCmd cmd(get1394Service()); cmd.setCommandType(AVC::AVCCommand::eCT_Status); cmd.setNodeId(getNodeId()); cmd.setSubunitType(AVC::eST_Unit); cmd.setSubunitId(0xff); cmd.setVerbose(getDebugLevel()); AVC::SignalSubunitAddress dst; dst.m_subunitType = AVC::eST_Music; dst.m_subunitId = 0x00; dst.m_plugId = 0x05; cmd.setSignalDestination(dst); if (!cmd.fire()) { debugError( "Signal source command failed\n" ); return eCT_Invalid; } AVC::SignalAddress* pSyncPlugSignalAddress = cmd.getSignalSource(); AVC::SignalSubunitAddress* pSyncPlugSubunitAddress = dynamic_cast( pSyncPlugSignalAddress ); if (pSyncPlugSubunitAddress) { debugOutput(DEBUG_LEVEL_VERBOSE, "Sync mode 0x%02x\n", ( pSyncPlugSubunitAddress->m_subunitType << 3 | pSyncPlugSubunitAddress->m_subunitId ) << 8 | pSyncPlugSubunitAddress->m_plugId ); return eCT_Internal; } AVC::SignalUnitAddress* pSyncPlugUnitAddress = dynamic_cast( pSyncPlugSignalAddress ); if (pSyncPlugUnitAddress) { debugOutput(DEBUG_LEVEL_VERBOSE, "Sync mode 0x%02x\n", 0xff << 8 | pSyncPlugUnitAddress->m_plugId ); return eCT_SPDIF; } debugError( "Could not retrieve sync mode\n" ); return eCT_Invalid; } FFADODevice::ClockSource Device::getActiveClockSource() { switch (getClkSrc()) { case eCT_Internal: m_intl_clksrc.active = true; m_spdif_clksrc.active = false; return m_intl_clksrc; case eCT_SPDIF: m_intl_clksrc.active = false; m_spdif_clksrc.active = true; return m_spdif_clksrc; default: ClockSource s; s.type = eCT_Invalid; return s; } } bool Device::setActiveClockSource(ClockSource s) { AVC::SignalSourceCmd cmd(get1394Service()); cmd.setCommandType(AVC::AVCCommand::eCT_Control); cmd.setNodeId(getNodeId()); cmd.setSubunitType(AVC::eST_Unit); cmd.setSubunitId(0xff); cmd.setVerbose(getDebugLevel()); AVC::SignalSubunitAddress dst; dst.m_subunitType = AVC::eST_Music; dst.m_subunitId = 0x00; dst.m_plugId = 0x05; cmd.setSignalDestination(dst); if (s.id == 0x00) { AVC::SignalSubunitAddress src; src.m_subunitType = AVC::eST_Music; src.m_subunitId = 0x00; src.m_plugId = 0x06; cmd.setSignalSource( src ); } else { AVC::SignalUnitAddress src; src.m_plugId = 0x83; cmd.setSignalSource(src); } if (!cmd.fire()) { debugError( "Signal source command failed\n" ); return false; } return true; } } // namespace Firebox } // namespace Presonus } // namespace BeBoB libffado-2.4.5/src/bebob/presonus/firebox_avdevice.h0000644000175000001440000000323214206145246022057 0ustar jwoitheusers/* * Copyright (C) 2013 by Takashi Sakamoto * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_PRESONUS_DEVICE_H #define BEBOB_PRESONUS_DEVICE_H #include "debugmodule/debugmodule.h" #include "bebob/bebob_avdevice.h" namespace BeBoB { namespace Presonus { namespace Firebox { class Device : public BeBoB::Device { public: Device( DeviceManager& d, ffado_smartptr( configRom )); virtual ~Device(); virtual void showDevice(); virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); private: enum FFADODevice::eClockSourceType getClkSrc(); ClockSource m_intl_clksrc; ClockSource m_spdif_clksrc; ClockSource *m_active_clksrc; }; } // namespace Firebox } // namespace Presonus } // namespace BeBoB #endif libffado-2.4.5/src/bebob/presonus/inspire1394_avdevice.cpp0000644000175000001440000001346114206145246022753 0ustar jwoitheusers/* * Copyright (C) 2014 by Takashi Sakamoto * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "./inspire1394_avdevice.h" #include "libutil/ByteSwap.h" #include "libutil/cmd_serialize.h" namespace BeBoB { namespace Presonus { namespace Inspire1394 { Command::Command(Ieee1394Service& ieee1394service) : VendorDependentCmd( ieee1394service ) , m_subfunc( 0x00 ) , m_idx( 0x00 ) , m_arg( 0x00 ) { m_companyId = 0x000a92; setSubunitType( AVC::eST_Audio ); setSubunitId( 0x00 ); } bool Command::serialize( Util::Cmd::IOSSerialize& se ) { bool result = true; result &= VendorDependentCmd::serialize( se ); result &= se.write(m_subfunc, "Cmd subfunc"); result &= se.write(m_idx, "Cmd idx"); result &= se.write(m_arg, "Cmd arg"); return result; } bool Command::deserialize( Util::Cmd::IISDeserialize& de ) { bool result = true; result &= VendorDependentCmd::deserialize( de ); result &= de.read( &m_subfunc); result &= de.read( &m_idx ); result &= de.read( &m_arg ); return result; } BinaryControl::BinaryControl(Device& parent, ECmdSubfunc subfunc, std::string name, std::string label, std::string desc) : Control::Discrete(&parent) , m_Parent(parent) , m_subfunc( subfunc ) { setName(name); setLabel(label); setDescription(desc); } bool BinaryControl::setValue(int idx, int v) { uint8_t val = v; debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for type: %d, idx: %d, val: %d\n", m_subfunc, idx, val); if ( !m_Parent.setSpecificValue(m_subfunc, idx, val) ) { debugError( "setSpecificValue failed\n" ); return false; } return true; } int BinaryControl::getValue(int idx) { uint8_t val; if ( !m_Parent.getSpecificValue(m_subfunc, idx, &val) ) { debugError( "getSpecificValue failed\n" ); return 0; } debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for type: %d, idx: %d, val: %d\n", m_subfunc, idx, val); return val; } Device::Device(DeviceManager& d, ffado_smartptr(configRom)) : BeBoB::Device( d, configRom ) { addSpecificControls(); } Device::~Device(void) { } void Device::showDevice() { debugOutput(DEBUG_LEVEL_NORMAL, "This is a BeBoB::Presonus::Inspire1394::Device\n"); BeBoB::Device::showDevice(); } bool Device::addSpecificControls(void) { Control::Container *ctls; BinaryControl *ctl; bool result = true; debugOutput(DEBUG_LEVEL_VERBOSE, "Building a PreSonus Inspire1394 mixer...\n"); ctls = new Control::Container(this, "Preamp"); if ( !addElement(ctls) ) { debugWarning("Could not register specific controls to device\n"); delete ctls; return false; } // RIAA equalization curve for Analog In 3/4 ctl = new BinaryControl(*this, ECmdSubfuncPhono, "PhonoSwitch", "Phono Switch", "Phono Switch"); result &= ctls->addElement(ctl); // 48V for Analog In 1/2 ctl = new BinaryControl(*this, ECmdSubfuncPhantom, "PhantomPower", "Phantom Power", "Phantom Power"); result &= ctls->addElement(ctl); // +20dB for Analog In 1/2 ctl = new BinaryControl(*this, ECmdSubfuncBoost, "MicBoost", "Mic Boost", "Mic Boost"); result &= ctls->addElement(ctl); // Limitter of preamp for Analog In 1/2 ctl = new BinaryControl(*this, ECmdSubfuncLimit, "MicLimit", "Mic Limit", "Mic Limit"); result &= ctls->addElement(ctl); if ( !result ) { debugWarning("Any controls could not be added\n"); destroyMixer(); return false; } return true; } bool Device::getSpecificValue(ECmdSubfunc subfunc, int idx, uint8_t *val) { Command cmd( get1394Service() ); cmd.setCommandType( AVC::AVCCommand::eCT_Status ); cmd.setNodeId( getConfigRom().getNodeId() ); cmd.setVerbose( getDebugLevel() ); cmd.setSubfunc(subfunc); cmd.setIdx(idx); cmd.setArg(0xff); if ( !cmd.fire() ) { debugError( "Cmd failed\n" ); return false; } else if (cmd.getResponse() != AVC::AVCCommand::eR_Implemented ) { debugError("Cmd received error response\n"); return false; } *val = cmd.getArg(); return true; } bool Device::setSpecificValue(ECmdSubfunc subfunc, int idx, uint8_t val) { Command cmd( get1394Service() ); cmd.setCommandType( AVC::AVCCommand::eCT_Control ); cmd.setNodeId( getConfigRom().getNodeId() ); cmd.setVerbose( getDebugLevel() ); cmd.setSubfunc(subfunc); cmd.setIdx(idx); cmd.setArg(val); if ( !cmd.fire() ) { debugError( "Cmd failed\n" ); return false; } else if (cmd.getResponse() != AVC::AVCCommand::eR_Accepted) { debugError("Cmd received error response\n"); return false; } return true; } } // namespace Inspire1394 } // namespace Presonus } // namespace BeBoB libffado-2.4.5/src/bebob/presonus/inspire1394_avdevice.h0000644000175000001440000000573714206145246022427 0ustar jwoitheusers/* * Copyright (C) 2014 by Takashi Sakamoto * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_PRESONUS_INSPIRE1394_DEVICE_H #define BEBOB_PRESONUS_INSPIRE1394_DEVICE_H #include "debugmodule/debugmodule.h" #include "bebob/bebob_avdevice.h" #include "libavc/general/avc_vendor_dependent_cmd.h" namespace BeBoB { namespace Presonus { namespace Inspire1394 { enum ECmdSubfunc { ECmdSubfuncPhono = 0, ECmdSubfuncPhantom, ECmdSubfuncBoost, ECmdSubfuncLimit }; class Device : public BeBoB::Device { public: Device( DeviceManager& d, ffado_smartptr( configRom )); virtual ~Device(); virtual void showDevice(); bool setSpecificValue(ECmdSubfunc subfunc, int idx, uint8_t val); bool getSpecificValue(ECmdSubfunc subfunc, int idx, uint8_t *val); private: bool addSpecificControls(void); }; class Command : public AVC::VendorDependentCmd { public: Command( Ieee1394Service& ieee1394service ); virtual ~Command() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCommandName() const { return "Inspire1394"; } virtual void setSubfunc(uint8_t subfunc) { m_subfunc = subfunc; } virtual void setIdx(int idx) { m_idx = idx; } virtual void setArg(int arg) { m_arg = arg; } virtual int getArg(void) { return m_arg; } protected: uint8_t m_subfunc; uint8_t m_idx; uint8_t m_arg; }; class BinaryControl : public Control::Discrete { public: BinaryControl(Device& parent, ECmdSubfunc subfunc, std::string name, std::string label, std::string desc); virtual bool setValue(int idx, int val); virtual int getValue(int idx); virtual bool setValue(int val) { return setValue(0, val); } virtual int getValue(void) { return getValue(0); } virtual int getMinimum() { return 0; }; virtual int getMaximum() { return 1; }; private: Device& m_Parent; ECmdSubfunc m_subfunc; }; } // namespace Inspire1394 } // namespace Presonus } // namespace BeBoB #endif libffado-2.4.5/src/bebob/template/0000755000175000001440000000000014206145612016334 5ustar jwoitheuserslibffado-2.4.5/src/bebob/template/vendor_device.cpp0000644000175000001440000000265414206145246021666 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "__vendor___device.h" namespace BeBoB { namespace __Vendor__ { VendorDevice::VendorDevice( Ieee1394Service& ieee1394Service, ffado_smartptr( configRom )) : BeBoB::Device( ieee1394Service, configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::__Vendor__::VendorDevice (NodeID %d)\n", getConfigRom().getNodeId() ); } VendorDevice::~VendorDevice() { } void VendorDevice::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a BeBoB::__Vendor__::VendorDevice\n"); BeBoB::Device::showDevice(); } } // __Vendor__ } // BeBoB libffado-2.4.5/src/bebob/template/vendor_device.h0000644000175000001440000000247614206145246021335 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB___VENDOR___DEVICE_H #define BEBOB___VENDOR___DEVICE_H #include "debugmodule/debugmodule.h" #include "bebob/bebob_avdevice.h" namespace BeBoB { namespace __Vendor__ { class VendorDevice : public BeBoB::Device { public: VendorDevice( Ieee1394Service& ieee1394Service, ffado_smartptr( configRom )); virtual ~VendorDevice(); virtual void showDevice(); }; } // namespace __Vendor__ } // namespace BeBoB #endif libffado-2.4.5/src/bebob/terratec/0000755000175000001440000000000014206145612016332 5ustar jwoitheuserslibffado-2.4.5/src/bebob/terratec/terratec_cmd.cpp0000644000175000001440000001302314206145246021474 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "terratec_cmd.h" #include "libutil/ByteSwap.h" #include using namespace std; using namespace AVC; namespace BeBoB { namespace Terratec { TerratecVendorDependentCmd::TerratecVendorDependentCmd(Ieee1394Service& ieee1394service) : VendorDependentCmd( ieee1394service ) , m_subfunction ( 0x00 ) { m_companyId=0x000aac; } bool TerratecVendorDependentCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= VendorDependentCmd::serialize( se ); result &= se.write(m_subfunction,"TerratecVendorDependentCmd subfunction"); return result; } bool TerratecVendorDependentCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= VendorDependentCmd::deserialize( de ); result &= de.read(&m_subfunction); return result; } //--------- TerratecSyncStateCmd::TerratecSyncStateCmd(Ieee1394Service& ieee1394service) : TerratecVendorDependentCmd( ieee1394service ) { m_subfunction=0x21; } bool TerratecSyncStateCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= TerratecVendorDependentCmd::serialize( se ); result &= se.write(m_syncstate,"TerratecSyncStateCmd m_syncstate"); return result; } bool TerratecSyncStateCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= TerratecVendorDependentCmd::deserialize( de ); result &= de.read(&m_syncstate); return result; } //--------- TerratecStoreMixerSettingsCmd::TerratecStoreMixerSettingsCmd(Ieee1394Service& ieee1394service) : TerratecVendorDependentCmd( ieee1394service ) { m_subfunction=0x22; } bool TerratecStoreMixerSettingsCmd::serialize( Util::Cmd::IOSSerialize& se ) { return TerratecVendorDependentCmd::serialize( se );; } bool TerratecStoreMixerSettingsCmd::deserialize( Util::Cmd::IISDeserialize& de ) { return TerratecVendorDependentCmd::deserialize( de );; } //--------- TerratecSetMidiRemoteChannelCmd::TerratecSetMidiRemoteChannelCmd(Ieee1394Service& ieee1394service) : TerratecVendorDependentCmd( ieee1394service ) { m_subfunction=0x23; } bool TerratecSetMidiRemoteChannelCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= TerratecVendorDependentCmd::serialize( se ); result &= se.write(m_midichannel,"TerratecSetMidiRemoteChannelCmd m_midichannel"); return result; } bool TerratecSetMidiRemoteChannelCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= TerratecVendorDependentCmd::deserialize( de ); result &= de.read(&m_midichannel); return result; } //--------- TerratecSetMidiControlCmd::TerratecSetMidiControlCmd(Ieee1394Service& ieee1394service) : TerratecVendorDependentCmd( ieee1394service ) { m_subfunction=0x24; } bool TerratecSetMidiControlCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= TerratecVendorDependentCmd::serialize( se ); result &= se.write(m_mixercontrol,"TerratecSetMidiControlCmd m_mixercontrol"); result &= se.write(m_midicontroller,"TerratecSetMidiControlCmd m_midicontroller"); return result; } bool TerratecSetMidiControlCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= TerratecVendorDependentCmd::deserialize( de ); result &= de.read(&m_mixercontrol); result &= de.read(&m_midicontroller); return result; } //--------- TerratecSetDefaultRoutingCmd::TerratecSetDefaultRoutingCmd(Ieee1394Service& ieee1394service) : TerratecVendorDependentCmd( ieee1394service ) { m_subfunction=0x25; } bool TerratecSetDefaultRoutingCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= TerratecVendorDependentCmd::serialize( se ); result &= se.write(m_output,"TerratecSetDefaultRoutingCmd m_output"); result &= se.write(m_source,"TerratecSetDefaultRoutingCmd m_source"); return result; } bool TerratecSetDefaultRoutingCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= TerratecVendorDependentCmd::deserialize( de ); result &= de.read(&m_output); result &= de.read(&m_source); return result; } //--------- TerratecDeviceIdCmd::TerratecDeviceIdCmd(Ieee1394Service& ieee1394service) : TerratecVendorDependentCmd( ieee1394service ) { m_subfunction=0x26; } bool TerratecDeviceIdCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= TerratecVendorDependentCmd::serialize( se ); result &= se.write(m_deviceid,"TerratecDeviceIdCmd m_deviceid"); return result; } bool TerratecDeviceIdCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= TerratecVendorDependentCmd::deserialize( de ); result &= de.read(&m_deviceid); return result; } } } libffado-2.4.5/src/bebob/terratec/terratec_cmd.h0000644000175000001440000001006614206145246021145 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef TERRATECVENDORDEPENDENT_H #define TERRATECVENDORDEPENDENT_H #include "libavc/general/avc_generic.h" #include "libutil/cmd_serialize.h" #include "libavc/general/avc_vendor_dependent_cmd.h" namespace BeBoB { namespace Terratec { class TerratecVendorDependentCmd: public AVC::VendorDependentCmd { public: TerratecVendorDependentCmd(Ieee1394Service& ieee1394service); virtual ~TerratecVendorDependentCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "TerratecVendorDependentCmd"; } protected: byte_t m_subfunction; }; class TerratecSyncStateCmd: public TerratecVendorDependentCmd { public: TerratecSyncStateCmd(Ieee1394Service& ieee1394service); virtual ~TerratecSyncStateCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "TerratecSyncStateCmd"; } byte_t m_syncstate; }; class TerratecStoreMixerSettingsCmd: public TerratecVendorDependentCmd { public: TerratecStoreMixerSettingsCmd(Ieee1394Service& ieee1394service); virtual ~TerratecStoreMixerSettingsCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "TerratecStoreMixerSettingsCmd"; } }; class TerratecSetMidiRemoteChannelCmd: public TerratecVendorDependentCmd { public: TerratecSetMidiRemoteChannelCmd(Ieee1394Service& ieee1394service); virtual ~TerratecSetMidiRemoteChannelCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "TerratecSetMidiRemoteChannelCmd"; } byte_t m_midichannel; }; class TerratecSetMidiControlCmd: public TerratecVendorDependentCmd { public: TerratecSetMidiControlCmd(Ieee1394Service& ieee1394service); virtual ~TerratecSetMidiControlCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "TerratecSetMidiControlCmd"; } byte_t m_mixercontrol; byte_t m_midicontroller; }; class TerratecSetDefaultRoutingCmd: public TerratecVendorDependentCmd { public: TerratecSetDefaultRoutingCmd(Ieee1394Service& ieee1394service); virtual ~TerratecSetDefaultRoutingCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "TerratecSetDefaultRoutingCmd"; } byte_t m_output; byte_t m_source; }; class TerratecDeviceIdCmd: public TerratecVendorDependentCmd { public: TerratecDeviceIdCmd(Ieee1394Service& ieee1394service); virtual ~TerratecDeviceIdCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "TerratecDeviceIdCmd"; } byte_t m_deviceid; }; } } #endif // TERRATECVENDORDEPENDENT_H libffado-2.4.5/src/bebob/terratec/terratec_device.cpp0000644000175000001440000001034314206145246022172 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "terratec_device.h" #include "src/bebob/bebob_dl_mgr.h" #include "src/bebob/bebob_dl_bcd.h" namespace BeBoB { namespace Terratec { Phase88Device::Phase88Device(DeviceManager& d, ffado_smartptr( configRom )) : BeBoB::Device( d, configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::Terratec::Phase88Device (NodeID %d)\n", getConfigRom().getNodeId() ); updateClockSources(); } Phase88Device::~Phase88Device() { } void Phase88Device::showDevice() { debugOutput(DEBUG_LEVEL_NORMAL, "This is a BeBoB::Terratec::Phase88Device\n"); BeBoB::Device::showDevice(); } bool Phase88Device::discover() { BeBoB::BootloaderManager blMgr( get1394Service(), getNodeId() ); blMgr.printInfoRegisters(); if (blMgr.getSoftwareVersion() < 0x01120d1f) { debugError("The firmware of this Phase88 device is too old. Please update the firmware.\n"); return false; } return BeBoB::Device::discover(); } void Phase88Device::updateClockSources() { m_internal_clocksource.type = FFADODevice::eCT_Internal; m_internal_clocksource.valid = true; m_internal_clocksource.locked = true; m_internal_clocksource.id = 0; m_internal_clocksource.slipping = false; m_internal_clocksource.description = "Internal"; m_spdif_clocksource.type = FFADODevice::eCT_SPDIF; m_spdif_clocksource.valid = true; m_spdif_clocksource.locked = false; m_spdif_clocksource.id = 1; m_spdif_clocksource.slipping = false; m_spdif_clocksource.description = "S/PDIF"; m_wordclock_clocksource.type = FFADODevice::eCT_WordClock; m_wordclock_clocksource.valid = true; m_wordclock_clocksource.locked = false; m_wordclock_clocksource.id = 2; m_wordclock_clocksource.slipping = false; m_wordclock_clocksource.description = "WordClock"; } FFADODevice::ClockSource Phase88Device::getActiveClockSource() { int fb_extsync_value = getSelectorFBValue(8); int fb_syncsource_value = getSelectorFBValue(9); debugOutput(DEBUG_LEVEL_VERBOSE, "Selectors: 0x%02X 0x%02X\n", fb_extsync_value, fb_syncsource_value); if(fb_syncsource_value == 0) { return m_internal_clocksource; } else { if(fb_extsync_value == 0) { return m_spdif_clocksource; } else { return m_wordclock_clocksource; } } } bool Phase88Device::setActiveClockSource(ClockSource s) { if(s.id == m_internal_clocksource.id) { return setSelectorFBValue(9, 0); } if(s.id == m_spdif_clocksource.id) { bool retval = true; retval &= setSelectorFBValue(8, 0); retval &= setSelectorFBValue(9, 1); return retval; } if(s.id == m_wordclock_clocksource.id) { bool retval = true; retval &= setSelectorFBValue(8, 1); retval &= setSelectorFBValue(9, 1); return retval; } return false; } FFADODevice::ClockSourceVector Phase88Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; r.push_back(m_internal_clocksource); r.push_back(m_spdif_clocksource); r.push_back(m_wordclock_clocksource); return r; } uint16_t Phase88Device::getConfigurationIdSyncMode() { uint8_t fb_extsync_value = getSelectorFBValue(8); uint8_t fb_syncsource_value = getSelectorFBValue(9); return (fb_extsync_value & 0x01) | ((fb_syncsource_value << 1) & 0x01); } } // namespace Terratec } // namespace BeBoB libffado-2.4.5/src/bebob/terratec/terratec_device.h0000644000175000001440000000344014206145246021637 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_TERRATEC_DEVICE_H #define BEBOB_TERRATEC_DEVICE_H #include "debugmodule/debugmodule.h" #include "bebob/bebob_avdevice.h" namespace BeBoB { namespace Terratec { class Phase88Device : public BeBoB::Device { public: Phase88Device( DeviceManager& d, ffado_smartptr( configRom )); virtual ~Phase88Device(); virtual bool discover(); virtual void showDevice(); // override these since the phase88 does not support // the usual clock source selection mechanism virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); protected: virtual uint16_t getConfigurationIdSyncMode(); private: void updateClockSources(); ClockSource m_internal_clocksource; ClockSource m_spdif_clocksource; ClockSource m_wordclock_clocksource; }; } // namespace Terratec } // namespace BeBoB #endif libffado-2.4.5/src/bebob/yamaha/0000755000175000001440000000000014206145612015761 5ustar jwoitheuserslibffado-2.4.5/src/bebob/yamaha/yamaha_avdevice.cpp0000644000175000001440000000673214206145246021606 0ustar jwoitheusers/* * Copyright (C) 2013 by Takashi Sakamoto * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "yamaha_avdevice.h" #include "yamaha_cmd.h" namespace BeBoB { namespace Yamaha { GoDevice::GoDevice(DeviceManager& d, ffado_smartptr( configRom )) : BeBoB::Device( d, configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::Yamaha::GoDevice (NodeID %d)\n", getConfigRom().getNodeId() ); updateClockSources(); } GoDevice::~GoDevice() { } void GoDevice::showDevice() { debugOutput(DEBUG_LEVEL_NORMAL, "This is a BeBoB::Yamaha::GoDevice\n"); BeBoB::Device::showDevice(); } bool GoDevice::updateClockSources() { int err; m_internal_clocksource.type = FFADODevice::eCT_Internal; m_internal_clocksource.valid = true; m_internal_clocksource.active = false; m_internal_clocksource.locked = true; m_internal_clocksource.id = 0; m_internal_clocksource.slipping = false; m_internal_clocksource.description = "Internal"; m_spdif_clocksource.type = FFADODevice::eCT_SPDIF; m_spdif_clocksource.valid = true; m_spdif_clocksource.active = false; m_spdif_clocksource.locked = false; m_spdif_clocksource.id = 1; m_spdif_clocksource.slipping = false; m_spdif_clocksource.description = "S/PDIF"; /* detect digital input */ YamahaDigInDetectCmd cmd ( get1394Service() ); cmd.setCommandType( AVC::AVCCommand::eCT_Status ); cmd.setNodeId( getConfigRom().getNodeId() ); cmd.setVerbose( getDebugLevel() ); if ( !cmd.fire() ) { debugError( "YamahaDigInDetectCmd failed\n" ); return false; } else if (cmd.m_digin == 0) { m_spdif_clocksource.locked = true; } /* get current clock source */ err = getSelectorFBValue(4); if (err < 0) return err; else if (err > 0) { m_active_clocksource = &m_spdif_clocksource; m_spdif_clocksource.active = true; } else { m_active_clocksource = &m_internal_clocksource; m_internal_clocksource.active = true; } return true; } FFADODevice::ClockSource GoDevice::getActiveClockSource() { if (!updateClockSources()) { ClockSource s; s.type = eCT_Invalid; return s; } return *m_active_clocksource; } bool GoDevice::setActiveClockSource(ClockSource s) { if (!updateClockSources()) return false; if ((s.id > 0) || (!m_spdif_clocksource.locked)) return false; return setSelectorFBValue(4, s.id); } FFADODevice::ClockSourceVector GoDevice::getSupportedClockSources() { FFADODevice::ClockSourceVector r; r.push_back(m_internal_clocksource); r.push_back(m_spdif_clocksource); return r; } } // namespace Yamaha } // namespace BeBoB libffado-2.4.5/src/bebob/yamaha/yamaha_avdevice.h0000644000175000001440000000333314206145246021245 0ustar jwoitheusers/* * Copyright (C) 2013 by Takashi Sakamoto * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BEBOB_YAMAHA_DEVICE_H #define BEBOB_YAMAHA_DEVICE_H #include "debugmodule/debugmodule.h" #include "bebob/bebob_avdevice.h" namespace BeBoB { namespace Yamaha { class GoDevice : public BeBoB::Device { public: GoDevice( DeviceManager& d, ffado_smartptr( configRom )); virtual ~GoDevice(); virtual void showDevice(); // override these since the go series does not support // the usual clock source selection mechanism virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); private: bool updateClockSources(); ClockSource m_internal_clocksource; ClockSource m_spdif_clocksource; ClockSource *m_active_clocksource; }; } // namespace Yamaha } // namespace BeBoB #endif libffado-2.4.5/src/bebob/yamaha/yamaha_cmd.cpp0000644000175000001440000000637414206145246020565 0ustar jwoitheusers/* * Copyright (C) 2013 by Takashi Sakamoto * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "yamaha_cmd.h" #include "libutil/ByteSwap.h" #include using namespace std; using namespace AVC; namespace BeBoB { namespace Yamaha { YamahaVendorDependentCmd::YamahaVendorDependentCmd(Ieee1394Service& ieee1394service) : VendorDependentCmd( ieee1394service ) , m_subfunction ( 0x00 ) { /* company id is different between commands */ m_companyId = 0x000000; } bool YamahaVendorDependentCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= VendorDependentCmd::serialize( se ); result &= se.write(m_subfunction,"YamahaVendorDependentCmd subfunction"); return result; } bool YamahaVendorDependentCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= VendorDependentCmd::deserialize( de ); result &= de.read(&m_subfunction); return result; } //--------- YamahaSyncStateCmd::YamahaSyncStateCmd(Ieee1394Service& ieee1394service) : YamahaVendorDependentCmd( ieee1394service ) { m_companyId = 0x010203; m_subfunction = 0x21; m_syncstate = 0x00; } bool YamahaSyncStateCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= YamahaVendorDependentCmd::serialize( se ); result &= se.write(m_syncstate,"YamahaSyncStateCmd m_syncstate"); return result; } bool YamahaSyncStateCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= YamahaVendorDependentCmd::deserialize( de ); result &= de.read(&m_syncstate); return result; } //--------- YamahaDigInDetectCmd::YamahaDigInDetectCmd(Ieee1394Service& ieee1394service) : YamahaVendorDependentCmd( ieee1394service ) { m_companyId = 0x0007f5; m_subfunction = 0x00; m_digin = 0x00; } bool YamahaDigInDetectCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; uint8_t buf[] = {0x00, 0x01}; result &= YamahaVendorDependentCmd::serialize( se ); result &= se.write(buf[0], "YamahaDigInDetectCmd Unknown 1"); result &= se.write(buf[1], "YamahaDigInDetectCmd Unknown 2"); result &= se.write(m_digin, "YamahaDigInDetectCmd m_digin"); return result; } bool YamahaDigInDetectCmd::deserialize( Util::Cmd::IISDeserialize& de ) { uint8_t tmp; bool result = true; result &= YamahaVendorDependentCmd::deserialize( de ); result &= de.read(&tmp); result &= de.read(&tmp); result &= de.read(&m_digin); return result; } } } libffado-2.4.5/src/bebob/yamaha/yamaha_cmd.h0000644000175000001440000000452214206145246020223 0ustar jwoitheusers/* * Copyright (C) 2013 by Takashi Sakamoto * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef YAMAHA_VENDOR_DEPENDENT_H #define YAMAHA_VENDOR_DEPENDENT_H #include "libavc/general/avc_generic.h" #include "libutil/cmd_serialize.h" #include "libavc/general/avc_vendor_dependent_cmd.h" namespace BeBoB { namespace Yamaha { class YamahaVendorDependentCmd: public AVC::VendorDependentCmd { public: YamahaVendorDependentCmd(Ieee1394Service& ieee1394service); virtual ~YamahaVendorDependentCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "YamahaVendorDependentCmd"; } protected: byte_t m_subfunction; }; class YamahaSyncStateCmd: public YamahaVendorDependentCmd { public: YamahaSyncStateCmd(Ieee1394Service& ieee1394service); virtual ~YamahaSyncStateCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "YamahaSyncStateCmd"; } byte_t m_syncstate; }; class YamahaDigInDetectCmd: public YamahaVendorDependentCmd { public: YamahaDigInDetectCmd(Ieee1394Service& ieee1394service); virtual ~YamahaDigInDetectCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "YamahaDigInDetectCmd"; } byte_t m_digin; }; } } #endif // YAMAHA_VENDOR_DEPENDENT_H libffado-2.4.5/src/bounce/0000755000175000001440000000000014206145612014723 5ustar jwoitheuserslibffado-2.4.5/src/bounce/bounce_avdevice.cpp0000644000175000001440000004112614206145246020557 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "bounce/bounce_avdevice.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/Configuration.h" #include "debugmodule/debugmodule.h" #include "devicemanager.h" #include #include #include #include #include "libutil/ByteSwap.h" namespace Bounce { Device::Device( DeviceManager& d, ffado_smartptr< ConfigRom >( configRom ) ) : FFADODevice( d, configRom ) , m_samplerate (44100) , m_Notifier ( NULL ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Bounce::Device (NodeID %d)\n", getConfigRom().getNodeId() ); addOption(Util::OptionContainer::Option("snoopMode",false)); } Device::~Device() { } bool Device::probe( Util::Configuration& c, ConfigRom& configRom, bool generic ) { if(generic) { return false; } else { // check if device is in supported devices list unsigned int vendorId = configRom.getNodeVendorId(); unsigned int modelId = configRom.getModelId(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); return c.isValid(vme) && vme.driver == Util::Configuration::eD_Bounce; } return false; } FFADODevice * Device::createDevice(DeviceManager& d, ffado_smartptr( configRom )) { return new Device(d, configRom ); } bool Device::discover() { debugOutput( DEBUG_LEVEL_VERBOSE, "discovering Device (NodeID %d)\n", getNodeId() ); unsigned int vendorId = getConfigRom().getNodeVendorId(); unsigned int modelId = getConfigRom().getModelId(); Util::Configuration &c = getDeviceManager().getConfiguration(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); if (c.isValid(vme) && vme.driver == Util::Configuration::eD_Bounce) { debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", vme.vendor_name.c_str(), vme.model_name.c_str()); } else { debugWarning("Using generic Bounce support for unsupported device '%s %s'\n", getConfigRom().getVendorName().c_str(), getConfigRom().getModelName().c_str()); } return true; } int Device::getSamplingFrequency( ) { return m_samplerate; } bool Device::setSamplingFrequency( int s ) { if (s) { m_samplerate=s; return true; } else return false; } FFADODevice::ClockSourceVector Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; return r; } bool Device::setActiveClockSource(ClockSource s) { return false; } FFADODevice::ClockSource Device::getActiveClockSource() { ClockSource s; return s; } std::vector Device::getSupportedSamplingFrequencies() { std::vector frequencies; return frequencies; } bool Device::lock() { return true; } bool Device::unlock() { return true; } void Device::showDevice() { debugOutput(DEBUG_LEVEL_NORMAL, "\nI am the bouncedevice, the bouncedevice I am...\n" ); debugOutput(DEBUG_LEVEL_NORMAL, "Vendor : %s\n", getConfigRom().getVendorName().c_str()); debugOutput(DEBUG_LEVEL_NORMAL, "Model : %s\n", getConfigRom().getModelName().c_str()); debugOutput(DEBUG_LEVEL_NORMAL, "Node : %d\n", getNodeId()); debugOutput(DEBUG_LEVEL_NORMAL, "GUID : 0x%016llX\n", getConfigRom().getGuid()); debugOutput(DEBUG_LEVEL_NORMAL, "\n" ); } bool Device::addPortsToProcessor( Streaming::StreamProcessor *processor, Streaming::Port::E_Direction direction) { debugOutput(DEBUG_LEVEL_VERBOSE,"Adding ports to processor\n"); std::string id=std::string("dev?"); if(!getOption("id", id)) { debugWarning("Could not retrieve id parameter, defauling to 'dev?'\n"); } int i=0; for (i=0;iinit()) { debugFatal("Could not initialize receive processor!\n"); delete p; return false; } if (!addPortsToProcessor(p, Streaming::Port::E_Capture)) { debugFatal("Could not add plug to processor!\n"); delete p; return false; } m_receiveProcessors.push_back(p); // do the transmit processor if (snoopMode) { // we are snooping, so this is receive too. p=new Streaming::AmdtpReceiveStreamProcessor(*this, BOUNCE_NB_AUDIO_CHANNELS+(BOUNCE_NB_MIDI_CHANNELS?1:0)); } else { p=new Streaming::AmdtpTransmitStreamProcessor(*this, BOUNCE_NB_AUDIO_CHANNELS+(BOUNCE_NB_MIDI_CHANNELS?1:0)); } if(!p->init()) { debugFatal("Could not initialize transmit processor %s!\n", (snoopMode?" in snoop mode":"")); delete p; return false; } if (snoopMode) { if (!addPortsToProcessor(p, Streaming::Port::E_Capture)) { debugFatal("Could not add plug to processor!\n"); delete p; return false; } m_receiveProcessors.push_back(p); } else { if (!addPortsToProcessor(p, Streaming::Port::E_Playback)) { debugFatal("Could not add plug to processor!\n"); delete p; return false; } m_transmitProcessors.push_back(p); } return true; } int Device::getStreamCount() { return m_receiveProcessors.size() + m_transmitProcessors.size(); } Streaming::StreamProcessor * Device::getStreamProcessorByIndex(int i) { if (i<(int)m_receiveProcessors.size()) { return m_receiveProcessors.at(i); } else if (i<(int)m_receiveProcessors.size() + (int)m_transmitProcessors.size()) { return m_transmitProcessors.at(i-m_receiveProcessors.size()); } return NULL; } bool Device::startStreamByIndex(int i) { if (i<(int)m_receiveProcessors.size()) { int n=i; Streaming::StreamProcessor *p=m_receiveProcessors.at(n); // allocate ISO channel int isochannel=allocateIsoChannel(p->getMaxPacketSize()); if(isochannel<0) { debugError("Could not allocate iso channel for SP %d\n",i); return false; } p->setChannel(isochannel); fb_quadlet_t reg_isoch; // check value of ISO_CHANNEL register if(!readReg(BOUNCE_REGISTER_TX_ISOCHANNEL, ®_isoch)) { debugError("Could not read ISO_CHANNEL register\n"); p->setChannel(-1); deallocateIsoChannel(isochannel); return false; } if(reg_isoch != 0xFFFFFFFFUL) { debugError("ISO_CHANNEL register != 0xFFFFFFFF (=0x%08X)\n", reg_isoch); p->setChannel(-1); deallocateIsoChannel(isochannel); return false; } // write value of ISO_CHANNEL register reg_isoch=isochannel; if(!writeReg(BOUNCE_REGISTER_TX_ISOCHANNEL, reg_isoch)) { debugError("Could not write ISO_CHANNEL register\n"); p->setChannel(-1); deallocateIsoChannel(isochannel); return false; } return true; } else if (i<(int)m_receiveProcessors.size() + (int)m_transmitProcessors.size()) { int n=i-m_receiveProcessors.size(); Streaming::StreamProcessor *p=m_transmitProcessors.at(n); // allocate ISO channel int isochannel=allocateIsoChannel(p->getMaxPacketSize()); if(isochannel<0) { debugError("Could not allocate iso channel for SP %d\n",i); return false; } p->setChannel(isochannel); fb_quadlet_t reg_isoch; // check value of ISO_CHANNEL register if(!readReg(BOUNCE_REGISTER_RX_ISOCHANNEL, ®_isoch)) { debugError("Could not read ISO_CHANNEL register\n"); p->setChannel(-1); deallocateIsoChannel(isochannel); return false; } if(reg_isoch != 0xFFFFFFFFUL) { debugError("ISO_CHANNEL register != 0xFFFFFFFF (=0x%08X)\n", reg_isoch); p->setChannel(-1); deallocateIsoChannel(isochannel); return false; } // write value of ISO_CHANNEL register reg_isoch=isochannel; if(!writeReg(BOUNCE_REGISTER_RX_ISOCHANNEL, reg_isoch)) { debugError("Could not write ISO_CHANNEL register\n"); p->setChannel(-1); deallocateIsoChannel(isochannel); return false; } return true; } debugError("SP index %d out of range!\n",i); return false; } bool Device::stopStreamByIndex(int i) { if (i<(int)m_receiveProcessors.size()) { int n=i; Streaming::StreamProcessor *p=m_receiveProcessors.at(n); unsigned int isochannel=p->getChannel(); fb_quadlet_t reg_isoch; // check value of ISO_CHANNEL register if(!readReg(BOUNCE_REGISTER_TX_ISOCHANNEL, ®_isoch)) { debugError("Could not read ISO_CHANNEL register\n"); return false; } if(reg_isoch != isochannel) { debugError("ISO_CHANNEL register != 0x%08X (=0x%08X)\n", isochannel, reg_isoch); return false; } // write value of ISO_CHANNEL register reg_isoch=0xFFFFFFFFUL; if(!writeReg(BOUNCE_REGISTER_TX_ISOCHANNEL, reg_isoch)) { debugError("Could not write ISO_CHANNEL register\n" ); return false; } // deallocate ISO channel if(!deallocateIsoChannel(isochannel)) { debugError("Could not deallocate iso channel for SP\n"); return false; } p->setChannel(-1); return true; } else if (i<(int)m_receiveProcessors.size() + (int)m_transmitProcessors.size()) { int n=i-m_receiveProcessors.size(); Streaming::StreamProcessor *p=m_transmitProcessors.at(n); unsigned int isochannel=p->getChannel(); fb_quadlet_t reg_isoch; // check value of ISO_CHANNEL register if(!readReg(BOUNCE_REGISTER_RX_ISOCHANNEL, ®_isoch)) { debugError("Could not read ISO_CHANNEL register\n"); return false; } if(reg_isoch != isochannel) { debugError("ISO_CHANNEL register != 0x%08X (=0x%08X)\n", isochannel, reg_isoch); return false; } // write value of ISO_CHANNEL register reg_isoch=0xFFFFFFFFUL; if(!writeReg(BOUNCE_REGISTER_RX_ISOCHANNEL, reg_isoch)) { debugError("Could not write ISO_CHANNEL register\n"); return false; } // deallocate ISO channel if(!deallocateIsoChannel(isochannel)) { debugError("Could not deallocate iso channel for SP (%d)\n",i); return false; } p->setChannel(-1); return true; } debugError("SP index %d out of range!\n",i); return false; } // helper functions // allocate ISO resources for the SP's int Device::allocateIsoChannel(unsigned int packet_size) { unsigned int bandwidth=8+packet_size; int ch=get1394Service().allocateIsoChannelGeneric(bandwidth); debugOutput(DEBUG_LEVEL_VERBOSE, "allocated channel %d, bandwidth %d\n", ch, bandwidth); return ch; } // deallocate ISO resources bool Device::deallocateIsoChannel(int channel) { debugOutput(DEBUG_LEVEL_VERBOSE, "freeing channel %d\n",channel); return get1394Service().freeIsoChannel(channel); } // I/O functions bool Device::readReg(fb_nodeaddr_t offset, fb_quadlet_t *result) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Reading base register offset 0x%08llX\n", offset); if(offset >= BOUNCE_INVALID_OFFSET) { debugError("invalid offset: 0x%016llX\n", offset); return false; } fb_nodeaddr_t addr=BOUNCE_REGISTER_BASE + offset; fb_nodeid_t nodeId=getNodeId() | 0xFFC0; if(!get1394Service().read_quadlet( nodeId, addr, result ) ) { debugError("Could not read from node 0x%04X addr 0x%012X\n", nodeId, (unsigned int)addr); return false; } debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Read result: 0x%08X\n", *result); return true; } bool Device::writeReg(fb_nodeaddr_t offset, fb_quadlet_t data) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Writing base register offset 0x%08llX, data: 0x%08X\n", offset, data); if(offset >= BOUNCE_INVALID_OFFSET) { debugError("invalid offset: 0x%016llX\n", offset); return false; } fb_nodeaddr_t addr=BOUNCE_REGISTER_BASE + offset; fb_nodeid_t nodeId=getNodeId() | 0xFFC0; if(!get1394Service().write_quadlet( nodeId, addr, data ) ) { debugError("Could not write to node 0x%04X addr 0x%012X\n", nodeId, (unsigned int)addr); return false; } return true; } bool Device::readRegBlock(fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Reading base register offset 0x%08llX, length %u\n", offset, length); if(offset >= BOUNCE_INVALID_OFFSET) { debugError("invalid offset: 0x%016llX\n", offset); return false; } fb_nodeaddr_t addr=BOUNCE_REGISTER_BASE + offset; fb_nodeid_t nodeId=getNodeId() | 0xFFC0; if(!get1394Service().read( nodeId, addr, length, data ) ) { debugError("Could not read from node 0x%04X addr 0x%012llX\n", nodeId, addr); return false; } return true; } bool Device::writeRegBlock(fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Writing base register offset 0x%08llX, length: %u\n", offset, length); if(offset >= BOUNCE_INVALID_OFFSET) { debugError("invalid offset: 0x%016llX\n", offset); return false; } fb_nodeaddr_t addr=BOUNCE_REGISTER_BASE + offset; fb_nodeid_t nodeId=getNodeId() | 0xFFC0; if(!get1394Service().write( nodeId, addr, length, data ) ) { debugError("Could not write to node 0x%04X addr 0x%012llX\n", nodeId, addr); return false; } return true; } } // namespace libffado-2.4.5/src/bounce/bounce_avdevice.h0000644000175000001440000000777614206145246020241 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BOUNCEDEVICE_H #define BOUNCEDEVICE_H #include "debugmodule/debugmodule.h" #include "libavc/avc_definitions.h" #include "libavc/general/avc_extended_cmd_generic.h" #include "libstreaming/amdtp/AmdtpReceiveStreamProcessor.h" #include "libstreaming/amdtp/AmdtpTransmitStreamProcessor.h" #include "libstreaming/amdtp/AmdtpPort.h" #include "libstreaming/amdtp/AmdtpPortInfo.h" #include "libieee1394/ieee1394service.h" #include "ffadodevice.h" #include #define BOUNCE_REGISTER_BASE 0x0000FFFFE0000000ULL #define BOUNCE_REGISTER_LENGTH (4*256) #define BOUNCE_REGISTER_TX_ISOCHANNEL 0x10 #define BOUNCE_REGISTER_RX_ISOCHANNEL 0x14 #define BOUNCE_INVALID_OFFSET 0xFFFFF00000000000ULL #define BOUNCE_NB_AUDIO_CHANNELS 4 // don't define more than 8 midi channels! #define BOUNCE_NB_MIDI_CHANNELS 2 class ConfigRom; class DeviceManager; namespace Util { class Configuration; } namespace Bounce { class Device : public FFADODevice { private: class BounceNotifier; public: Device( DeviceManager& d, ffado_smartptr( configRom )); virtual ~Device(); static bool probe( Util::Configuration&, ConfigRom& configRom, bool generic = false ); static FFADODevice * createDevice( DeviceManager& d, ffado_smartptr( configRom )); virtual bool discover(); virtual bool setSamplingFrequency( int samplingFrequency ); virtual int getSamplingFrequency( ); virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); virtual std::vector getSupportedSamplingFrequencies(); virtual bool prepare(); virtual bool lock(); virtual bool unlock(); virtual int getStreamCount(); virtual Streaming::StreamProcessor *getStreamProcessorByIndex(int i); virtual bool startStreamByIndex(int i); virtual bool stopStreamByIndex(int i); virtual void showDevice(); protected: unsigned int m_samplerate; // streaming stuff typedef std::vector< Streaming::StreamProcessor * > StreamProcessorVector; StreamProcessorVector m_receiveProcessors; StreamProcessorVector m_transmitProcessors; bool addPortsToProcessor( Streaming::StreamProcessor *processor, Streaming::Port::E_Direction direction); private: // generic helpers int allocateIsoChannel(unsigned int packet_size); bool deallocateIsoChannel(int channel); protected: // I/O helpers // quadlet read/write routines bool readReg(fb_nodeaddr_t, fb_quadlet_t *); bool writeReg(fb_nodeaddr_t, fb_quadlet_t); bool readRegBlock(fb_nodeaddr_t, fb_quadlet_t *, size_t); bool writeRegBlock(fb_nodeaddr_t, fb_quadlet_t *, size_t); private: BounceNotifier *m_Notifier; /** * this class reacts on the other side writing to the * hosts address space */ #define BOUNCE_NOTIFIER_BASE_ADDRESS 0x0000FFFFE0000000ULL #define BOUNCE_NOTIFIER_BLOCK_LENGTH 4 class BounceNotifier : public Ieee1394Service::ARMHandler { public: BounceNotifier(Device &, nodeaddr_t start); virtual ~BounceNotifier(); private: Device &m_bouncedevice; }; }; } #endif libffado-2.4.5/src/bounce/bounce_slave_avdevice.cpp0000644000175000001440000003370014206145246021750 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "libstreaming/amdtp/AmdtpReceiveStreamProcessor.h" #include "libstreaming/amdtp/AmdtpTransmitStreamProcessor.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/Time.h" #include "bounce_slave_avdevice.h" #include #include namespace Bounce { SlaveDevice::SlaveDevice( DeviceManager& d, ffado_smartptr< ConfigRom >( configRom ) ) : Device( d, configRom ) { addOption(Util::OptionContainer::Option("isoTimeoutSecs",(int64_t)120)); } SlaveDevice::~SlaveDevice() { } bool SlaveDevice::probe( Util::Configuration& c, ConfigRom& configRom, bool generic ) { // we are always capable of constructing a slave device return true; } FFADODevice * SlaveDevice::createDevice(DeviceManager& d, ffado_smartptr( configRom )) { return new SlaveDevice(d, configRom ); } bool SlaveDevice::discover() { return true; } bool SlaveDevice::initMemSpace() { debugOutput(DEBUG_LEVEL_VERBOSE, "Initializing memory space...\n"); fb_quadlet_t result=0xFFFFFFFFLU; // initialize the ISO channel registers // this will write to our own registers if (!writeReg(BOUNCE_REGISTER_TX_ISOCHANNEL, result)) { debugError("Could not initalize ISO channel register for TX\n"); return false; } if (!writeReg(BOUNCE_REGISTER_RX_ISOCHANNEL, result)) { debugError("Could not initalize ISO channel register for TX\n"); return false; } // set everything such that we can be discovered m_original_config_rom = save_config_rom( get1394Service().getHandle() ); if ( init_config_rom( get1394Service().getHandle() ) < 0 ) { debugError("Could not initalize local config rom\n"); return false; } // refresh our config rom cache if ( !getConfigRom().initialize() ) { // \todo If a PHY on the bus is in power safe mode then // the config rom is missing. So this might be just // such this case and we can safely skip it. But it might // be there is a real software problem on our side. // This should be handled more carefuly. debugError( "Could not reread config rom from device (node id %d).\n", getNodeId() ); return false; } return true; } bool SlaveDevice::restoreMemSpace() { debugOutput(DEBUG_LEVEL_VERBOSE, "Restoring memory space...\n"); restore_config_rom( get1394Service().getHandle(), m_original_config_rom); return true; } bool SlaveDevice::lock() { debugOutput(DEBUG_LEVEL_VERBOSE, "Locking node %d\n", getNodeId()); // get a notifier to handle device notifications nodeaddr_t notify_address; notify_address = get1394Service().findFreeARMBlock( BOUNCE_REGISTER_BASE, BOUNCE_REGISTER_LENGTH, BOUNCE_REGISTER_LENGTH); if (notify_address == 0xFFFFFFFFFFFFFFFFLLU) { debugError("Could not find free ARM block for notification\n"); return false; } m_Notifier=new SlaveDevice::BounceSlaveNotifier(*this, notify_address); if(!m_Notifier) { debugError("Could not allocate notifier\n"); return false; } if (!get1394Service().registerARMHandler(m_Notifier)) { debugError("Could not register notifier\n"); delete m_Notifier; m_Notifier=NULL; return false; } // (re)initialize the memory space if (!initMemSpace()) { debugError("Could not initialize memory space\n"); return false; } return true; } bool SlaveDevice::unlock() { // (re)initialize the memory space if (!restoreMemSpace()) { debugError("Could not restore memory space\n"); return false; } get1394Service().unregisterARMHandler(m_Notifier); delete m_Notifier; m_Notifier=NULL; return true; } bool SlaveDevice::prepare() { debugOutput(DEBUG_LEVEL_NORMAL, "Preparing SlaveDevice...\n" ); // create & add streamprocessors Streaming::StreamProcessor *p; p = new Streaming::AmdtpReceiveStreamProcessor(*this, BOUNCE_NB_AUDIO_CHANNELS+(BOUNCE_NB_MIDI_CHANNELS?1:0)); if(!p->init()) { debugFatal("Could not initialize receive processor!\n"); delete p; return false; } if (!addPortsToProcessor(p, Streaming::Port::E_Capture)) { debugFatal("Could not add plug to processor!\n"); delete p; return false; } m_receiveProcessors.push_back(p); // do the transmit processor p = new Streaming::AmdtpTransmitStreamProcessor(*this, BOUNCE_NB_AUDIO_CHANNELS+(BOUNCE_NB_MIDI_CHANNELS?1:0)); if(!p->init()) { debugFatal("Could not initialize transmit processor!\n"); delete p; return false; } if (!addPortsToProcessor(p, Streaming::Port::E_Playback)) { debugFatal("Could not add plug to processor!\n"); delete p; return false; } m_transmitProcessors.push_back(p); return true; } // this has to wait until the ISO channel numbers are written bool SlaveDevice::startStreamByIndex(int i) { if (i<(int)m_receiveProcessors.size()) { int n=i; Streaming::StreamProcessor *p=m_receiveProcessors.at(n); // the other side sends on this channel nodeaddr_t iso_channel_offset = BOUNCE_REGISTER_RX_ISOCHANNEL; iso_channel_offset += ((unsigned)n)*4; if (!waitForRegisterNotEqualTo(iso_channel_offset, 0xFFFFFFFFLU)) { debugError("Timeout waiting for stream %d to get an ISO channel\n",i); return false; } fb_quadlet_t result; // this will read from our own registers if (!readReg(iso_channel_offset, &result)) { debugError("Could not read ISO channel register for stream %d\n",i); return false; } // set ISO channel p->setChannel(result); return true; } else if (i<(int)m_receiveProcessors.size() + (int)m_transmitProcessors.size()) { int n=i-m_receiveProcessors.size(); Streaming::StreamProcessor *p = m_transmitProcessors.at(n); // the other side sends on this channel nodeaddr_t iso_channel_offset = BOUNCE_REGISTER_TX_ISOCHANNEL; iso_channel_offset += ((unsigned)n)*4; if (!waitForRegisterNotEqualTo(iso_channel_offset, 0xFFFFFFFF)) { debugError("Timeout waiting for stream %d to get an ISO channel\n",i); return false; } fb_quadlet_t result; // this will read from our own registers if (!readReg(iso_channel_offset, &result)) { debugError("Could not read ISO channel register for stream %d\n",i); return false; } // set ISO channel p->setChannel(result); return true; } debugError("SP index %d out of range!\n",i); return false; } bool SlaveDevice::stopStreamByIndex(int i) { // nothing special to do I guess... return false; } // helpers bool SlaveDevice::waitForRegisterNotEqualTo(nodeaddr_t offset, fb_quadlet_t v) { debugOutput( DEBUG_LEVEL_VERBOSE, "Waiting for StreamProcessor streams to start running...\n"); // we have to wait until all streamprocessors indicate that they are running // i.e. that there is actually some data stream flowing int timeoutSecs=120; if(!getOption("isoTimeoutSecs", timeoutSecs)) { debugWarning("Could not retrieve isoTimeoutSecs parameter, defauling to 120secs\n"); } int wait_cycles=timeoutSecs*10; // two seconds fb_quadlet_t reg=v; while ((v == reg) && wait_cycles) { wait_cycles--; if (!readReg(offset,®)) { debugError("Could not read register\n"); return false; } SleepRelativeUsec(100000); } if(!wait_cycles) { // timout has occurred return false; } return true; } // configrom helpers // FIXME: should be changed into a better framework struct SlaveDevice::configrom_backup SlaveDevice::save_config_rom(raw1394handle_t handle) { int retval; struct configrom_backup tmp; /* get the current rom image */ retval=raw1394_get_config_rom(handle, tmp.rom, 0x100, &tmp.rom_size, &tmp.rom_version); // tmp.rom_size=rom1394_get_size(tmp.rom); // printf("save_config_rom get_config_rom returned %d, romsize %d, rom_version %d:\n",retval,tmp.rom_size,tmp.rom_version); return tmp; } int SlaveDevice::restore_config_rom(raw1394handle_t handle, struct SlaveDevice::configrom_backup old) { int retval; // int i; quadlet_t current_rom[0x100]; size_t current_rom_size; unsigned char current_rom_version; retval=raw1394_get_config_rom(handle, current_rom, 0x100, ¤t_rom_size, ¤t_rom_version); // printf("restore_config_rom get_config_rom returned %d, romsize %d, rom_version %d:\n",retval,current_rom_size,current_rom_version); // printf("restore_config_rom restoring to romsize %d, rom_version %d:\n",old.rom_size,old.rom_version); retval = raw1394_update_config_rom(handle, old.rom, old.rom_size, current_rom_version); // printf("restore_config_rom update_config_rom returned %d\n",retval); /* get the current rom image */ retval=raw1394_get_config_rom(handle, current_rom, 0x100, ¤t_rom_size, ¤t_rom_version); current_rom_size = rom1394_get_size(current_rom); // printf("get_config_rom returned %d, romsize %d, rom_version %d:",retval,current_rom_size,current_rom_version); // for (i = 0; i < current_rom_size; i++) // { // if (i % 4 == 0) printf("\n0x%04x:", CSR_CONFIG_ROM+i*4); // printf(" %08x", CondSwapFromBus32(current_rom[i])); // } // printf("\n"); return retval; } int SlaveDevice::init_config_rom(raw1394handle_t handle) { int retval, i; quadlet_t rom[0x100]; size_t rom_size; unsigned char rom_version; rom1394_directory dir; char *leaf; /* get the current rom image */ retval=raw1394_get_config_rom(handle, rom, 0x100, &rom_size, &rom_version); rom_size = rom1394_get_size(rom); // printf("get_config_rom returned %d, romsize %d, rom_version %d:",retval,rom_size,rom_version); // for (i = 0; i < rom_size; i++) // { // if (i % 4 == 0) printf("\n0x%04x:", CSR_CONFIG_ROM+i*4); // printf(" %08x", CondSwapFromBus32(rom[i])); // } // printf("\n"); /* get the local directory */ rom1394_get_directory( handle, raw1394_get_local_id(handle) & 0x3f, &dir); /* change the vendor description for kicks */ i = strlen(dir.textual_leafs[0]); strncpy(dir.textual_leafs[0], FFADO_BOUNCE_SERVER_VENDORNAME " ", i); dir.vendor_id=FFADO_BOUNCE_SERVER_VENDORID; dir.model_id=FFADO_BOUNCE_SERVER_MODELID; /* update the rom */ retval = rom1394_set_directory(rom, &dir); // printf("rom1394_set_directory returned %d, romsize %d:",retval,rom_size); // for (i = 0; i < rom_size; i++) // { // if (i % 4 == 0) printf("\n0x%04x:", CSR_CONFIG_ROM+i*4); // printf(" %08x", CondSwapFromBus32(rom[i])); // } // printf("\n"); /* free the allocated mem for the textual leaves */ rom1394_free_directory( &dir); /* add an AV/C unit directory */ dir.unit_spec_id = FFADO_BOUNCE_SERVER_SPECID; dir.unit_sw_version = 0x00010001; leaf = (char*)FFADO_BOUNCE_SERVER_MODELNAME; dir.nr_textual_leafs = 1; dir.textual_leafs = &leaf; /* manipulate the rom */ retval = rom1394_add_unit( rom, &dir); /* get the computed size of the rom image */ rom_size = rom1394_get_size(rom); // printf("rom1394_add_unit_directory returned %d, romsize %d:",retval,rom_size); // for (i = 0; i < rom_size; i++) // { // if (i % 4 == 0) printf("\n0x%04x:", CSR_CONFIG_ROM+i*4); // printf(" %08x", CondSwapFromBus32(rom[i])); // } // printf("\n"); // /* convert computed rom size from quadlets to bytes before update */ rom_size *= sizeof(quadlet_t); retval = raw1394_update_config_rom(handle, rom, rom_size, rom_version); // printf("update_config_rom returned %d\n",retval); retval=raw1394_get_config_rom(handle, rom, 0x100, &rom_size, &rom_version); // printf("get_config_rom returned %d, romsize %d, rom_version %d:",retval,rom_size,rom_version); // for (i = 0; i < rom_size; i++) // { // if (i % 4 == 0) printf("\n0x%04x:", CSR_CONFIG_ROM+i*4); // printf(" %08x", CondSwapFromBus32(rom[i])); // } // printf("\n"); // printf("You need to reload your ieee1394 modules to reset the rom.\n"); return 0; } // the notifier SlaveDevice::BounceSlaveNotifier::BounceSlaveNotifier(SlaveDevice &d, nodeaddr_t start) : ARMHandler(d.get1394Service(), start, BOUNCE_REGISTER_LENGTH, RAW1394_ARM_READ | RAW1394_ARM_WRITE, // allowed operations 0, //RAW1394_ARM_READ | RAW1394_ARM_WRITE, // operations to be notified of 0) // operations that are replied to by us (instead of kernel) , m_bounceslavedevice(d) { } SlaveDevice::BounceSlaveNotifier::~BounceSlaveNotifier() { } } // end of namespace Bounce libffado-2.4.5/src/bounce/bounce_slave_avdevice.h0000644000175000001440000000560514206145246021420 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_BOUNCESLAVEDEVICE__ #define __FFADO_BOUNCESLAVEDEVICE__ #include "debugmodule/debugmodule.h" #include "bounce_avdevice.h" #define FFADO_MAX_NAME_LEN 256 #define FFADO_BOUNCE_SERVER_VENDORNAME "FFADO Server" #define FFADO_BOUNCE_SERVER_MODELNAME "ffado-server" // NOTE: this is currently free, but it is not really allowed to use #define FFADO_BOUNCE_SERVER_VENDORID 0x000B0001 #define FFADO_BOUNCE_SERVER_MODELID 0x000B0001 #define FFADO_BOUNCE_SERVER_SPECID 0x000B0001 namespace Bounce { class SlaveDevice : public Device { class BounceSlaveNotifier; public: SlaveDevice( DeviceManager& d, ffado_smartptr( configRom ) ); virtual ~SlaveDevice(); static bool probe( Util::Configuration&, ConfigRom& configRom, bool generic = false ); static FFADODevice * createDevice( DeviceManager& d, ffado_smartptr( configRom )); bool discover(); bool prepare(); bool lock(); bool unlock(); bool startStreamByIndex(int i); bool stopStreamByIndex(int i); private: bool waitForRegisterNotEqualTo(nodeaddr_t offset, fb_quadlet_t v); bool initMemSpace(); bool restoreMemSpace(); private: // configrom shit struct configrom_backup { quadlet_t rom[0x100]; size_t rom_size; unsigned char rom_version; }; struct configrom_backup m_original_config_rom; struct configrom_backup save_config_rom(raw1394handle_t handle); int restore_config_rom(raw1394handle_t handle, struct configrom_backup old); int init_config_rom(raw1394handle_t handle); private: BounceSlaveNotifier *m_Notifier; /** * this class reacts on the other side writing to the * hosts address space */ class BounceSlaveNotifier : public Ieee1394Service::ARMHandler { public: BounceSlaveNotifier(SlaveDevice &, nodeaddr_t start); virtual ~BounceSlaveNotifier(); private: SlaveDevice &m_bounceslavedevice; }; }; } // end of namespace Bounce #endif /* __FFADO_BOUNCESLAVEDEVICE__ */ libffado-2.4.5/src/debugmodule/0000755000175000001440000000000014206145612015744 5ustar jwoitheuserslibffado-2.4.5/src/debugmodule/debugmodule.cpp0000644000175000001440000006167414206145246020765 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "debugmodule.h" #include #include "libutil/ByteSwap.h" #include "libutil/Time.h" #include #include #include #include #if DEBUG_BACKTRACE_SUPPORT #include #include #define GNU_SOURCE #include #endif #if DEBUG_USE_MESSAGE_BUFFER #else #ifdef DEBUG_MESSAGES #warning Printing debug info without ringbuffer, not RT-safe! #else #error Printing debug info without ringbuffer, not RT-safe (not allowed for non-debug builds)! #endif #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 (m_manager && !m_manager->unregisterModule( *this ) ) { cerr << "Could not unregister DebugModule at DebugModuleManager" << endl; } } void DebugModule::printShort( debug_level_t level, const char* format, ... ) const { // bypass for performance #if DEBUG_BACKLOG_SUPPORT 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); } #if DEBUG_BACKLOG_SUPPORT // 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 #if DEBUG_BACKLOG_SUPPORT 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; Util::SystemTimeSource::clockGettime(&ts); uint64_t ts_usec=(uint64_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, "%011" PRIu64 ": %s (%s)[%4u] %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); } #if DEBUG_BACKLOG_SUPPORT // 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) #if DEBUG_USE_MESSAGE_BUFFER , mb_inbuffer(0) , mb_outbuffer(0) , mb_overruns(0) #endif #if DEBUG_BACKTRACE_SUPPORT , m_backtrace_buffer_nb_seen(0) #endif #if DEBUG_BACKLOG_SUPPORT , bl_mb_inbuffer(0) #endif { } DebugModuleManager::~DebugModuleManager() { // cleaning up leftover modules while (!m_debugModules.empty()) { DebugModule *mod = m_debugModules.back(); unregisterModule(*mod); } if (!mb_initialized) return; #if DEBUG_USE_MESSAGE_BUFFER pthread_mutex_lock(&mb_write_lock); mb_initialized = 0; sem_post(&mb_writes); pthread_mutex_unlock(&mb_write_lock); pthread_join(mb_writer_thread, NULL); mb_flush(); #endif #if DEBUG_BACKTRACE_SUPPORT pthread_mutex_lock(&m_backtrace_lock); // print a list of the symbols seen in a backtrace fprintf(stderr, "Backtrace saw %d symbols:\n", m_backtrace_buffer_nb_seen); char **strings = backtrace_symbols(m_backtrace_buffer_seen, m_backtrace_buffer_nb_seen); if (strings == NULL) { perror("backtrace_symbols"); } else { char* outbuf = NULL; size_t length; int status; Dl_info info; for (int j = 0; j < m_backtrace_buffer_nb_seen; j++) { if (dladdr(m_backtrace_buffer_seen[j], &info) != 0) { outbuf = __cxxabiv1::__cxa_demangle(info.dli_sname, outbuf, &length, &status); if(outbuf && status == 0) { fprintf(stderr, " %p => %s\n", m_backtrace_buffer_seen[j], outbuf); free(outbuf); outbuf = NULL; } else { fprintf(stderr, " %p => %s (demangle status: %d)\n", m_backtrace_buffer_seen[j], strings[j], status); } } else { fprintf(stderr, " %p => %s\n", m_backtrace_buffer_seen[j], strings[j]); } } free(strings); } pthread_mutex_unlock(&m_backtrace_lock); #endif #if DEBUG_USE_MESSAGE_BUFFER 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); sem_destroy(&mb_writes); #endif #if DEBUG_BACKTRACE_SUPPORT pthread_mutex_destroy(&m_backtrace_lock); #endif #if DEBUG_BACKLOG_SUPPORT 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; #if DEBUG_BACKTRACE_SUPPORT pthread_mutex_init(&m_backtrace_lock, NULL); #endif #if DEBUG_BACKLOG_SUPPORT pthread_mutex_init(&bl_mb_write_lock, NULL); #endif #if DEBUG_USE_MESSAGE_BUFFER pthread_mutex_init(&mb_flush_lock, NULL); pthread_mutex_init(&mb_write_lock, NULL); sem_init(&mb_writes, 0, 0); mb_overruns = 0; int res; #if DEBUG_MESSAGE_BUFFER_REALTIME /* Get the client thread to run as an RT-FIFO scheduled thread of appropriate priority. */ pthread_attr_t attributes; struct sched_param rt_param; pthread_attr_init(&attributes); if ((res = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED))) { fprintf(stderr, "Cannot request explicit scheduling for messagebuffer thread: %s (%d)\n", strerror(res), res); return -1; } if ((res = pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_JOINABLE))) { fprintf(stderr, "Cannot request joinable thread creation for messagebuffer thread: %s (%d)\n", strerror(res), res); return -1; } if ((res = pthread_attr_setscope(&attributes, PTHREAD_SCOPE_SYSTEM))) { fprintf(stderr, "Cannot set scheduling scope for messagebuffer thread: %s (%d)\n", strerror(res), res); return -1; } if ((res = pthread_attr_setschedpolicy(&attributes, SCHED_FIFO))) { //if ((res = pthread_attr_setschedpolicy(&attributes, SCHED_RR))) { fprintf(stderr, "Cannot set FIFO scheduling class for messagebuffer thread: %s (%d)\n", strerror(res), res); return -1; } memset(&rt_param, 0, sizeof(rt_param)); rt_param.sched_priority = DEBUG_MESSAGE_BUFFER_REALTIME_PRIO; if ((res = pthread_attr_setschedparam(&attributes, &rt_param))) { fprintf(stderr, "Cannot set scheduling priority for messagebuffer thread: %s (%d)\n", strerror(res), res); return -1; } mb_initialized = 1; // set to 1 otherwise the thread might exit (race condition) if ((res = pthread_create(&mb_writer_thread, &attributes, mb_thread_func, (void *)this))) { fprintf(stderr, "Cannot create RT messagebuffer thread: %s (%d)\n", strerror(res), res); mb_initialized = 0; } if (res==EPERM && mb_initialized==0) { fprintf(stderr, "Retrying messagebuffer thread without RT scheduling\n"); memset(&rt_param, 0, sizeof(rt_param)); if ((res=pthread_attr_setschedpolicy(&attributes, SCHED_OTHER)) || (res=pthread_attr_setschedparam(&attributes, &rt_param))) { fprintf(stderr, "Cannot set standard scheduling for messagebuffer thread: %s (%d)\n", strerror(res), res); } else { mb_initialized = 1; // set to 1 otherwise the thread might exit (race condition) if ((res = pthread_create(&mb_writer_thread, &attributes, mb_thread_func, (void *)this))) { fprintf(stderr, "Cannot create messagebuffer thread: %s (%d)\n", strerror(res), res); mb_initialized = 0; } else fprintf(stderr, "Messagebuffer not realtime; consider enabling RT scheduling for user\n"); } } #else mb_initialized = 1; // set to 1 otherwise the thread might exit (race condition) if ((res = pthread_create(&mb_writer_thread, NULL, &mb_thread_func, (void *)this))) { fprintf(stderr, "Cannot create thread %d %s\n", res, strerror(res)); mb_initialized = 0; } #endif #endif 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 ); if (debugModule.m_manager == NULL) debugModule.m_manager = this; } 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 ); if (debugModule.m_manager == this) debugModule.m_manager = NULL; 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() { #if DEBUG_USE_MESSAGE_BUFFER mb_flush(); #else fflush(stderr); #endif } #if DEBUG_USE_MESSAGE_BUFFER 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); } fflush(stderr); pthread_mutex_unlock(&m->mb_flush_lock); } void * DebugModuleManager::mb_thread_func(void *arg) { DebugModuleManager *m=static_cast(arg); while (m->mb_initialized) { sem_wait(&m->mb_writes); m->mb_flush(); } return NULL; } #endif #if DEBUG_BACKLOG_SUPPORT 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); } void DebugModuleManager::backlog_print(const char *msg) { unsigned int ntries; struct timespec wait = {0, DEBUG_MESSAGE_BUFFER_COLLISION_WAIT_NSEC}; // the backlog ntries=DEBUG_MESSAGE_BUFFER_COLLISION_WAIT_NTRIES; 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) { #if DEBUG_USE_MESSAGE_BUFFER unsigned int ntries; struct timespec wait = {0,50000}; if (!mb_initialized) { /* Unable to print message with realtime safety. * Complain and print it anyway. */ fprintf(stderr, "ERROR: messagebuffer not initialized: %s", msg); return; } ntries=6; 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); sem_post(&mb_writes); 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 } #if DEBUG_BACKTRACE_SUPPORT void DebugModuleManager::printBacktrace(int len) { int nptrs; int chars_written=0; if(len > DEBUG_MAX_BACKTRACE_LENGTH) { len = DEBUG_MAX_BACKTRACE_LENGTH; } pthread_mutex_lock(&m_backtrace_lock); nptrs = backtrace(m_backtrace_buffer, len); chars_written += snprintf(m_backtrace_strbuffer, MB_BUFFERSIZE-chars_written, "BACKTRACE (%d/%d): ", nptrs, len); for (int j = 0; j < nptrs; j++) { char name[64]; name[0]=0; getFunctionName(m_backtrace_buffer[j], name, 64); chars_written += snprintf(m_backtrace_strbuffer + chars_written, MB_BUFFERSIZE-chars_written, "%s\n", name); } chars_written += snprintf(m_backtrace_strbuffer + chars_written, MB_BUFFERSIZE-chars_written, "\n"); // make sure the string is terminated properly m_backtrace_strbuffer[MB_BUFFERSIZE-2] = '\n'; m_backtrace_strbuffer[MB_BUFFERSIZE-1] = 0; // save the pointers to the pointers-seen list such that we can // dump their info later on bool seen; for (int i=0; i= DEBUG_MAX_BACKTRACE_LENGTH) { return NULL; } pthread_mutex_lock(&m_backtrace_lock); nptrs = backtrace(m_backtrace_buffer, id+1); if(id >= nptrs) { id = nptrs-1; } retval = m_backtrace_buffer[id]; // save the pointers to the pointers-seen list such that we can // dump their info later on bool seen = false; int j; for (j=0; j 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) { fprintf(stderr, "%02d %04X: %08X (%08X)" "\n", i, i*4, data[i],CondSwapFromBus32(data[i])); } } libffado-2.4.5/src/debugmodule/debugmodule.h0000644000175000001440000003630314206145246020421 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DEBUGMODULE_H #define DEBUGMODULE_H #include "config_debug.h" #include "../fbtypes.h" #include #include #include #include #include #include #define FFADO_ASSERT(x) { \ if(!(x)) { \ m_debugModule.print( DebugModule::eDL_Fatal, \ __FILE__, \ __FUNCTION__, \ __LINE__, \ "Assertion failed...\n"); \ debugPrintBacktrace( 10 ); \ DebugModuleManager::instance()->flush(); \ assert(x); \ }} typedef short debug_level_t; #define DEBUG_LEVEL_MESSAGE 0 #define DEBUG_LEVEL_FATAL 1 #define DEBUG_LEVEL_ERROR 2 #define DEBUG_LEVEL_WARNING 3 #define DEBUG_LEVEL_NORMAL 4 #define DEBUG_LEVEL_INFO 5 #define DEBUG_LEVEL_VERBOSE 6 #define DEBUG_LEVEL_VERY_VERBOSE 7 #define DEBUG_LEVEL_ULTRA_VERBOSE 8 /* MB_NEXT() relies on the fact that MB_BUFFERS is a power of two */ #define MB_NEXT(index) (((index)+1) & (DEBUG_MB_BUFFERS-1)) #define MB_BUFFERSIZE DEBUG_MAX_MESSAGE_LENGTH // no backtrace support when not debugging #ifndef DEBUG #undef DEBUG_BACKTRACE_SUPPORT #define DEBUG_BACKTRACE_SUPPORT 0 #endif // no backlog support when not debugging #ifndef DEBUG #undef DEBUG_BACKLOG_SUPPORT #define DEBUG_BACKLOG_SUPPORT 0 #endif // the backlog is a similar buffer as the message buffer #define DEBUG_BACKLOG_MB_NEXT(index) (((index)+1) & (DEBUG_BACKLOG_MB_BUFFERS-1)) #define DEBUG_BACKLOG_MIN_LEVEL DEBUG_LEVEL_VERY_VERBOSE #define debugFatal( format, args... ) \ m_debugModule.print( DebugModule::eDL_Fatal, \ __FILE__, \ __FUNCTION__, \ __LINE__, \ format, \ ##args ) #define debugError( format, args... ) \ m_debugModule.print( DebugModule::eDL_Error, \ __FILE__, \ __FUNCTION__, \ __LINE__, \ format, \ ##args ) #define debugWarning( format, args... ) \ m_debugModule.print( DebugModule::eDL_Warning, \ __FILE__, \ __FUNCTION__, \ __LINE__, \ format, \ ##args ) #define debugFatalShort( format, args... ) \ m_debugModule.printShort( DebugModule::eDL_Fatal, \ format, \ ##args ) #define debugErrorShort( format, args... ) \ m_debugModule.printShort( DebugModule::eDL_Error, \ format, \ ##args ) #define debugWarningShort( format, args... ) \ m_debugModule.printShort( DebugModule::eDL_Warning, \ format, \ ##args ) // these are for messages that are also displayed when not compiled // for debug messages #ifdef DEBUG_MESSAGES #define printMessage( format, args... ) \ m_debugModule.print( DebugModule::eDL_Message, \ __FILE__, \ __FUNCTION__, \ __LINE__, \ format, \ ##args ) #else #define printMessage( format, args... ) \ m_debugModule.printShort( DebugModule::eDL_Message, \ format, \ ##args ) #endif #define printMessageShort( format, args... ) \ m_debugModule.printShort( DebugModule::eDL_Message, \ format, \ ##args ) #define DECLARE_DEBUG_MODULE static DebugModule m_debugModule #define DECLARE_DEBUG_MODULE_REFERENCE DebugModule &m_debugModule #define IMPL_DEBUG_MODULE( ClassName, RegisterName, Level ) \ DebugModule ClassName::m_debugModule = \ DebugModule( #RegisterName, Level ) #define DECLARE_GLOBAL_DEBUG_MODULE extern DebugModule m_debugModule #define IMPL_GLOBAL_DEBUG_MODULE( RegisterName, Level ) \ DebugModule m_debugModule = \ DebugModule( #RegisterName, Level ) #define setDebugLevel( Level ) { \ m_debugModule.setLevel( Level ); \ } /* m_debugModule.print( eDL_Normal, \ __FILE__, \ __FUNCTION__, \ __LINE__, \ "Setting debug level to %d\n", \ Level ); \ }*/ #define getDebugLevel( ) \ m_debugModule.getLevel( ) #define flushDebugOutput() DebugModuleManager::instance()->flush() #if DEBUG_BACKLOG_SUPPORT #define debugShowBackLog() \ { \ m_debugModule.print( DebugModule::eDL_Warning, \ __FILE__, \ __FUNCTION__, \ __LINE__, \ "Backlog print requested\n"); \ DebugModuleManager::instance()->showBackLog(); \ } #define debugShowBackLogLines(x) \ { \ m_debugModule.print( DebugModule::eDL_Warning, \ __FILE__, \ __FUNCTION__, \ __LINE__, \ "Backlog print requested\n"); \ DebugModuleManager::instance()->showBackLog(x); \ } #else #define debugShowBackLog() #define debugShowBackLogLines(x) #endif #ifdef DEBUG_MESSAGES #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 ) #define DEBUG_NORMAL( x ) x; #if DEBUG_EXTREME_ENABLE #define debugOutputExtreme( level, format, args... ) \ m_debugModule.print( level, \ __FILE__, \ __FUNCTION__, \ __LINE__, \ format, \ ##args ) #define debugOutputShortExtreme( level, format, args... ) \ m_debugModule.printShort( level, \ format, \ ##args ) #define DEBUG_EXTREME( x ) x; #else #define debugOutputExtreme( level, format, args... ) #define debugOutputShortExtreme( level, format, args... ) #define DEBUG_EXTREME( x ) #endif #else #define debugOutput( level, format, args... ) #define debugOutputShort( level, format, args... ) #define DEBUG_NORMAL( x ) #define debugOutputExtreme( level, format, args... ) #define debugOutputShortExtreme( level, format, args... ) #define DEBUG_EXTREME( x ) #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 /* * Backtrace support */ #if DEBUG_BACKTRACE_SUPPORT #define debugPrintBacktrace( _SIZE_ ) \ DebugModuleManager::instance()->printBacktrace( _SIZE_ ); #define debugBacktraceGet( _ID_ ) \ DebugModuleManager::instance()->getBacktracePtr( _ID_ ); #define debugGetFunctionNameFromAddr( _ADDR_, _BUFF_, _MAX_SIZE_ ) \ DebugModuleManager::instance()->getFunctionName( _ADDR_, _BUFF_, _MAX_SIZE_ ); #else #define debugPrintBacktrace( _SIZE_ ) #define debugBacktraceGet( _ID_ ) NULL #define debugGetFunctionNameFromAddr( _ADDR_, _BUFF_, _MAX_SIZE_ ) #endif /* * helper functions */ 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 DebugModuleManager; class DebugModule { public: friend class DebugModuleManager; 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 #ifdef __GNUC__ __attribute__((format(printf, 3, 4))) #endif ; void print( debug_level_t level, const char* file, const char* function, unsigned int line, const char* format, ... ) const #ifdef __GNUC__ __attribute__((format(printf, 6, 7))) #endif ; 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; DebugModuleManager* m_manager; }; class DebugModuleManager { public: friend class DebugModule; static DebugModuleManager* instance(); ~DebugModuleManager(); bool setMgrDebugLevel( std::string name, debug_level_t level ); void flush(); #if DEBUG_BACKLOG_SUPPORT // 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); #endif #if DEBUG_BACKTRACE_SUPPORT void printBacktrace(int len); void *getBacktracePtr(int id); void getFunctionName( void *, char *, int ); #endif protected: bool registerModule( DebugModule& debugModule ); bool unregisterModule( DebugModule& debugModule ); bool init(); void print(const char *msg); #if DEBUG_BACKLOG_SUPPORT void backlog_print(const char *msg); #endif private: DebugModuleManager(); typedef std::vector< DebugModule* > DebugModuleVector; typedef std::vector< DebugModule* >::iterator DebugModuleVectorIterator; unsigned int mb_initialized; #if DEBUG_USE_MESSAGE_BUFFER char mb_buffers[DEBUG_MB_BUFFERS][MB_BUFFERSIZE]; 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; sem_t mb_writes; #endif #if DEBUG_BACKTRACE_SUPPORT pthread_mutex_t m_backtrace_lock; char m_backtrace_strbuffer[MB_BUFFERSIZE]; void *m_backtrace_buffer[DEBUG_MAX_BACKTRACE_LENGTH]; void *m_backtrace_buffer_seen[DEBUG_MAX_BACKTRACE_FUNCTIONS_SEEN]; int m_backtrace_buffer_nb_seen; #endif static void *mb_thread_func(void *arg); void mb_flush(); #if DEBUG_BACKLOG_SUPPORT // the backlog char bl_mb_buffers[DEBUG_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 libffado-2.4.5/src/debugmodule/test_debugmodule.cpp0000644000175000001440000000570314206145246022013 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "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: */ libffado-2.4.5/src/devicemanager.cpp0000644000175000001440000012540414206145246016757 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "fbtypes.h" #include "devicemanager.h" #include "ffadodevice.h" #include "DeviceStringParser.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/IsoHandlerManager.h" #include "libstreaming/generic/StreamProcessor.h" #include "libstreaming/StreamProcessorManager.h" #include "debugmodule/debugmodule.h" #include "libutil/PosixMutex.h" #ifdef ENABLE_BEBOB #include "bebob/bebob_avdevice.h" #endif #ifdef ENABLE_GENERICAVC #include "genericavc/avc_avdevice.h" #endif #ifdef ENABLE_FIREWORKS #include "fireworks/fireworks_device.h" #endif #ifdef ENABLE_OXFORD #include "oxford/oxford_device.h" #endif #ifdef ENABLE_BOUNCE #include "bounce/bounce_avdevice.h" #include "bounce/bounce_slave_avdevice.h" #endif #ifdef ENABLE_MOTU #include "motu/motu_avdevice.h" #endif #ifdef ENABLE_RME #include "rme/rme_avdevice.h" #endif #ifdef ENABLE_DICE #include "dice/dice_avdevice.h" #endif #ifdef ENABLE_METRIC_HALO #include "metrichalo/mh_avdevice.h" #endif #include #include #include using namespace std; IMPL_DEBUG_MODULE( DeviceManager, DeviceManager, DEBUG_LEVEL_NORMAL ); DeviceManager::DeviceManager() : Control::Container(NULL, "devicemanager") // this is the control root node , m_DeviceListLock( new Util::PosixMutex("DEVLST") ) , m_BusResetLock( new Util::PosixMutex("DEVBR") ) , m_processorManager( new Streaming::StreamProcessorManager( *this ) ) , m_deviceStringParser( new DeviceStringParser() ) , m_configuration ( new Util::Configuration() ) , m_used_cache_last_time( false ) , m_thread_realtime( false ) , m_thread_priority( 0 ) { addOption(Util::OptionContainer::Option("slaveMode", false)); addOption(Util::OptionContainer::Option("snoopMode", false)); } DeviceManager::~DeviceManager() { // save configuration if(!m_configuration->save()) { debugWarning("could not save configuration\n"); } m_BusResetLock->Lock(); // make sure we are not handling a busreset. m_DeviceListLock->Lock(); // make sure nobody is using this for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { if (!deleteElement(*it)) { debugWarning("failed to remove Device from Control::Container\n"); } delete *it; } m_DeviceListLock->Unlock(); // the SP's are automatically unregistered from the SPM delete m_processorManager; // the device list is empty, so wake up any waiting // reset handlers m_BusResetLock->Unlock(); // remove the bus-reset handlers for ( FunctorVectorIterator it = m_busreset_functors.begin(); it != m_busreset_functors.end(); ++it ) { delete *it; } for ( Ieee1394ServiceVectorIterator it = m_1394Services.begin(); it != m_1394Services.end(); ++it ) { delete *it; } delete m_DeviceListLock; delete m_BusResetLock; delete m_deviceStringParser; } bool DeviceManager::setThreadParameters(bool rt, int priority) { if (!m_processorManager->setThreadParameters(rt, priority)) { debugError("Could not set processor manager thread parameters\n"); return false; } for ( Ieee1394ServiceVectorIterator it = m_1394Services.begin(); it != m_1394Services.end(); ++it ) { if (!(*it)->setThreadParameters(rt, priority)) { debugError("Could not set 1394 service thread parameters\n"); return false; } } m_thread_realtime = rt; m_thread_priority = priority; return true; } bool DeviceManager::initialize() { assert(m_1394Services.size() == 0); assert(m_busreset_functors.size() == 0); m_configuration->openFile( "temporary", Util::Configuration::eFM_Temporary ); m_configuration->openFile( USER_CONFIG_FILE, Util::Configuration::eFM_ReadWrite ); m_configuration->openFile( SYSTEM_CONFIG_FILE, Util::Configuration::eFM_ReadOnly ); int nb_detected_ports = Ieee1394Service::detectNbPorts(); if (nb_detected_ports < 0) { debugFatal("Failed to detect the number of 1394 adapters. Is the IEEE1394 stack loaded (raw1394)?\n"); return false; } if (nb_detected_ports == 0) { debugFatal("No FireWire adapters (ports) found.\n"); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "Found %d FireWire adapters (ports)\n", nb_detected_ports); for (unsigned int port = 0; port < (unsigned int)nb_detected_ports; port++) { Ieee1394Service* tmp1394Service = new Ieee1394Service(); if ( !tmp1394Service ) { debugFatal( "Could not create Ieee1349Service object for port %d\n", port ); return false; } tmp1394Service->setVerboseLevel( getDebugLevel() ); m_1394Services.push_back(tmp1394Service); if(!tmp1394Service->useConfiguration(m_configuration)) { debugWarning("Could not load config to 1394service\n"); } tmp1394Service->setThreadParameters(m_thread_realtime, m_thread_priority); if ( !tmp1394Service->initialize( port ) ) { debugFatal( "Could not initialize Ieee1349Service object for port %d\n", port ); return false; } // add the bus reset handler Util::Functor* tmp_busreset_functor = new Util::MemberFunctor1< DeviceManager*, void (DeviceManager::*)(Ieee1394Service &), Ieee1394Service & > ( this, &DeviceManager::busresetHandler, *tmp1394Service, false ); if ( !tmp_busreset_functor ) { debugFatal( "Could not create busreset handler for port %d\n", port ); return false; } m_busreset_functors.push_back(tmp_busreset_functor); tmp1394Service->addBusResetHandler( tmp_busreset_functor ); } return true; } bool DeviceManager::addSpecString(char *s) { std::string spec = s; if(isSpecStringValid(spec)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Adding spec string %s\n", spec.c_str()); assert(m_deviceStringParser); m_deviceStringParser->parseString(spec); return true; } else { debugError("Invalid spec string: %s\n", spec.c_str()); return false; } } bool DeviceManager::isSpecStringValid(std::string s) { assert(m_deviceStringParser); return m_deviceStringParser->isValidString(s); } void DeviceManager::busresetHandler(Ieee1394Service &service) { // serialize bus reset handling since it can be that a new one occurs while we're // doing stuff. debugOutput( DEBUG_LEVEL_NORMAL, "Bus reset detected on service %p...\n", &service ); Util::MutexLockHelper lock(*m_BusResetLock); debugOutput( DEBUG_LEVEL_NORMAL, " handling busreset...\n" ); // FIXME: what if the devices are gone? (device should detect this!) // propagate the bus reset to all avDevices m_DeviceListLock->Lock(); // make sure nobody is using this for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { if(&service == &((*it)->get1394Service())) { debugOutput(DEBUG_LEVEL_NORMAL, "issue busreset on device GUID %s\n", (*it)->getConfigRom().getGuidString().c_str()); (*it)->handleBusReset(); } else { debugOutput(DEBUG_LEVEL_NORMAL, "skipping device GUID %s since not on service %p\n", (*it)->getConfigRom().getGuidString().c_str(), &service); } } m_DeviceListLock->Unlock(); // now that the devices have been updates, we can request to update the iso streams if(!service.getIsoHandlerManager().handleBusReset()) { debugError("IsoHandlerManager failed to handle busreset\n"); } // notify the streamprocessormanager of the busreset // if(m_processorManager) { // m_processorManager->handleBusReset(service); // } else { // debugWarning("No valid SPM\n"); // } // rediscover to find new devices // (only for the control server ATM, streaming can't dynamically add/remove devices) if(!discover(m_used_cache_last_time, true)) { debugError("Could not rediscover devices\n"); } // notify any clients signalNotifiers(m_busResetNotifiers); // display the new state if(getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { showDeviceInfo(); } } void DeviceManager::signalNotifiers(notif_vec_t& list) { for ( notif_vec_t::iterator it = list.begin(); it != list.end(); ++it ) { Util::Functor* func = *it; debugOutput( DEBUG_LEVEL_VERBOSE, " running notifier %p...\n", func ); ( *func )(); } } bool DeviceManager::registerNotification(notif_vec_t& list, Util::Functor *handler) { debugOutput( DEBUG_LEVEL_VERBOSE, "register %p...\n", handler); assert(handler); for ( notif_vec_t::iterator it = list.begin(); it != list.end(); ++it ) { if ( *it == handler ) { debugOutput(DEBUG_LEVEL_VERBOSE, "already registered\n"); return false; } } list.push_back(handler); return true; } bool DeviceManager::unregisterNotification(notif_vec_t& list, Util::Functor *handler) { debugOutput( DEBUG_LEVEL_VERBOSE, "unregister %p...\n", handler); assert(handler); for ( notif_vec_t::iterator it = list.begin(); it != list.end(); ++it ) { if ( *it == handler ) { list.erase(it); return true; } } debugError("Could not find handler (%p)\n", handler); return false; //not found } bool DeviceManager::discover( bool useCache, bool rediscover ) { debugOutput( DEBUG_LEVEL_NORMAL, "Starting discovery...\n" ); useCache = useCache && ENABLE_DISCOVERY_CACHE; m_used_cache_last_time = useCache; bool slaveMode=false; if(!getOption("slaveMode", slaveMode)) { debugWarning("Could not retrieve slaveMode parameter, defauling to false\n"); } bool snoopMode=false; if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } setVerboseLevel(getDebugLevel()); // FIXME: it could be that a 1394service has disappeared (cardbus) ConfigRomVector configRoms; // build a list of configroms on the bus. for ( Ieee1394ServiceVectorIterator it = m_1394Services.begin(); it != m_1394Services.end(); ++it ) { Ieee1394Service *portService = *it; for ( fb_nodeid_t nodeId = 0; nodeId < portService->getNodeCount(); ++nodeId ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Probing node %d...\n", nodeId ); if (nodeId == portService->getLocalNodeId()) { debugOutput( DEBUG_LEVEL_VERBOSE, "Skipping local node (%d)...\n", nodeId ); continue; } ConfigRom * configRom = new ConfigRom( *portService, nodeId ); if ( !configRom->initialize() ) { // \todo If a PHY on the bus is in power safe mode then // the config rom is missing. So this might be just // such this case and we can safely skip it. But it might // be there is a real software problem on our side. // This should be handlede more carefuly. debugOutput( DEBUG_LEVEL_NORMAL, "Could not read config rom from device (node id %d). " "Skip device discovering for this node\n", nodeId ); continue; } configRoms.push_back(configRom); } } // notify that we are going to manipulate the list signalNotifiers(m_preUpdateNotifiers); m_DeviceListLock->Lock(); // make sure nobody starts using the list if(rediscover) { FFADODeviceVector discovered_devices_on_bus; for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { bool seen_device = false; for ( ConfigRomVectorIterator it2 = configRoms.begin(); it2 != configRoms.end(); ++it2 ) { seen_device |= ((*it)->getConfigRom().getGuid() == (*it2)->getGuid()); } if(seen_device) { debugOutput( DEBUG_LEVEL_VERBOSE, "Already discovered device with GUID: %s\n", (*it)->getConfigRom().getGuidString().c_str() ); // we already discovered this device, and it is still here. keep it discovered_devices_on_bus.push_back(*it); } else { debugOutput( DEBUG_LEVEL_VERBOSE, "Device with GUID: %s disappeared from bus, removing...\n", (*it)->getConfigRom().getGuidString().c_str() ); // the device has disappeared, remove it from the control tree if (!deleteElement(*it)) { debugWarning("failed to remove Device from Control::Container\n"); } // delete the device // FIXME: this will mess up the any code that waits for bus resets to // occur delete *it; } } // prune the devices that disappeared m_avDevices = discovered_devices_on_bus; } else { // remove everything since we are not rediscovering for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { if (!deleteElement(*it)) { debugWarning("failed to remove Device from Control::Container\n"); } delete *it; } m_avDevices.clear(); } // delete the config rom list entries // FIXME: we should reuse it for ( ConfigRomVectorIterator it = configRoms.begin(); it != configRoms.end(); ++it ) { delete *it; } assert(m_deviceStringParser); // show the spec strings we're going to use if(getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { m_deviceStringParser->show(); } if (!slaveMode) { // for the devices that are still in the list check if they require re-discovery FFADODeviceVector failed_to_rediscover; for ( FFADODeviceVectorIterator it_dev = m_avDevices.begin(); it_dev != m_avDevices.end(); ++it_dev ) { FFADODevice* avDevice = *it_dev; if(avDevice->needsRediscovery()) { debugOutput( DEBUG_LEVEL_NORMAL, "Device with GUID %s requires rediscovery (state changed)...\n", avDevice->getConfigRom().getGuidString().c_str()); bool isFromCache = false; if ( useCache && avDevice->loadFromCache() ) { debugOutput( DEBUG_LEVEL_VERBOSE, "could load from cache\n" ); isFromCache = true; // restore the debug level for everything that was loaded avDevice->setVerboseLevel( getDebugLevel() ); } else if ( avDevice->discover() ) { debugOutput( DEBUG_LEVEL_VERBOSE, "discovery successful\n" ); } else { debugError( "could not discover device\n" ); failed_to_rediscover.push_back(avDevice); continue; } if ( !isFromCache && !avDevice->saveCache() ) { debugOutput( DEBUG_LEVEL_VERBOSE, "No cached version of AVC model created\n" ); } } else { debugOutput( DEBUG_LEVEL_NORMAL, "Device with GUID %s does not require rediscovery...\n", avDevice->getConfigRom().getGuidString().c_str()); } } // remove devices that failed to rediscover // FIXME: surely there has to be a better way to do this FFADODeviceVector to_keep; for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { bool keep_this_device = true; for ( FFADODeviceVectorIterator it2 = failed_to_rediscover.begin(); it2 != failed_to_rediscover.end(); ++it2 ) { if(*it == *it2) { debugOutput( DEBUG_LEVEL_NORMAL, "Removing device with GUID %s due to failed discovery...\n", (*it)->getConfigRom().getGuidString().c_str()); keep_this_device = false; break; } } if(keep_this_device) { to_keep.push_back(*it); } } for ( FFADODeviceVectorIterator it2 = failed_to_rediscover.begin(); it2 != failed_to_rediscover.end(); ++it2 ) { if (!deleteElement(*it2)) { debugWarning("failed to remove Device from Control::Container\n"); } delete *it2; } m_avDevices = to_keep; // pick up new devices for ( Ieee1394ServiceVectorIterator it = m_1394Services.begin(); it != m_1394Services.end(); ++it ) { Ieee1394Service *portService = *it; for ( fb_nodeid_t nodeId = 0; nodeId < portService->getNodeCount(); ++nodeId ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Probing node %d...\n", nodeId ); if (nodeId == portService->getLocalNodeId()) { debugOutput( DEBUG_LEVEL_VERBOSE, "Skipping local node (%d)...\n", nodeId ); continue; } ConfigRom *configRom = new ConfigRom( *portService, nodeId ); if ( !configRom->initialize() ) { // \todo If a PHY on the bus is in power safe mode then // the config rom is missing. So this might be just // such this case and we can safely skip it. But it might // be there is a real software problem on our side. // This should be handlede more carefuly. debugOutput( DEBUG_LEVEL_NORMAL, "Could not read config rom from device (node id %d). " "Skip device discovering for this node\n", nodeId ); continue; } bool already_in_vector = false; for ( FFADODeviceVectorIterator it_dev = m_avDevices.begin(); it_dev != m_avDevices.end(); ++it_dev ) { if ((*it_dev)->getConfigRom().getGuid() == configRom->getGuid()) { already_in_vector = true; break; } } if(already_in_vector) { if(!rediscover) { debugWarning("Device with GUID %s already discovered on other port, skipping device...\n", configRom->getGuidString().c_str()); } continue; } if(getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { configRom->printConfigRomDebug(); } // if spec strings are given, only add those devices // that match the spec string(s). // if no (valid) spec strings are present, grab all // supported devices. if(m_deviceStringParser->countDeviceStrings() && !m_deviceStringParser->match(*configRom)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Device doesn't match any of the spec strings. skipping...\n"); continue; } // find a driver FFADODevice* avDevice = getDriverForDevice( configRom, nodeId ); if ( avDevice ) { debugOutput( DEBUG_LEVEL_NORMAL, "driver found for device %d\n", nodeId ); avDevice->setVerboseLevel( getDebugLevel() ); bool isFromCache = false; if ( useCache && avDevice->loadFromCache() ) { debugOutput( DEBUG_LEVEL_VERBOSE, "could load from cache\n" ); isFromCache = true; // restore the debug level for everything that was loaded avDevice->setVerboseLevel( getDebugLevel() ); } else if ( avDevice->discover() ) { debugOutput( DEBUG_LEVEL_VERBOSE, "discovery successful\n" ); } else { debugError( "could not discover device\n" ); delete avDevice; continue; } if (snoopMode) { debugOutput( DEBUG_LEVEL_VERBOSE, "Enabling snoop mode on node %d...\n", nodeId ); if(!avDevice->setOption("snoopMode", snoopMode)) { debugWarning("Could not set snoop mode for device on node %d\n", nodeId); delete avDevice; continue; } } if ( !isFromCache && !avDevice->saveCache() ) { debugOutput( DEBUG_LEVEL_VERBOSE, "No cached version of AVC model created\n" ); } m_avDevices.push_back( avDevice ); if (!addElement(avDevice)) { debugWarning("failed to add Device to Control::Container\n"); } debugOutput( DEBUG_LEVEL_NORMAL, "discovery of node %d on port %d done...\n", nodeId, portService->getPort() ); } else { // we didn't get a device, hence we have to delete the configrom ptr manually delete configRom; } } } debugOutput( DEBUG_LEVEL_NORMAL, "Discovery finished...\n" ); // FIXME: do better sorting // sort the m_avDevices vector on their GUID // then assign reassign the id's to the devices // the side effect of this is that for the same set of attached devices, // a device id always corresponds to the same device sort(m_avDevices.begin(), m_avDevices.end(), FFADODevice::compareGUID); int i=0; if(m_deviceStringParser->countDeviceStrings()) { // only if there are devicestrings // first map the devices to a position using the device spec strings std::map positionMap; for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { int pos = m_deviceStringParser->matchPosition((*it)->getConfigRom()); fb_octlet_t guid = (*it)->getConfigRom().getGuid(); positionMap[guid] = pos; debugOutput( DEBUG_LEVEL_VERBOSE, "Mapping %s to position %d...\n", (*it)->getConfigRom().getGuidString().c_str(), pos ); } // now run over all positions, and add the devices that belong to it FFADODeviceVector sorted; int nbPositions = m_deviceStringParser->countDeviceStrings(); for (i=0; i < nbPositions; i++) { for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { fb_octlet_t guid = (*it)->getConfigRom().getGuid(); if(positionMap[guid] == i) { sorted.push_back(*it); } } } // assign the new vector flushDebugOutput(); assert(sorted.size() == m_avDevices.size()); m_avDevices = sorted; } showDeviceInfo(); } else { // slave mode // notify any clients signalNotifiers(m_preUpdateNotifiers); Ieee1394Service *portService = m_1394Services.at(0); fb_nodeid_t nodeId = portService->getLocalNodeId(); debugOutput( DEBUG_LEVEL_VERBOSE, "Starting in slave mode on node %d...\n", nodeId ); ffado_smartptr configRom = ffado_smartptr( new ConfigRom( *portService, nodeId ) ); if ( !configRom->initialize() ) { // \todo If a PHY on the bus is in power safe mode then // the config rom is missing. So this might be just // such this case and we can safely skip it. But it might // be there is a real software problem on our side. // This should be handled more carefuly. debugOutput( DEBUG_LEVEL_NORMAL, "Could not read config rom from device (node id %d). " "Skip device discovering for this node\n", nodeId ); return false; } // remove any already present devices for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { if (!deleteElement(*it)) { debugWarning("failed to remove Device from Control::Container\n"); } delete *it; } m_avDevices.clear(); // get the slave driver FFADODevice* avDevice = getSlaveDriver( configRom ); if ( avDevice ) { debugOutput( DEBUG_LEVEL_NORMAL, "driver found for device %d\n", nodeId ); avDevice->setVerboseLevel( getDebugLevel() ); if ( !avDevice->discover() ) { debugError( "could not discover device\n" ); delete avDevice; return false; } if ( getDebugLevel() >= DEBUG_LEVEL_VERBOSE ) { avDevice->showDevice(); } m_avDevices.push_back( avDevice ); debugOutput( DEBUG_LEVEL_NORMAL, "discovery of node %d on port %d done...\n", nodeId, portService->getPort() ); } debugOutput( DEBUG_LEVEL_NORMAL, "discovery finished...\n" ); } m_DeviceListLock->Unlock(); // notify any clients signalNotifiers(m_postUpdateNotifiers); return true; } bool DeviceManager::initStreaming() { // iterate over the found devices // add the stream processors of the devices to the managers for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { FFADODevice *device = *it; assert(device); debugOutput(DEBUG_LEVEL_VERBOSE, "Locking device (%p)\n", device); if (!device->lock()) { debugWarning("Could not lock device, skipping device (%p)!\n", device); continue; } debugOutput(DEBUG_LEVEL_VERBOSE, "Setting samplerate to %d for (%p)\n", m_processorManager->getNominalRate(), device); // Set the device's sampling rate to that requested // FIXME: does this really belong here? If so we need to handle errors. if (!device->setSamplingFrequency(m_processorManager->getNominalRate())) { debugOutput(DEBUG_LEVEL_VERBOSE, " => Retry setting samplerate to %d for (%p)\n", m_processorManager->getNominalRate(), device); // try again: if (!device->setSamplingFrequency(m_processorManager->getNominalRate())) { debugFatal("Could not set sampling frequency to %d\n",m_processorManager->getNominalRate()); return false; } } // prepare the device device->prepare(); } // set the sync source if (!m_processorManager->setSyncSource(getSyncSource())) { debugWarning("Could not set processorManager sync source (%p)\n", getSyncSource()); } return true; } bool DeviceManager::prepareStreaming() { if (!m_processorManager->prepare()) { debugFatal("Could not prepare streaming...\n"); return false; } return true; } bool DeviceManager::finishStreaming() { bool result = true; // iterate over the found devices for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Unlocking device (%p)\n", *it); if (!(*it)->unlock()) { debugWarning("Could not unlock device (%p)!\n", *it); result = false; } } return result; } bool DeviceManager::startStreamingOnDevice(FFADODevice *device) { assert(device); int j=0; bool all_streams_started = true; bool device_start_failed = false; if (device->resetForStreaming() == false) return false; for(j=0; j < device->getStreamCount(); j++) { debugOutput(DEBUG_LEVEL_VERBOSE,"Starting stream %d of device %p\n", j, device); // start the stream if (!device->startStreamByIndex(j)) { debugWarning("Could not start stream %d of device %p\n", j, device); all_streams_started = false; break; } } if(!all_streams_started) { // disable all streams that did start up correctly for(j = j-1; j >= 0; j--) { debugOutput(DEBUG_LEVEL_VERBOSE,"Stopping stream %d of device %p\n", j, device); // stop the stream if (!device->stopStreamByIndex(j)) { debugWarning("Could not stop stream %d of device %p\n", j, device); } } device_start_failed = true; } else { if (!device->enableStreaming()) { debugWarning("Could not enable streaming on device %p!\n", device); device_start_failed = true; } } return !device_start_failed; } bool DeviceManager::startStreaming() { bool device_start_failed = false; FFADODeviceVectorIterator it; // create the connections for all devices // iterate over the found devices for ( it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { if (!startStreamingOnDevice(*it)) { debugWarning("Could not start streaming on device %p!\n", *it); device_start_failed = true; break; } } // if one of the devices failed to start, // the previous routine should have cleaned up the failing one. // we still have to stop all devices that were started before this one. if(device_start_failed) { for (FFADODeviceVectorIterator it2 = m_avDevices.begin(); it2 != it; ++it2 ) { if (!stopStreamingOnDevice(*it2)) { debugWarning("Could not stop streaming on device %p!\n", *it2); } } return false; } // start the stream processor manager to tune in to the channels if(m_processorManager->start()) { return true; } else { debugWarning("Failed to start SPM!\n"); for( it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { if (!stopStreamingOnDevice(*it)) { debugWarning("Could not stop streaming on device %p!\n", *it); } } return false; } } bool DeviceManager::resetStreaming() { return true; } bool DeviceManager::stopStreamingOnDevice(FFADODevice *device) { assert(device); bool result = true; if (!device->disableStreaming()) { debugWarning("Could not disable streaming on device %p!\n", device); } int j=0; for(j=0; j < device->getStreamCount(); j++) { debugOutput(DEBUG_LEVEL_VERBOSE,"Stopping stream %d of device %p\n", j, device); // stop the stream // start the stream if (!device->stopStreamByIndex(j)) { debugWarning("Could not stop stream %d of device %p\n", j, device); result = false; continue; } } return result; } bool DeviceManager::stopStreaming() { bool result = true; m_processorManager->stop(); // create the connections for all devices // iterate over the found devices // add the stream processors of the devices to the managers for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { stopStreamingOnDevice(*it); } return result; } enum DeviceManager::eWaitResult DeviceManager::waitForPeriod() { if(m_processorManager->waitForPeriod()) { return eWR_OK; } else { if(m_processorManager->shutdownNeeded()) { debugWarning("Shutdown requested\n"); return eWR_Shutdown; } else { debugWarning("XRUN detected\n"); // do xrun recovery if(m_processorManager->handleXrun()) { return eWR_Xrun; } else { debugError("Could not handle XRUN\n"); return eWR_Error; } } } } bool DeviceManager::setPeriodSize(unsigned int period) { // Useful for cases where only the period size needs adjusting if (!m_processorManager->streamingParamsOk(period, -1, -1)) { return false; } m_processorManager->setPeriodSize(period); return true; } bool DeviceManager::setStreamingParams(unsigned int period, unsigned int rate, unsigned int nb_buffers) { if (!m_processorManager->streamingParamsOk(period, rate, nb_buffers)) { return false; } m_processorManager->setPeriodSize(period); m_processorManager->setNominalRate(rate); m_processorManager->setNbBuffers(nb_buffers); return true; } FFADODevice* DeviceManager::getDriverForDeviceDo( ConfigRom *configRom, int id, bool generic ) { #ifdef ENABLE_BEBOB debugOutput( DEBUG_LEVEL_VERBOSE, "Trying BeBoB...\n" ); if ( BeBoB::Device::probe( getConfiguration(), *configRom, generic ) ) { return BeBoB::Device::createDevice( *this, ffado_smartptr( configRom ) ); } #endif #ifdef ENABLE_FIREWORKS debugOutput( DEBUG_LEVEL_VERBOSE, "Trying ECHO Audio FireWorks...\n" ); if ( FireWorks::Device::probe( getConfiguration(), *configRom, generic ) ) { return FireWorks::Device::createDevice( *this, ffado_smartptr( configRom ) ); } #endif #ifdef ENABLE_OXFORD debugOutput( DEBUG_LEVEL_VERBOSE, "Trying Oxford FW90x...\n" ); if ( Oxford::Device::probe( getConfiguration(), *configRom, generic ) ) { return Oxford::Device::createDevice( *this, ffado_smartptr( configRom ) ); } #endif // we want to try the non-generic AV/C platforms before trying the generic ones #ifdef ENABLE_GENERICAVC debugOutput( DEBUG_LEVEL_VERBOSE, "Trying Generic AV/C...\n" ); if ( GenericAVC::Device::probe( getConfiguration(), *configRom, generic ) ) { return GenericAVC::Device::createDevice( *this, ffado_smartptr( configRom ) ); } #endif #ifdef ENABLE_MOTU debugOutput( DEBUG_LEVEL_VERBOSE, "Trying Motu...\n" ); if ( Motu::MotuDevice::probe( getConfiguration(), *configRom, generic ) ) { return Motu::MotuDevice::createDevice( *this, ffado_smartptr( configRom ) ); } #endif #ifdef ENABLE_DICE debugOutput( DEBUG_LEVEL_VERBOSE, "Trying Dice...\n" ); if ( Dice::Device::probe( getConfiguration(), *configRom, generic ) ) { return Dice::Device::createDevice( *this, ffado_smartptr( configRom ) ); } #endif #ifdef ENABLE_METRIC_HALO debugOutput( DEBUG_LEVEL_VERBOSE, "Trying Metric Halo...\n" ); if ( MetricHalo::Device::probe( getConfiguration(), *configRom, generic ) ) { return MetricHalo::Device::createDevice( *this, ffado_smartptr( configRom ) ); } #endif #ifdef ENABLE_RME debugOutput( DEBUG_LEVEL_VERBOSE, "Trying RME...\n" ); if ( Rme::Device::probe( getConfiguration(), *configRom, generic ) ) { return Rme::Device::createDevice( *this, ffado_smartptr( configRom ) ); } #endif #ifdef ENABLE_BOUNCE debugOutput( DEBUG_LEVEL_VERBOSE, "Trying Bounce...\n" ); if ( Bounce::Device::probe( getConfiguration(), *configRom, generic ) ) { return Bounce::Device::createDevice( *this, ffado_smartptr( configRom ) ); } #endif return NULL; } FFADODevice* DeviceManager::getDriverForDevice( ConfigRom *configRom, int id ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Probing for supported device...\n" ); FFADODevice* dev = getDriverForDeviceDo(configRom, id, false); if(dev) { debugOutput( DEBUG_LEVEL_VERBOSE, " found supported device...\n" ); dev->setVerboseLevel(getDebugLevel()); return dev; } debugOutput( DEBUG_LEVEL_VERBOSE, " no supported device found, trying generic support...\n" ); dev = getDriverForDeviceDo(configRom, id, true); if(dev) { debugOutput( DEBUG_LEVEL_VERBOSE, " found generic support for device...\n" ); dev->setVerboseLevel(getDebugLevel()); return dev; } debugOutput( DEBUG_LEVEL_VERBOSE, " device not supported...\n" ); return NULL; } FFADODevice* DeviceManager::getSlaveDriver( ffado_smartptr( configRom ) ) { #ifdef ENABLE_BOUNCE if ( Bounce::SlaveDevice::probe( getConfiguration(), *configRom, false ) ) { return Bounce::SlaveDevice::createDevice( *this, ffado_smartptr( configRom ) ); } #endif return NULL; } bool DeviceManager::isValidNode(int node) { for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { FFADODevice* avDevice = *it; if (avDevice->getConfigRom().getNodeId() == node) { return true; } } return false; } int DeviceManager::getNbDevices() { return m_avDevices.size(); } int DeviceManager::getDeviceNodeId( int deviceNr ) { if ( ! ( deviceNr < getNbDevices() ) ) { debugError( "Device number out of range (%d)\n", deviceNr ); return -1; } FFADODevice* avDevice = m_avDevices.at( deviceNr ); if ( !avDevice ) { debugError( "Could not get device at position (%d)\n", deviceNr ); } return avDevice->getConfigRom().getNodeId(); } FFADODevice* DeviceManager::getAvDevice( int nodeId ) { for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { FFADODevice* avDevice = *it; if ( avDevice->getConfigRom().getNodeId() == nodeId ) { return avDevice; } } return 0; } FFADODevice* DeviceManager::getAvDeviceByIndex( int idx ) { return m_avDevices.at(idx); } unsigned int DeviceManager::getAvDeviceCount( ) { return m_avDevices.size(); } /** * Return the streamprocessor that is to be used as * the sync source. * * Algorithm still to be determined * * @return StreamProcessor that is sync source */ Streaming::StreamProcessor * DeviceManager::getSyncSource() { FFADODevice* device = getAvDeviceByIndex(0); bool slaveMode=false; if(!getOption("slaveMode", slaveMode)) { debugOutput(DEBUG_LEVEL_NORMAL, "Could not retrieve slaveMode parameter, defauling to false\n"); } return device->getStreamProcessorByIndex(0); /* #warning TEST CODE FOR BOUNCE DEVICE !! // this makes the bounce slave use the xmit SP as sync source if (slaveMode) { return device->getStreamProcessorByIndex(1); } else { return device->getStreamProcessorByIndex(0); }*/ } bool DeviceManager::deinitialize() { return true; } void DeviceManager::setVerboseLevel(int l) { setDebugLevel(l); Control::Element::setVerboseLevel(l); m_processorManager->setVerboseLevel(l); m_deviceStringParser->setVerboseLevel(l); m_configuration->setVerboseLevel(l); for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { (*it)->setVerboseLevel(l); } for ( Ieee1394ServiceVectorIterator it = m_1394Services.begin(); it != m_1394Services.end(); ++it ) { (*it)->setVerboseLevel(l); } debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); } void DeviceManager::showDeviceInfo() { debugOutput(DEBUG_LEVEL_NORMAL, "===== Device Manager =====\n"); Control::Element::show(); int i=0; for ( Ieee1394ServiceVectorIterator it = m_1394Services.begin(); it != m_1394Services.end(); ++it ) { debugOutput(DEBUG_LEVEL_NORMAL, "--- IEEE1394 Service %2d ---\n", i++); (*it)->show(); } i=0; for ( FFADODeviceVectorIterator it = m_avDevices.begin(); it != m_avDevices.end(); ++it ) { FFADODevice* avDevice = *it; debugOutput(DEBUG_LEVEL_NORMAL, "--- Device %2d ---\n", i++); avDevice->showDevice(); debugOutput(DEBUG_LEVEL_NORMAL, "Clock sync sources:\n"); FFADODevice::ClockSourceVector sources=avDevice->getSupportedClockSources(); for ( FFADODevice::ClockSourceVector::const_iterator it = sources.begin(); it != sources.end(); ++it ) { FFADODevice::ClockSource c=*it; debugOutput(DEBUG_LEVEL_NORMAL, " Type: %s, Id: %2d, Valid: %1d, Active: %1d, Locked %1d, Slipping: %1d, Description: %s\n", FFADODevice::ClockSourceTypeToString(c.type), c.id, c.valid, c.active, c.locked, c.slipping, c.description.c_str()); } } } void DeviceManager::showStreamingInfo() { m_processorManager->dumpInfo(); } libffado-2.4.5/src/devicemanager.h0000644000175000001440000001427614206145246016430 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FFADODEVICEMANAGER_H #define FFADODEVICEMANAGER_H #include "debugmodule/debugmodule.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libstreaming/StreamProcessorManager.h" #include "libutil/OptionContainer.h" #include "libcontrol/BasicElements.h" #include "libutil/Functors.h" #include "libutil/Mutex.h" #include "libutil/Configuration.h" #include #include class Ieee1394Service; class FFADODevice; class DeviceStringParser; namespace Streaming { class StreamProcessor; } typedef std::vector< FFADODevice* > FFADODeviceVector; typedef std::vector< FFADODevice* >::iterator FFADODeviceVectorIterator; typedef std::vector< Ieee1394Service* > Ieee1394ServiceVector; typedef std::vector< Ieee1394Service* >::iterator Ieee1394ServiceVectorIterator; typedef std::vector< Util::Functor* > FunctorVector; typedef std::vector< Util::Functor* >::iterator FunctorVectorIterator; typedef std::vector< ConfigRom* > ConfigRomVector; typedef std::vector< ConfigRom* >::iterator ConfigRomVectorIterator; class DeviceManager : public Util::OptionContainer, public Control::Container { public: enum eWaitResult { eWR_OK, eWR_Xrun, eWR_Error, eWR_Shutdown, }; DeviceManager(); ~DeviceManager(); bool setThreadParameters(bool rt, int priority); bool initialize(); bool deinitialize(); bool addSpecString(char *); bool isSpecStringValid(std::string s); bool discover(bool useCache=true, bool rediscover=false); bool initStreaming(); bool prepareStreaming(); bool finishStreaming(); bool startStreamingOnDevice(FFADODevice *device); bool startStreaming(); bool stopStreamingOnDevice(FFADODevice *device); bool stopStreaming(); bool resetStreaming(); enum eWaitResult waitForPeriod(); bool setPeriodSize(unsigned int period); 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(); /** * prevents the busreset handler from running. use with care! */ void lockBusResetHandler() {m_BusResetLock->Lock();}; /** * releases the busreset handlers */ void unlockBusResetHandler() {m_BusResetLock->Unlock();}; bool registerBusresetNotification(Util::Functor *f) {return registerNotification(m_busResetNotifiers, f);}; bool unregisterBusresetNotification(Util::Functor *f) {return unregisterNotification(m_busResetNotifiers, f);}; bool registerPreUpdateNotification(Util::Functor *f) {return registerNotification(m_preUpdateNotifiers, f);}; bool unregisterPreUpdateNotification(Util::Functor *f) {return unregisterNotification(m_preUpdateNotifiers, f);}; bool registerPostUpdateNotification(Util::Functor *f) {return registerNotification(m_postUpdateNotifiers, f);}; bool unregisterPostUpdateNotification(Util::Functor *f) {return unregisterNotification(m_postUpdateNotifiers, f);}; Util::Configuration& getConfiguration() {return *m_configuration;}; void showDeviceInfo(); void showStreamingInfo(); // the Control::Container functions virtual std::string getName() {return "DeviceManager";}; virtual bool setName( std::string n ) { return false;}; protected: FFADODevice* getDriverForDeviceDo( ConfigRom *configRom, int id, bool generic ); FFADODevice* getDriverForDevice( ConfigRom *configRom, int id ); FFADODevice* getSlaveDriver( ffado_smartptr( configRom ) ); void busresetHandler(Ieee1394Service &); protected: // we have one service for each port // found on the system. We don't allow dynamic addition of ports (yet) Ieee1394ServiceVector m_1394Services; FFADODeviceVector m_avDevices; FunctorVector m_busreset_functors; // the lock protecting the device list Util::Mutex* m_DeviceListLock; // the lock to serialize bus reset handling Util::Mutex* m_BusResetLock; public: // FIXME: this should be better Streaming::StreamProcessorManager& getStreamProcessorManager() {return *m_processorManager;}; private: Streaming::StreamProcessorManager* m_processorManager; DeviceStringParser* m_deviceStringParser; Util::Configuration* m_configuration; bool m_used_cache_last_time; typedef std::vector< Util::Functor* > notif_vec_t; notif_vec_t m_busResetNotifiers; notif_vec_t m_preUpdateNotifiers; notif_vec_t m_postUpdateNotifiers; bool registerNotification(notif_vec_t&, Util::Functor *); bool unregisterNotification(notif_vec_t&, Util::Functor *); void signalNotifiers(notif_vec_t& list); protected: std::vector m_SpecStrings; bool m_thread_realtime; int m_thread_priority; // debug stuff public: void setVerboseLevel(int l); private: DECLARE_DEBUG_MODULE; }; #endif libffado-2.4.5/src/dice/0000755000175000001440000000000014206145612014354 5ustar jwoitheuserslibffado-2.4.5/src/dice/dice_avdevice.cpp0000644000175000001440000021762714206145246017654 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "dice/dice_avdevice.h" #include "dice/dice_defines.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "debugmodule/debugmodule.h" #include "libutil/ByteSwap.h" #include #include #include #include #include #include #include #include #include #include "libutil/Configuration.h" #include "devicemanager.h" #include "focusrite/saffire_pro40.h" #include "focusrite/saffire_pro26.h" #include "focusrite/saffire_pro24.h" #include "focusrite/saffire_pro14.h" #include "maudio/profire_2626.h" #include "presonus/firestudio_tube.h" #include "presonus/firestudio_project.h" #include "presonus/firestudio_mobile.h" using namespace std; namespace Dice { Device::Device( DeviceManager& d, ffado_smartptr( configRom )) : FFADODevice( d, configRom ) , m_eap( NULL ) , m_global_reg_offset (0xFFFFFFFFLU) , m_global_reg_size (0xFFFFFFFFLU) , m_tx_reg_offset (0xFFFFFFFFLU) , m_tx_reg_size (0xFFFFFFFFLU) , m_rx_reg_offset (0xFFFFFFFFLU) , m_rx_reg_size (0xFFFFFFFFLU) , m_unused1_reg_offset (0xFFFFFFFFLU) , m_unused1_reg_size (0xFFFFFFFFLU) , m_unused2_reg_offset (0xFFFFFFFFLU) , m_unused2_reg_size (0xFFFFFFFFLU) , m_nb_tx (0xFFFFFFFFLU) , m_tx_size (0xFFFFFFFFLU) , m_nb_rx (0xFFFFFFFFLU) , m_rx_size (0xFFFFFFFFLU) , m_notifier (NULL) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Dice::Device (NodeID %d)\n", getConfigRom().getNodeId() ); addOption(Util::OptionContainer::Option("snoopMode", false)); } Device::~Device() { for ( StreamProcessorVectorIterator it = m_receiveProcessors.begin(); it != m_receiveProcessors.end(); ++it ) { delete *it; } for ( StreamProcessorVectorIterator it = m_transmitProcessors.begin(); it != m_transmitProcessors.end(); ++it ) { delete *it; } if (m_notifier) { unlock(); } if(m_eap) { delete m_eap; } } bool Device::probe( Util::Configuration& c, ConfigRom& configRom, bool generic ) { if (generic) { return false; } else { // check if device is in supported devices list unsigned int vendorId = configRom.getNodeVendorId(); unsigned int modelId = configRom.getModelId(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); return c.isValid(vme) && vme.driver == Util::Configuration::eD_DICE; } } FFADODevice * Device::createDevice( DeviceManager& d, ffado_smartptr( configRom )) { unsigned int vendorId = configRom->getNodeVendorId(); unsigned int modelId = configRom->getModelId(); switch (vendorId) { case FW_VENDORID_FOCUSRITE: switch(modelId) { case 0x00000005: return new Focusrite::SaffirePro40(d, configRom); case 0x00000007: case 0x00000008: return new Focusrite::SaffirePro24(d, configRom); case 0x00000009: return new Focusrite::SaffirePro14(d, configRom); case 0x00000012: return new Focusrite::SaffirePro26(d, configRom); default: // return a plain Dice device return new Device(d, configRom); } case FW_VENDORID_MAUDIO: switch(modelId) { case 0x00000010: // ProFire 2626 case 0x00000011: // ProFire 610 return new Maudio::Profire2626(d, configRom); default: // return a plain Dice device return new Device(d, configRom); } case FW_VENDORID_PRESONUS: switch(modelId) { case 0x0000000c: return new Presonus::FirestudioTube(d, configRom); case 0x0000000b: return new Presonus::FirestudioProject(d, configRom); case 0x00000011: return new Presonus::FirestudioMobile(d, configRom); default: // return a plain Dice device return new Device(d, configRom); } default: return new Device(d, configRom); } return NULL; } bool Device::discover() { unsigned int vendorId = getConfigRom().getNodeVendorId(); unsigned int modelId = getConfigRom().getModelId(); Util::Configuration &c = getDeviceManager().getConfiguration(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); if (c.isValid(vme) && vme.driver == Util::Configuration::eD_DICE) { debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", vme.vendor_name.c_str(), vme.model_name.c_str()); } else { debugWarning("Using generic DICE support for unsupported device '%s %s'\n", getConfigRom().getVendorName().c_str(), getConfigRom().getModelName().c_str()); } if ( !initIoFunctions() ) { debugError("Could not initialize I/O functions\n"); return false; } m_eap = createEAP(); if(m_eap == NULL) { debugError("Failed to allocate EAP.\n"); return false; } if(!m_eap->init()) { debugWarning("Could not init EAP\n"); delete m_eap; m_eap = NULL; } else { // register the EAP controls to the control structure if(!addElement(m_eap)) { debugError("Failed to add the EAP controls to the control tree\n"); return false; } } return true; } EAP* Device::createEAP() { return new EAP(*this); } enum Device::eDiceConfig Device::getCurrentConfig() { int samplerate = getSamplingFrequency(); if(samplerate > 31999 && samplerate <= 48000) { return eDC_Low; } if(samplerate > 48000 && samplerate <= 96000) { return eDC_Mid; } if(samplerate > 96000 && samplerate <= 192000) { return eDC_High; } return eDC_Unknown; } int Device::getSamplingFrequency() { int samplingFrequency; fb_quadlet_t clockreg; if (!readGlobalReg(DICE_REGISTER_GLOBAL_CLOCK_SELECT, &clockreg)) { debugError("Could not read CLOCK_SELECT register\n"); return false; } clockreg = DICE_GET_RATE(clockreg); switch (clockreg) { case DICE_RATE_32K: samplingFrequency = 32000; break; case DICE_RATE_44K1: samplingFrequency = 44100; break; case DICE_RATE_48K: samplingFrequency = 48000; break; case DICE_RATE_88K2: samplingFrequency = 88200; break; case DICE_RATE_96K: samplingFrequency = 96000; break; case DICE_RATE_176K4: samplingFrequency = 176400; break; case DICE_RATE_192K: samplingFrequency = 192000; break; case DICE_RATE_ANY_LOW: samplingFrequency = 0; break; case DICE_RATE_ANY_MID: samplingFrequency = 0; break; case DICE_RATE_ANY_HIGH: samplingFrequency = 0; break; case DICE_RATE_NONE: samplingFrequency = 0; break; default: samplingFrequency = 0; break; } return samplingFrequency; } #define DICE_CHECK_AND_ADD_SR(v, x, reg) \ { if(maskedCheckNotZeroGlobalReg( DICE_REGISTER_GLOBAL_CLOCKCAPABILITIES, reg)) \ v.push_back(x); } std::vector Device::getSupportedSamplingFrequencies() { std::vector frequencies; DICE_CHECK_AND_ADD_SR(frequencies, 32000, DICE_CLOCKCAP_RATE_32K); DICE_CHECK_AND_ADD_SR(frequencies, 44100, DICE_CLOCKCAP_RATE_44K1); DICE_CHECK_AND_ADD_SR(frequencies, 48000, DICE_CLOCKCAP_RATE_48K); DICE_CHECK_AND_ADD_SR(frequencies, 88200, DICE_CLOCKCAP_RATE_88K2); DICE_CHECK_AND_ADD_SR(frequencies, 96000, DICE_CLOCKCAP_RATE_96K); // In order to run at 4x rates on the DICE platform, one needs to // implement support for "dual-wire" mode within the FFADO streaming // engine. Until this is done there's no point advertising 4x rate // capabilities. While theoretically devices based on newer DICE // versions don't require dual-wire mode at these rates, as of 1 May // 2012 no such devices are known. See also ticket #343. // // DICE_CHECK_AND_ADD_SR(frequencies, 176400, DICE_CLOCKCAP_RATE_176K4); // DICE_CHECK_AND_ADD_SR(frequencies, 192000, DICE_CLOCKCAP_RATE_192K); return frequencies; } int Device::getConfigurationId() { return 0; } bool Device::onSamplerateChange( int oldSamplingFrequency ) { int current_sr = getSamplingFrequency(); debugOutput(DEBUG_LEVEL_VERBOSE, "Current sample rate is: %d\n", (current_sr)); debugOutput(DEBUG_LEVEL_VERBOSE, "Previous sample rate was: %d\n", (oldSamplingFrequency)); if (current_sr != oldSamplingFrequency) { // Update for the new samplerate if (m_eap) { m_eap->update(); } if ( !initIoFunctions() ) { debugError("Could not initialize I/O functions\n"); return false; } showDevice(); return true; } return false; } bool Device::setSamplingFrequency( int samplingFrequency ) { printMessage("Setting sample rate: %d\n", (samplingFrequency)); bool supported=false; fb_quadlet_t select=0x0; fb_quadlet_t clockreg; bool snoopMode = false; if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defaulting to false\n"); } int current_sr = getSamplingFrequency(); if(snoopMode) { if (current_sr != samplingFrequency ) { debugError("In snoop mode it is impossible to set the sample rate.\n"); debugError("Please start the client with the correct setting.\n"); return false; } return true; } else { switch ( samplingFrequency ) { default: case 22050: case 24000: supported=false; break; case 32000: supported=maskedCheckNotZeroGlobalReg( DICE_REGISTER_GLOBAL_CLOCKCAPABILITIES, DICE_CLOCKCAP_RATE_32K); select=DICE_RATE_32K; break; case 44100: supported=maskedCheckNotZeroGlobalReg( DICE_REGISTER_GLOBAL_CLOCKCAPABILITIES, DICE_CLOCKCAP_RATE_44K1); select=DICE_RATE_44K1; break; case 48000: supported=maskedCheckNotZeroGlobalReg( DICE_REGISTER_GLOBAL_CLOCKCAPABILITIES, DICE_CLOCKCAP_RATE_48K); select=DICE_RATE_48K; break; case 88200: supported=maskedCheckNotZeroGlobalReg( DICE_REGISTER_GLOBAL_CLOCKCAPABILITIES, DICE_CLOCKCAP_RATE_88K2); select=DICE_RATE_88K2; break; case 96000: supported=maskedCheckNotZeroGlobalReg( DICE_REGISTER_GLOBAL_CLOCKCAPABILITIES, DICE_CLOCKCAP_RATE_96K); select=DICE_RATE_96K; break; // We currently can't support 4x rates on these devices. See note // in getSupportedSamplingFrequencies(). #if 0 case 176400: supported=maskedCheckNotZeroGlobalReg( DICE_REGISTER_GLOBAL_CLOCKCAPABILITIES, DICE_CLOCKCAP_RATE_176K4); select=DICE_RATE_176K4; break; case 192000: supported=maskedCheckNotZeroGlobalReg( DICE_REGISTER_GLOBAL_CLOCKCAPABILITIES, DICE_CLOCKCAP_RATE_192K); select=DICE_RATE_192K; break; #endif } if (!supported) { debugWarning("Unsupported sample rate: %d\n", (samplingFrequency)); return false; } #if USE_OLD_DEFENSIVE_STREAMING_PROTECTION if (isIsoStreamingEnabled()) { debugError("Cannot change samplerate while streaming is enabled\n"); return false; } #endif if (!readGlobalReg(DICE_REGISTER_GLOBAL_CLOCK_SELECT, &clockreg)) { debugError("Could not read CLOCK_SELECT register\n"); return false; } clockreg = DICE_SET_RATE(clockreg, select); if (!writeGlobalReg(DICE_REGISTER_GLOBAL_CLOCK_SELECT, clockreg)) { debugError("Could not write CLOCK_SELECT register\n"); return false; } // check if the write succeeded fb_quadlet_t clockreg_verify; if (!readGlobalReg(DICE_REGISTER_GLOBAL_CLOCK_SELECT, &clockreg_verify)) { debugError("Could not read CLOCK_SELECT register\n"); return false; } if(clockreg != clockreg_verify) { debugError("Samplerate register write failed\n"); return false; } } // Wait up to 2s for the device to lock to the desired samplerate fb_quadlet_t statusreg; readGlobalReg(DICE_REGISTER_GLOBAL_STATUS, &statusreg); int n_it = 0; while (((statusreg&0x1) == 0 || ((clockreg>>8) & 0xFF) != ((statusreg>>8) & 0xFF)) && n_it < 20) { usleep(100000); readGlobalReg(DICE_REGISTER_GLOBAL_STATUS, &statusreg); n_it++; } if (n_it == 20) { debugWarning(" Initialization started before device was locked\n"); } // Update for the new samplerate if (onSamplerateChange(current_sr)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Device configuration updated"); } return true; } FFADODevice::ClockSourceVector Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; quadlet_t clock_caps; readGlobalReg(DICE_REGISTER_GLOBAL_CLOCKCAPABILITIES, &clock_caps); uint16_t clocks_supported = (clock_caps >> 16) & 0xFFFF; debugOutput(DEBUG_LEVEL_VERBOSE," Clock caps: 0x%08" PRIX32 ", supported=0x%04X\n", clock_caps, clocks_supported); quadlet_t clock_select; readGlobalReg(DICE_REGISTER_GLOBAL_CLOCK_SELECT, &clock_select); byte_t clock_selected = (clock_select) & 0xFF; debugOutput(DEBUG_LEVEL_VERBOSE," Clock select: 0x%08" PRIX32 ", selected=0x%04X\n", clock_select, clock_selected); quadlet_t extended_status; readGlobalReg(DICE_REGISTER_GLOBAL_EXTENDED_STATUS, &extended_status); #ifdef DEBUG uint16_t clock_status = (extended_status) & 0xFFFF; uint16_t clock_slipping = (extended_status >> 16) & 0xFFFF; debugOutput(DEBUG_LEVEL_VERBOSE," Clock status: 0x%08" PRIX32 ", status=0x%04X, slip=0x%04X\n", extended_status, clock_status, clock_slipping); #endif stringlist names = getClockSourceNameString(); if( names.size() < DICE_CLOCKSOURCE_COUNT) { debugError("Not enough clock source names on device\n"); return r; } for (unsigned int i=0; i < DICE_CLOCKSOURCE_COUNT; i++) { bool supported = (((clocks_supported >> i) & 0x01) == 1); if (supported) { ClockSource s; s.type = clockIdToType(i); s.id = i; s.valid = true; s.locked = isClockSourceIdLocked(i, extended_status); s.slipping = isClockSourceIdSlipping(i, extended_status); s.active = (clock_selected == i); s.description = names.at(i); r.push_back(s); } else { debugOutput(DEBUG_LEVEL_VERBOSE, "Clock source id %d not supported by device\n", i); } } return r; } bool Device::isClockSourceIdLocked(unsigned int id, quadlet_t ext_status_reg) { switch (id) { default: return true; case DICE_CLOCKSOURCE_AES1: return ext_status_reg & DICE_EXT_STATUS_AES0_LOCKED; case DICE_CLOCKSOURCE_AES2: return ext_status_reg & DICE_EXT_STATUS_AES1_LOCKED; case DICE_CLOCKSOURCE_AES3: return ext_status_reg & DICE_EXT_STATUS_AES2_LOCKED; case DICE_CLOCKSOURCE_AES4: return ext_status_reg & DICE_EXT_STATUS_AES3_LOCKED; case DICE_CLOCKSOURCE_AES_ANY: return ext_status_reg & DICE_EXT_STATUS_AES_ANY_LOCKED; case DICE_CLOCKSOURCE_ADAT: return ext_status_reg & DICE_EXT_STATUS_ADAT_LOCKED; case DICE_CLOCKSOURCE_TDIF: return ext_status_reg & DICE_EXT_STATUS_TDIF_LOCKED; case DICE_CLOCKSOURCE_ARX1: return ext_status_reg & DICE_EXT_STATUS_ARX1_LOCKED; case DICE_CLOCKSOURCE_ARX2: return ext_status_reg & DICE_EXT_STATUS_ARX2_LOCKED; case DICE_CLOCKSOURCE_ARX3: return ext_status_reg & DICE_EXT_STATUS_ARX3_LOCKED; case DICE_CLOCKSOURCE_ARX4: return ext_status_reg & DICE_EXT_STATUS_ARX4_LOCKED; case DICE_CLOCKSOURCE_WC: return ext_status_reg & DICE_EXT_STATUS_WC_LOCKED; } } bool Device::isClockSourceIdSlipping(unsigned int id, quadlet_t ext_status_reg) { switch (id) { default: return false; case DICE_CLOCKSOURCE_AES1: return ext_status_reg & DICE_EXT_STATUS_AES0_SLIP; case DICE_CLOCKSOURCE_AES2: return ext_status_reg & DICE_EXT_STATUS_AES1_SLIP; case DICE_CLOCKSOURCE_AES3: return ext_status_reg & DICE_EXT_STATUS_AES2_SLIP; case DICE_CLOCKSOURCE_AES4: return ext_status_reg & DICE_EXT_STATUS_AES3_SLIP; case DICE_CLOCKSOURCE_AES_ANY: return false; case DICE_CLOCKSOURCE_ADAT: return ext_status_reg & DICE_EXT_STATUS_ADAT_SLIP; case DICE_CLOCKSOURCE_TDIF: return ext_status_reg & DICE_EXT_STATUS_TDIF_SLIP; case DICE_CLOCKSOURCE_ARX1: return ext_status_reg & DICE_EXT_STATUS_ARX1_SLIP; case DICE_CLOCKSOURCE_ARX2: return ext_status_reg & DICE_EXT_STATUS_ARX2_SLIP; case DICE_CLOCKSOURCE_ARX3: return ext_status_reg & DICE_EXT_STATUS_ARX3_SLIP; case DICE_CLOCKSOURCE_ARX4: return ext_status_reg & DICE_EXT_STATUS_ARX4_SLIP; case DICE_CLOCKSOURCE_WC: // FIXME: not in driver spec, so non-existant return ext_status_reg & DICE_EXT_STATUS_WC_SLIP; } } enum FFADODevice::eClockSourceType Device::clockIdToType(unsigned int id) { switch (id) { default: return eCT_Invalid; case DICE_CLOCKSOURCE_AES1: case DICE_CLOCKSOURCE_AES2: case DICE_CLOCKSOURCE_AES3: case DICE_CLOCKSOURCE_AES4: case DICE_CLOCKSOURCE_AES_ANY: return eCT_AES; case DICE_CLOCKSOURCE_ADAT: return eCT_ADAT; case DICE_CLOCKSOURCE_TDIF: return eCT_TDIF; case DICE_CLOCKSOURCE_ARX1: case DICE_CLOCKSOURCE_ARX2: case DICE_CLOCKSOURCE_ARX3: case DICE_CLOCKSOURCE_ARX4: return eCT_SytMatch; case DICE_CLOCKSOURCE_WC: return eCT_WordClock; case DICE_CLOCKSOURCE_INTERNAL: return eCT_Internal; } } bool Device::setActiveClockSource(ClockSource s) { fb_quadlet_t clockreg; if (!readGlobalReg(DICE_REGISTER_GLOBAL_CLOCK_SELECT, &clockreg)) { debugError("Could not read CLOCK_SELECT register\n"); return false; } clockreg = DICE_SET_CLOCKSOURCE(clockreg, s.id); if (!writeGlobalReg(DICE_REGISTER_GLOBAL_CLOCK_SELECT, clockreg)) { debugError("Could not write CLOCK_SELECT register\n"); return false; } // check if the write succeeded fb_quadlet_t clockreg_verify; if (!readGlobalReg(DICE_REGISTER_GLOBAL_CLOCK_SELECT, &clockreg_verify)) { debugError("Could not read CLOCK_SELECT register\n"); return false; } if(clockreg != clockreg_verify) { debugError("CLOCK_SELECT register write failed\n"); return false; } return DICE_GET_CLOCKSOURCE(clockreg_verify) == s.id; } FFADODevice::ClockSource Device::getActiveClockSource() { ClockSource s; quadlet_t clock_caps; readGlobalReg(DICE_REGISTER_GLOBAL_CLOCKCAPABILITIES, &clock_caps); uint16_t clocks_supported = (clock_caps >> 16) & 0xFFFF; debugOutput(DEBUG_LEVEL_VERBOSE," Clock caps: 0x%08" PRIX32 ", supported=0x%04X\n", clock_caps, clocks_supported); quadlet_t clock_select; readGlobalReg(DICE_REGISTER_GLOBAL_CLOCK_SELECT, &clock_select); byte_t id = (clock_select) & 0xFF; debugOutput(DEBUG_LEVEL_VERBOSE," Clock select: 0x%08" PRIX32 ", selected=0x%04X\n", clock_select, id); quadlet_t extended_status; readGlobalReg(DICE_REGISTER_GLOBAL_EXTENDED_STATUS, &extended_status); #ifdef DEBUG uint16_t clock_status = (extended_status) & 0xFFFF; uint16_t clock_slipping = (extended_status >> 16) & 0xFFFF; debugOutput(DEBUG_LEVEL_VERBOSE," Clock status: 0x%08" PRIX32 ", status=0x%04X, slip=0x%04X\n", extended_status, clock_status, clock_slipping); #endif stringlist names = getClockSourceNameString(); if( names.size() < DICE_CLOCKSOURCE_COUNT) { debugError("Not enough clock source names on device\n"); return s; } bool supported = (((clocks_supported >> id) & 0x01) == 1); if (supported) { s.type = clockIdToType(id); s.id = id; s.valid = true; s.locked = isClockSourceIdLocked(id, extended_status); s.slipping = isClockSourceIdSlipping(id, extended_status); s.active = true; s.description = names.at(id); } else { debugOutput(DEBUG_LEVEL_VERBOSE, "Clock source id %2d not supported by device\n", id); } return s; } bool Device::setNickname( std::string name) { char namestring[DICE_NICK_NAME_SIZE+1]; strncpy(namestring, name.c_str(), DICE_NICK_NAME_SIZE); // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)namestring, DICE_NICK_NAME_SIZE/4); #endif if (!writeGlobalRegBlock(DICE_REGISTER_GLOBAL_NICK_NAME, (fb_quadlet_t *)namestring, DICE_NICK_NAME_SIZE)) { debugError("Could not write nickname string \n"); return false; } return true; } std::string Device::getNickname() { char namestring[DICE_NICK_NAME_SIZE+1]; if (!readGlobalRegBlock(DICE_REGISTER_GLOBAL_NICK_NAME, (fb_quadlet_t *)namestring, DICE_NICK_NAME_SIZE)) { debugError("Could not read nickname string \n"); return std::string("(unknown)"); } // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)namestring, DICE_NICK_NAME_SIZE/4); #endif namestring[DICE_NICK_NAME_SIZE]='\0'; return std::string(namestring); } void Device::showDevice() { fb_quadlet_t tmp_quadlet; fb_octlet_t tmp_octlet; debugOutput(DEBUG_LEVEL_NORMAL, "Device is a DICE device\n"); FFADODevice::showDevice(); printMessage(" DICE Parameter Space info:\n"); printMessage(" Global : offset=0x%04X size=%04d\n", m_global_reg_offset, m_global_reg_size); printMessage(" TX : offset=0x%04X size=%04d\n", m_tx_reg_offset, m_tx_reg_size); printMessage(" nb=%4d size=%04d\n", m_nb_tx, m_tx_size); printMessage(" RX : offset=0x%04X size=%04d\n", m_rx_reg_offset, m_rx_reg_size); printMessage(" nb=%4d size=%04d\n", m_nb_rx, m_rx_size); printMessage(" UNUSED1 : offset=0x%04X size=%04d\n", m_unused1_reg_offset, m_unused1_reg_size); printMessage(" UNUSED2 : offset=0x%04X size=%04d\n", m_unused2_reg_offset, m_unused2_reg_size); printMessage(" Global param space:\n"); readGlobalRegBlock(DICE_REGISTER_GLOBAL_OWNER, reinterpret_cast(&tmp_octlet), sizeof(fb_octlet_t)); printMessage(" Owner : 0x%016" PRIX64 "\n",tmp_octlet); readGlobalReg(DICE_REGISTER_GLOBAL_NOTIFICATION, &tmp_quadlet); printMessage(" Notification : 0x%08" PRIX32 "\n",tmp_quadlet); readGlobalReg(DICE_REGISTER_GLOBAL_NOTIFICATION, &tmp_quadlet); printMessage(" Nick name : %s\n",getNickname().c_str()); readGlobalReg(DICE_REGISTER_GLOBAL_CLOCK_SELECT, &tmp_quadlet); printMessage(" Clock Select : 0x%02X 0x%02X\n", (tmp_quadlet>>8) & 0xFF, tmp_quadlet & 0xFF); readGlobalReg(DICE_REGISTER_GLOBAL_ENABLE, &tmp_quadlet); printMessage(" Enable : %s\n", (tmp_quadlet&0x1?"true":"false")); readGlobalReg(DICE_REGISTER_GLOBAL_STATUS, &tmp_quadlet); printMessage(" Clock Status : %s 0x%02X\n", (tmp_quadlet&0x1?"locked":"not locked"), (tmp_quadlet>>8) & 0xFF); readGlobalReg(DICE_REGISTER_GLOBAL_EXTENDED_STATUS, &tmp_quadlet); printMessage(" Extended Status : 0x%08" PRIX32 "\n", tmp_quadlet); readGlobalReg(DICE_REGISTER_GLOBAL_SAMPLE_RATE, &tmp_quadlet); printMessage(" Samplerate : 0x%08" PRIX32 " (%" PRIu32 ")\n", tmp_quadlet, tmp_quadlet); readGlobalRegBlock(DICE_REGISTER_GLOBAL_VERSION, reinterpret_cast(&tmp_quadlet), sizeof(fb_quadlet_t)); printMessage(" Version : 0x%08" PRIX32 "\n", tmp_quadlet); readGlobalReg(DICE_REGISTER_GLOBAL_VERSION, &tmp_quadlet); printMessage(" Version : 0x%08" PRIX32 " (%u.%u.%u.%u)\n", tmp_quadlet, DICE_DRIVER_SPEC_VERSION_NUMBER_GET_A(tmp_quadlet), DICE_DRIVER_SPEC_VERSION_NUMBER_GET_B(tmp_quadlet), DICE_DRIVER_SPEC_VERSION_NUMBER_GET_C(tmp_quadlet), DICE_DRIVER_SPEC_VERSION_NUMBER_GET_D(tmp_quadlet) ); readGlobalReg(DICE_REGISTER_GLOBAL_CLOCKCAPABILITIES, &tmp_quadlet); printMessage(" Clock caps : 0x%08" PRIX32 "\n", tmp_quadlet); stringlist names=getClockSourceNameString(); printMessage(" Clock sources :\n"); for ( stringlist::iterator it = names.begin(); it != names.end(); ++it ) { printMessage(" %s\n", (*it).c_str()); } printMessage(" TX param space:\n"); printMessage(" Nb of xmit : %1d\n", m_nb_tx); for (unsigned int i=0;isendPayloadForNoDataPackets(false); #endif // transmit control parameters ((Streaming::AmdtpTransmitStreamProcessor*)p)->setMaxCyclesToTransmitEarly(xmit_max_cycles_early_transmit); ((Streaming::AmdtpTransmitStreamProcessor*)p)->setTransferDelay(xmit_transfer_delay); ((Streaming::AmdtpTransmitStreamProcessor*)p)->setMinCyclesBeforePresentation(xmit_min_cycles_before_presentation); } if(!p->init()) { debugFatal("Could not initialize %s processor!\n", dir); delete p; // non-fatal error, simply returning true. Only false is important for prepare(); return true; } // add audio ports to the processor for (unsigned int j=0;jsetDllBandwidth(dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete p; return false; } debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) %s SP on channel [%d audio, %d midi]\n", this, dir, nb_audio, nb_midi); // add the SP to the vector if (direction_requested == Streaming::Port::E_Capture) { m_receiveProcessors.push_back(p); } else { // we put this SP into the transmit SP vector, // no matter if we are in snoop mode or not // this allows us to find out what direction // a certain stream should have. m_transmitProcessors.push_back(p); } return true; } // NOTE on bandwidth calculation // FIXME: The bandwidth allocation calculation can probably be // refined somewhat since this is currently based on a rudimentary // understanding of the iso protocol. // Currently we assume the following. // * Ack/iso gap = 0.05 us // * DATA_PREFIX = 0.16 us1 // * 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 device 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. bool Device::prepare() { bool exit_code = true; // prepare receive SP's for (unsigned int i=0; iname; portname << id << "_" << channelInfo->name; Streaming::Port *p=NULL; switch(channelInfo->portType) { case ePT_Analog: p=new Streaming::AmdtpAudioPort( *processor, portname.str(), direction, channelInfo->streamPosition, channelInfo->streamLocation, Streaming::AmdtpPortInfo::E_MBLA ); break; case ePT_MIDI: p=new Streaming::AmdtpMidiPort( *processor, portname.str(), direction, channelInfo->streamPosition, channelInfo->streamLocation, Streaming::AmdtpPortInfo::E_Midi ); break; default: // unsupported break; } if (!p) { debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n",channelInfo->name.c_str()); } return true; } bool Device::lock() { fb_octlet_t result; debugOutput(DEBUG_LEVEL_VERBOSE, "Locking device at node %d\n", getNodeId()); bool snoopMode = false; if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } if (snoopMode) { debugWarning("Lock not supported in snoop mode\n"); return true; //FIXME: this should be false } else { // get a notifier to handle device notifications nodeaddr_t notify_address; notify_address = get1394Service().findFreeARMBlock( DICE_NOTIFIER_BASE_ADDRESS, DICE_NOTIFIER_BLOCK_LENGTH, DICE_NOTIFIER_BLOCK_LENGTH); if (notify_address == 0xFFFFFFFFFFFFFFFFLLU) { debugError("Could not find free ARM block for notification\n"); return false; } m_notifier = new Device::Notifier(*this, notify_address); if(!m_notifier) { debugError("Could not allocate notifier\n"); return false; } if (!get1394Service().registerARMHandler(m_notifier)) { debugError("Could not register notifier\n"); delete m_notifier; m_notifier=NULL; return false; } // register this notifier fb_nodeaddr_t addr = DICE_REGISTER_BASE + m_global_reg_offset + DICE_REGISTER_GLOBAL_OWNER; // registry offsets should always be smaller than 0x7FFFFFFF // because otherwise base + offset > 64bit if(m_global_reg_offset & 0x80000000) { debugError("register offset not initialized yet\n"); return false; } fb_nodeaddr_t swap_value = ((0xFFC0) | get1394Service().getLocalNodeId()); swap_value = swap_value << 48; swap_value |= m_notifier->getStart(); if (!get1394Service().lockCompareSwap64(getNodeId() | 0xFFC0, addr, DICE_OWNER_NO_OWNER, swap_value, &result )) { debugWarning("Could not register ourselves as device owner\n"); return false; } if (result != DICE_OWNER_NO_OWNER && result != swap_value) { debugWarning("Unexpected GLOBAL_OWNER register value: 0x%016" PRIX64 "\n", result); fprintf(stderr, "Could not register ourselves as owner of %s.\n", getNickname().c_str()); fprintf(stderr, "If the snd-dice kernel driver is present, " "either use the device via ALSA instead of FFADO, " "or unload snd-dice before using FFADO.\n"); return false; } return true; } } bool Device::unlock() { fb_octlet_t result; bool snoopMode = false; if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } if (snoopMode) { debugWarning("Unlock not supported in snoop mode\n"); return true; //FIXME: this should be false } else { if(!m_notifier) { debugWarning("Request to unlock, but no notifier present!\n"); return false; } fb_nodeaddr_t addr = DICE_REGISTER_BASE + m_global_reg_offset + DICE_REGISTER_GLOBAL_OWNER; // registry offsets should always be smaller than 0x7FFFFFFF // because otherwise base + offset > 64bit if(m_global_reg_offset & 0x80000000) { debugError("register offset not initialized yet\n"); return false; } fb_nodeaddr_t compare_value = ((0xFFC0) | get1394Service().getLocalNodeId()); compare_value <<= 48; compare_value |= m_notifier->getStart(); if (!get1394Service().lockCompareSwap64( getNodeId() | 0xFFC0, addr, compare_value, DICE_OWNER_NO_OWNER, &result )) { debugWarning("Could not unregister ourselves as device owner\n"); return false; } get1394Service().unregisterARMHandler(m_notifier); delete m_notifier; m_notifier=NULL; return true; } } bool Device::enableStreaming() { bool snoopMode = false; if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } if (snoopMode) { debugWarning("Stream should be already running for snoop mode\n"); return true; } else { return enableIsoStreaming(); } } bool Device::disableStreaming() { bool snoopMode = false; if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } if (snoopMode) { debugWarning("Won't disable stream in snoop mode\n"); return true; } else { return disableIsoStreaming(); } } int Device::getStreamCount() { return m_receiveProcessors.size() + m_transmitProcessors.size(); } Streaming::StreamProcessor * Device::getStreamProcessorByIndex(int i) { if (i<(int)m_receiveProcessors.size()) { return m_receiveProcessors.at(i); } else if (i<(int)m_receiveProcessors.size() + (int)m_transmitProcessors.size()) { return m_transmitProcessors.at(i-m_receiveProcessors.size()); } return NULL; } enum FFADODevice::eStreamingState Device::getStreamingState() { if (isIsoStreamingEnabled() == 0) return eSS_Idle; // If streaming is not idle assume both transmit and receive are // active since there is currently no way to distinguish these. return eSS_Both; } bool Device::startstopStreamByIndex(int i, const bool start) { bool snoopMode = false; fb_nodeaddr_t base_address; // holds DICE_REGISTER_TX_ISOC_BASE or DICE_REGISTER_RX_ISOC_BASE int n; // number of streaming processor Streaming::StreamProcessor *p; if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } #if USE_OLD_DEFENSIVE_STREAMING_PROTECTION if (!snoopMode && isIsoStreamingEnabled()) { debugError("Cannot start streams while streaming is enabled\n"); return false; } #endif if (i<(int)m_receiveProcessors.size()) { n=i; p = m_receiveProcessors.at(n); base_address = DICE_REGISTER_TX_ISOC_BASE; setRXTXfuncs (Streaming::Port::E_Capture); } else if (i<(int)m_receiveProcessors.size() + (int)m_transmitProcessors.size()) { n=i-m_receiveProcessors.size(); p=m_transmitProcessors.at(n); base_address = DICE_REGISTER_RX_ISOC_BASE; setRXTXfuncs (Streaming::Port::E_Playback); } else { debugError("SP index %d out of range!\n",i); return false; } if (start == true) { if(snoopMode) { // a stream from the device to another host fb_quadlet_t reg_isoch; // check value of ISO_CHANNEL register if(!(*this.*readFunc)(n, base_address, ®_isoch)) { debugError("Could not read ISO_CHANNEL register for A%s %d\n", dir, n); p->setChannel(-1); return false; } int isochannel = reg_isoch; debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) Snooping %s from channel %d\n", this, dir, isochannel); p->setChannel(isochannel); } else { // allocate ISO channel int isochannel = allocateIsoChannel(p->getMaxPacketSize()); if(isochannel<0) { debugError("Could not allocate iso channel for SP %d (A%s %d)\n", i, dir, n); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) Allocated channel %u for %s\n", this, isochannel, dir); p->setChannel(isochannel); fb_quadlet_t reg_isoch; // check value of ISO_CHANNEL register if(!(*this.*readFunc)(n, base_address, ®_isoch)) { debugError("Could not read ISO_CHANNEL register for A%s %d\n", dir, n); p->setChannel(-1); deallocateIsoChannel(isochannel); return false; } if(reg_isoch != 0xFFFFFFFFUL) { debugWarning("ISO_CHANNEL register != 0xFFFFFFFF (=0x%08" PRIX32 ") for A%s %d\n", reg_isoch, dir, n); /* The ISO channel has already been registered, probably * because the device was running before and jackd just * crashed. Let's simply reuse the previously selected * ISO channel. * * FIXME: try to reset the channel register and * return to a clean state */ deallocateIsoChannel(isochannel); p->setChannel(reg_isoch); #if 0 /* FIXME: Looks like it's not necessary to ask the IRM. * Just use the already registered ISO channel. */ // ask the IRM to use this channel if (get1394Service().allocateFixedIsoChannelGeneric(reg_isoch,p->getMaxPacketSize()) < 0) { debugError("Cannot allocate iso channel (0x%08" PRIX32 ") for ATX %d\n", reg_isoch, n); } #endif isochannel=reg_isoch; } // write value of ISO_CHANNEL register reg_isoch = isochannel; if(!(*this.*writeFunc)(n, base_address, reg_isoch)) { debugError("Could not write ISO_CHANNEL register for A%s %d\n", dir, n); p->setChannel(-1); deallocateIsoChannel(isochannel); return false; } } return true; } else { // stop if(!snoopMode) { unsigned int isochannel = p->getChannel(); fb_quadlet_t reg_isoch; // check value of ISO_CHANNEL register if(!(*this.*readFunc)(n, base_address, ®_isoch)) { debugError("Could not read ISO_CHANNEL register for A%s %d\n", dir, n); return false; } if(reg_isoch != isochannel) { debugError("ISO_CHANNEL register != 0x%08" PRIX32 " (=0x%08" PRIX32 ") for A%s %d\n", isochannel, reg_isoch, dir, n); return false; } // write value of ISO_CHANNEL register reg_isoch=0xFFFFFFFFUL; if(!writeTxReg(n, base_address, reg_isoch)) { debugError("Could not write ISO_CHANNEL register for A%s %d\n", dir, n); return false; } // deallocate ISO channel if(!deallocateIsoChannel(isochannel)) { debugError("Could not deallocate iso channel for SP %d (A%s %d)\n",i, dir, n); return false; } } p->setChannel(-1); return true; } } bool Device::startStreamByIndex(int i) { return startstopStreamByIndex(i, true); // start stream } bool Device::stopStreamByIndex(int i) { return startstopStreamByIndex(i, false); // stop stream } // helper routines // allocate ISO resources for the SP's int Device::allocateIsoChannel(unsigned int packet_size) { unsigned int bandwidth=8+packet_size; int ch=get1394Service().allocateIsoChannelGeneric(bandwidth); debugOutput(DEBUG_LEVEL_VERBOSE, "allocated channel %d, bandwidth %d\n", ch, bandwidth); return ch; } // deallocate ISO resources bool Device::deallocateIsoChannel(int channel) { debugOutput(DEBUG_LEVEL_VERBOSE, "freeing channel %d\n",channel); return get1394Service().freeIsoChannel(channel); } bool Device::enableIsoStreaming() { return writeGlobalReg(DICE_REGISTER_GLOBAL_ENABLE, DICE_ISOSTREAMING_ENABLE); } bool Device::disableIsoStreaming() { return writeGlobalReg(DICE_REGISTER_GLOBAL_ENABLE, DICE_ISOSTREAMING_DISABLE); } bool Device::isIsoStreamingEnabled() { fb_quadlet_t result; readGlobalReg(DICE_REGISTER_GLOBAL_ENABLE, &result); // I don't know what exactly is 'enable', // but disable is definately == 0 return (result != DICE_ISOSTREAMING_DISABLE); } /** * @brief performs a masked bit register equals 0 check on the global parameter space * @return true if readGlobalReg(offset) & mask == 0 */ bool Device::maskedCheckZeroGlobalReg(fb_nodeaddr_t offset, fb_quadlet_t mask) { fb_quadlet_t result; readGlobalReg(offset, &result); return ((result & mask) == 0); } /** * @brief performs a masked bit register not equal to 0 check on the global parameter space * @return true if readGlobalReg(offset) & mask != 0 */ bool Device::maskedCheckNotZeroGlobalReg(fb_nodeaddr_t offset, fb_quadlet_t mask) { return !maskedCheckZeroGlobalReg(offset, mask); } stringlist Device::getTxNameString(unsigned int i) { stringlist names; char namestring[DICE_TX_NAMES_SIZE+1]; if (!readTxRegBlock(i, DICE_REGISTER_TX_NAMES_BASE, (fb_quadlet_t *)namestring, DICE_TX_NAMES_SIZE)) { debugError("Could not read TX name string \n"); return names; } // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)namestring, DICE_TX_NAMES_SIZE/4); #endif namestring[DICE_TX_NAMES_SIZE]='\0'; return splitNameString(std::string(namestring)); } stringlist Device::getRxNameString(unsigned int i) { stringlist names; char namestring[DICE_RX_NAMES_SIZE+1]; if (!readRxRegBlock(i, DICE_REGISTER_RX_NAMES_BASE, (fb_quadlet_t *)namestring, DICE_RX_NAMES_SIZE)) { debugError("Could not read RX name string \n"); return names; } // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)namestring, DICE_RX_NAMES_SIZE/4); #endif namestring[DICE_RX_NAMES_SIZE]='\0'; return splitNameString(std::string(namestring)); } stringlist Device::getCptrNameString(unsigned int i) { if (m_eap) return m_eap->getCptrNameString(i); else return getTxNameString(i); } stringlist Device::getPbckNameString(unsigned int i) { if (m_eap) return m_eap->getPbckNameString(i); return getRxNameString(i); } stringlist Device::getClockSourceNameString() { stringlist names; char namestring[DICE_CLOCKSOURCENAMES_SIZE+1]; if (!readGlobalRegBlock(DICE_REGISTER_GLOBAL_CLOCKSOURCENAMES, (fb_quadlet_t *)namestring, DICE_CLOCKSOURCENAMES_SIZE)) { debugError("Could not read CLOCKSOURCE name string \n"); return names; } // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)namestring, DICE_CLOCKSOURCENAMES_SIZE/4); #endif namestring[DICE_CLOCKSOURCENAMES_SIZE]='\0'; return splitNameString(std::string(namestring)); } stringlist Device::splitNameString(std::string in) { in = in.substr(0,in.find("\\\\")); return stringlist::splitString(in, "\\"); } // I/O routines bool Device::initIoFunctions() { // offsets and sizes are returned in quadlets, but we use byte values if(!readReg(DICE_REGISTER_GLOBAL_PAR_SPACE_OFF, &m_global_reg_offset)) { debugError("Could not initialize m_global_reg_offset\n"); return false; } m_global_reg_offset*=4; if(!readReg(DICE_REGISTER_GLOBAL_PAR_SPACE_SZ, &m_global_reg_size)) { debugError("Could not initialize m_global_reg_size\n"); return false; } m_global_reg_size*=4; if(!readReg(DICE_REGISTER_TX_PAR_SPACE_OFF, &m_tx_reg_offset)) { debugError("Could not initialize m_tx_reg_offset\n"); return false; } m_tx_reg_offset*=4; if(!readReg(DICE_REGISTER_TX_PAR_SPACE_SZ, &m_tx_reg_size)) { debugError("Could not initialize m_tx_reg_size\n"); return false; } m_tx_reg_size*=4; if(!readReg(DICE_REGISTER_RX_PAR_SPACE_OFF, &m_rx_reg_offset)) { debugError("Could not initialize m_rx_reg_offset\n"); return false; } m_rx_reg_offset*=4; if(!readReg(DICE_REGISTER_RX_PAR_SPACE_SZ, &m_rx_reg_size)) { debugError("Could not initialize m_rx_reg_size\n"); return false; } m_rx_reg_size*=4; if(!readReg(DICE_REGISTER_UNUSED1_SPACE_OFF, &m_unused1_reg_offset)) { debugError("Could not initialize m_unused1_reg_offset\n"); return false; } m_unused1_reg_offset*=4; if(!readReg(DICE_REGISTER_UNUSED1_SPACE_SZ, &m_unused1_reg_size)) { debugError("Could not initialize m_unused1_reg_size\n"); return false; } m_unused1_reg_size*=4; if(!readReg(DICE_REGISTER_UNUSED2_SPACE_OFF, &m_unused2_reg_offset)) { debugError("Could not initialize m_unused2_reg_offset\n"); return false; } m_unused2_reg_offset*=4; if(!readReg(DICE_REGISTER_UNUSED2_SPACE_SZ, &m_unused2_reg_size)) { debugError("Could not initialize m_unused2_reg_size\n"); return false; } m_unused2_reg_size*=4; if(!readReg(m_tx_reg_offset + DICE_REGISTER_TX_NB_TX, &m_nb_tx)) { debugError("Could not initialize m_nb_tx\n"); return false; } if(!readReg(m_tx_reg_offset + DICE_REGISTER_TX_SZ_TX, &m_tx_size)) { debugError("Could not initialize m_tx_size\n"); return false; } m_tx_size*=4; if(!readReg(m_tx_reg_offset + DICE_REGISTER_RX_NB_RX, &m_nb_rx)) { debugError("Could not initialize m_nb_rx\n"); return false; } if(!readReg(m_tx_reg_offset + DICE_REGISTER_RX_SZ_RX, &m_rx_size)) { debugError("Could not initialize m_rx_size\n"); return false; } m_rx_size*=4; // FIXME: verify this and clean it up. Maybe check the number of channels // and ignore receivers with zero channels? /* special case for Alesis io14, which announces two receive transmitters, * but only has one. Same is true for Alesis Multimix16 and Focusrite * Saffire PRO 26. */ if (FW_VENDORID_ALESIS == getConfigRom().getNodeVendorId()) { switch (getConfigRom().getModelId()) { case 0x00000001: case 0x00000000: m_nb_rx = 1; break; } } if (FW_VENDORID_FOCUSRITE == getConfigRom().getNodeVendorId()) { switch (getConfigRom().getModelId()) { case 0x00000012: m_nb_rx = 1; break; } } #if USE_OLD_DEFENSIVE_STREAMING_PROTECTION // FIXME: after a crash, the device might still be streaming. We // simply force a stop now (unless in snoopMode) to return to a // clean state. bool snoopMode = false; if(!getOption("snoopMode", snoopMode)) { //debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } if (!snoopMode) { disableIsoStreaming(); } #endif debugOutput(DEBUG_LEVEL_VERBOSE,"DICE Parameter Space info:\n"); debugOutput(DEBUG_LEVEL_VERBOSE," Global : offset=%04X size=%04d\n", m_global_reg_offset, m_global_reg_size); debugOutput(DEBUG_LEVEL_VERBOSE," TX : offset=%04X size=%04d\n", m_tx_reg_offset, m_tx_reg_size); debugOutput(DEBUG_LEVEL_VERBOSE," nb=%4d size=%04d\n", m_nb_tx, m_tx_size); debugOutput(DEBUG_LEVEL_VERBOSE," RX : offset=%04X size=%04d\n", m_rx_reg_offset, m_rx_reg_size); debugOutput(DEBUG_LEVEL_VERBOSE," nb=%4d size=%04d\n", m_nb_rx, m_rx_size); debugOutput(DEBUG_LEVEL_VERBOSE," UNUSED1 : offset=%04X size=%04d\n", m_unused1_reg_offset, m_unused1_reg_size); debugOutput(DEBUG_LEVEL_VERBOSE," UNUSED2 : offset=%04X size=%04d\n", m_unused2_reg_offset, m_unused2_reg_size); /* The DnR Axum only works with the following entry. There is a single * clock in the device that needs to be master. * This code is a hack, we should ideally support easier clock selection, * even in case of broken clock name vectors. */ if (FW_VENDORID_DNR == getConfigRom().getNodeVendorId()) { writeGlobalReg(DICE_REGISTER_GLOBAL_CLOCK_SELECT, (0x01 << 8) | 0x07); } return true; } bool Device::readReg(fb_nodeaddr_t offset, fb_quadlet_t *result) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Reading base register offset 0x%08" PRIX64 "\n", offset); if(offset >= DICE_INVALID_OFFSET) { debugError("invalid offset: 0x%016" PRIX64 "\n", offset); return false; } fb_nodeaddr_t addr = DICE_REGISTER_BASE + offset; fb_nodeid_t nodeId = getNodeId() | 0xFFC0; if(!get1394Service().read_quadlet( nodeId, addr, result ) ) { debugError("Could not read from node 0x%04X addr 0x%12" PRIX64 "\n", nodeId, addr); return false; } *result = CondSwapFromBus32(*result); debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Read result: 0x%08" PRIX32 "\n", *result); return true; } bool Device::writeReg(fb_nodeaddr_t offset, fb_quadlet_t data) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Writing base register offset 0x%08" PRIX64 ", data: 0x%08" PRIX32 "\n", offset, data); if(offset >= DICE_INVALID_OFFSET) { debugError("invalid offset: 0x%012" PRIX64 "\n", offset); return false; } fb_nodeaddr_t addr = DICE_REGISTER_BASE + offset; fb_nodeid_t nodeId = getNodeId() | 0xFFC0; if(!get1394Service().write_quadlet( nodeId, addr, CondSwapToBus32(data) ) ) { debugError("Could not write to node 0x%04X addr 0x%12" PRIX64 "\n", nodeId, addr); return false; } return true; } bool Device::readRegBlock(fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length) { debugOutput(DEBUG_LEVEL_VERBOSE, "Reading base register offset 0x%08" PRIX64 ", length %zd, to %p\n", offset, length, data); const int blocksize_quads = 512/4; if(offset >= DICE_INVALID_OFFSET) { debugError("invalid offset: 0x%012" PRIX64 "\n", offset); return false; } fb_nodeaddr_t addr = DICE_REGISTER_BASE + offset; fb_nodeid_t nodeId = getNodeId() | 0xFFC0; int quads_done = 0; // round to next full quadlet int length_quads = (length+3)/4; while(quads_done < length_quads) { fb_nodeaddr_t curr_addr = addr + quads_done*4; fb_quadlet_t *curr_data = data + quads_done; int quads_todo = length_quads - quads_done; debugOutput(DEBUG_LEVEL_VERBOSE, "reading addr: 0x%012" PRIX64 ", %d quads to %p\n", curr_addr, quads_todo, curr_data); if (quads_todo > blocksize_quads) { debugOutput(DEBUG_LEVEL_VERBOSE, "Truncating read from %d to %d quadlets\n", quads_todo, blocksize_quads); quads_todo = blocksize_quads; } #ifdef DEBUG if (quads_todo < 0) { debugError("BUG: quads_todo < 0: %d\n", quads_todo); } #endif if(!get1394Service().read( nodeId, curr_addr, quads_todo, curr_data ) ) { debugError("Could not read %d quadlets from node 0x%04X addr 0x%012" PRIX64 "\n", quads_todo, nodeId, curr_addr); return false; } quads_done += quads_todo; } byteSwapFromBus(data, length/4); return true; } bool Device::writeRegBlock(fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Writing base register offset 0x%08" PRIX64 ", length: %zd\n", offset, length); const int blocksize_quads = 512/4; if(offset >= DICE_INVALID_OFFSET) { debugError("invalid offset: 0x%012" PRIX64 "\n", offset); return false; } fb_quadlet_t data_out[length/4]; memcpy(data_out, data, length); byteSwapToBus(data_out, length/4); fb_nodeaddr_t addr = DICE_REGISTER_BASE + offset; fb_nodeid_t nodeId = getNodeId() | 0xFFC0; int quads_done = 0; int length_quads = (length+3)/4; while(quads_done < length_quads) { fb_nodeaddr_t curr_addr = addr + quads_done*4; fb_quadlet_t *curr_data = data_out + quads_done; int quads_todo = length_quads - quads_done; if (quads_todo > blocksize_quads) { debugOutput(DEBUG_LEVEL_VERBOSE, "Truncating write from %d to %d quadlets\n", quads_todo, blocksize_quads); quads_todo = blocksize_quads; } #ifdef DEBUG if (quads_todo < 0) { debugError("BUG: quads_todo < 0: %d\n", quads_todo); } #endif if(!get1394Service().write( nodeId, addr, quads_todo, curr_data ) ) { debugError("Could not write %d quadlets to node 0x%04X addr 0x%012" PRIX64 "\n", quads_todo, nodeId, curr_addr); return false; } quads_done += quads_todo; } return true; } bool Device::readGlobalReg(fb_nodeaddr_t offset, fb_quadlet_t *result) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Reading global register offset 0x%04" PRIX64 "\n", offset); fb_nodeaddr_t offset_gl = globalOffsetGen(offset, sizeof(fb_quadlet_t)); return readReg(m_global_reg_offset + offset_gl, result); } bool Device::writeGlobalReg(fb_nodeaddr_t offset, fb_quadlet_t data) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Writing global register offset 0x%08" PRIX64 ", data: 0x%08" PRIX32 "\n", offset, data); fb_nodeaddr_t offset_gl = globalOffsetGen(offset, sizeof(fb_quadlet_t)); return writeReg(m_global_reg_offset + offset_gl, data); } bool Device::readGlobalRegBlock(fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Reading global register block offset 0x%04" PRIX64 ", length %zd bytes\n", offset, length); fb_nodeaddr_t offset_gl = globalOffsetGen(offset, length); return readRegBlock(m_global_reg_offset + offset_gl, data, length); } bool Device::writeGlobalRegBlock(fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Writing global register block offset 0x%04" PRIX64 ", length: %zd bytes\n", offset, length); fb_nodeaddr_t offset_gl = globalOffsetGen(offset, length); return writeRegBlock(m_global_reg_offset + offset_gl, data, length); } fb_nodeaddr_t Device::globalOffsetGen(fb_nodeaddr_t offset, size_t length) { // registry offsets should always be smaller than 0x7FFFFFFF // because otherwise base + offset > 64bit if(m_global_reg_offset & 0x80000000) { debugError("register offset not initialized yet\n"); return DICE_INVALID_OFFSET; } // out-of-range check if(offset+length > m_global_reg_offset+m_global_reg_size) { debugError("register offset+length too large: 0x%04" PRIX64 "\n", offset + length); return DICE_INVALID_OFFSET; } return offset; } bool Device::readTxReg(unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t *result) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Reading tx %d register offset 0x%04" PRIX64 "\n", i, offset); fb_nodeaddr_t offset_tx = txOffsetGen(i, offset, sizeof(fb_quadlet_t)); return readReg(m_tx_reg_offset + offset_tx, result); } bool Device::writeTxReg(unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t data) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Writing tx %d register offset 0x%08" PRIX64 ", data: 0x%08" PRIX32 "\n", i, offset, data); fb_nodeaddr_t offset_tx=txOffsetGen(i, offset, sizeof(fb_quadlet_t)); return writeReg(m_tx_reg_offset + offset_tx, data); } bool Device::readTxRegBlock(unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Reading rx register block offset 0x%04" PRIX64 ", length: %zd bytes\n", offset, length); fb_nodeaddr_t offset_tx=txOffsetGen(i, offset, length); return readRegBlock(m_tx_reg_offset + offset_tx, data, length); } bool Device::writeTxRegBlock(unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Writing rx register block offset 0x%04" PRIX64 ", length: %zd bytes\n", offset, length); fb_nodeaddr_t offset_tx=txOffsetGen(i, offset, length); return writeRegBlock(m_tx_reg_offset + offset_tx, data, length); } fb_nodeaddr_t Device::txOffsetGen(unsigned int i, fb_nodeaddr_t offset, size_t length) { // registry offsets should always be smaller than 0x7FFFFFFF // because otherwise base + offset > 64bit if(m_tx_reg_offset & 0x80000000) { debugError("register offset not initialized yet\n"); return DICE_INVALID_OFFSET; } if(m_nb_tx & 0x80000000) { debugError("m_nb_tx not initialized yet\n"); return DICE_INVALID_OFFSET; } if(m_tx_size & 0x80000000) { debugError("m_tx_size not initialized yet\n"); return DICE_INVALID_OFFSET; } if(i >= m_nb_tx) { debugError("TX index out of range\n"); return DICE_INVALID_OFFSET; } fb_nodeaddr_t offset_tx = DICE_REGISTER_TX_PARAM(m_tx_size, i, offset); // out-of-range check if(offset_tx + length > m_tx_reg_offset+4+m_tx_reg_size*m_nb_tx) { debugError("register offset+length too large: 0x%04" PRIX64 "\n", offset_tx + length); return DICE_INVALID_OFFSET; } return offset_tx; } bool Device::readRxReg(unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t *result) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Reading rx %d register offset 0x%04" PRIX64 "\n", i, offset); fb_nodeaddr_t offset_rx=rxOffsetGen(i, offset, sizeof(fb_quadlet_t)); return readReg(m_rx_reg_offset + offset_rx, result); } bool Device::writeRxReg(unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t data) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Writing rx register offset 0x%08" PRIX64 ", data: 0x%08" PRIX32 "\n", offset, data); fb_nodeaddr_t offset_rx=rxOffsetGen(i, offset, sizeof(fb_quadlet_t)); return writeReg(m_rx_reg_offset + offset_rx, data); } bool Device::readRxRegBlock(unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Reading rx register block offset 0x%04" PRIX64 ", length: %zd bytes\n", offset, length); fb_nodeaddr_t offset_rx=rxOffsetGen(i, offset, length); return readRegBlock(m_rx_reg_offset + offset_rx, data, length); } bool Device::writeRxRegBlock(unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Writing rx register block offset 0x%04" PRIX64 ", length: %zd bytes\n", offset, length); fb_nodeaddr_t offset_rx=rxOffsetGen(i, offset, length); return writeRegBlock(m_rx_reg_offset + offset_rx, data, length); } fb_nodeaddr_t Device::rxOffsetGen(unsigned int i, fb_nodeaddr_t offset, size_t length) { // registry offsets should always be smaller than 0x7FFFFFFF // because otherwise base + offset > 64bit if(m_rx_reg_offset & 0x80000000) { debugError("register offset not initialized yet\n"); return DICE_INVALID_OFFSET; } if(m_nb_rx & 0x80000000) { debugError("m_nb_rx not initialized yet\n"); return DICE_INVALID_OFFSET; } if(m_rx_size & 0x80000000) { debugError("m_rx_size not initialized yet\n"); return DICE_INVALID_OFFSET; } if(i >= m_nb_rx) { debugError("RX index out of range\n"); return DICE_INVALID_OFFSET; } fb_nodeaddr_t offset_rx = DICE_REGISTER_RX_PARAM(m_rx_size, i, offset); // out-of-range check if(offset_rx + length > m_rx_reg_offset+4+m_rx_reg_size*m_nb_rx) { debugError("register offset+length too large: 0x%04" PRIX64 "\n", offset_rx + length); return DICE_INVALID_OFFSET; } return offset_rx; } // the notifier Device::Notifier::Notifier(Device &d, nodeaddr_t start) : ARMHandler(d.get1394Service(), start, DICE_NOTIFIER_BLOCK_LENGTH, RAW1394_ARM_READ | RAW1394_ARM_WRITE | RAW1394_ARM_LOCK, RAW1394_ARM_WRITE, 0) , m_device(d) { // switch over the debug module to that of this device instead of the 1394 service m_debugModule = d.m_debugModule; } Device::Notifier::~Notifier() { } } libffado-2.4.5/src/dice/dice_avdevice.h0000644000175000001440000002033314206145246017303 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DICEDEVICE_H #define DICEDEVICE_H #include "dice_firmware_loader.h" #include "ffadotypes.h" #include "ffadodevice.h" #include "debugmodule/debugmodule.h" #include "libavc/avc_definitions.h" #include "libstreaming/amdtp/AmdtpReceiveStreamProcessor.h" #include "libstreaming/amdtp/AmdtpTransmitStreamProcessor.h" #include "libstreaming/amdtp/AmdtpPort.h" #include "libieee1394/ieee1394service.h" #include "libcontrol/Element.h" #include "libcontrol/MatrixMixer.h" #include "libcontrol/CrossbarRouter.h" #include #include class ConfigRom; class Ieee1394Service; namespace Util { class Configuration; } namespace Dice { class EAP; /** @brief Devices based on the DICE-platform This class is the basic implementation for devices using the DICE-chip. */ class Device : public FFADODevice { private: friend class EAP; public: /// constructor Device( DeviceManager& d, ffado_smartptr( configRom )); /// destructor ~Device(); static bool probe( Util::Configuration& c, ConfigRom& configRom, bool generic = false ); static FFADODevice * createDevice( DeviceManager& d, ffado_smartptr( configRom )); virtual bool discover(); static int getConfigurationId( ); virtual void showDevice(); bool canChangeNickname() { return true; } virtual bool deleteImgFL(const char*, bool v = true); virtual bool flashDiceFL(const char*, const char* image = "dice"); virtual bool dumpFirmwareFL(const char*); virtual bool showDiceInfoFL(); virtual bool showImgInfoFL(); virtual bool testDiceFL(int); virtual DICE_FL_INFO_PARAM* showFlashInfoFL(bool v = true); virtual bool showAppInfoFL(); virtual bool onSamplerateChange( int oldSamplingFrequency ); virtual bool setSamplingFrequency( int samplingFrequency ); virtual int getSamplingFrequency( ); virtual std::vector getSupportedSamplingFrequencies(); virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); virtual int getStreamCount(); virtual Streaming::StreamProcessor *getStreamProcessorByIndex(int i); virtual enum FFADODevice::eStreamingState getStreamingState(); virtual bool prepare(); virtual bool lock(); virtual bool unlock(); virtual bool startStreamByIndex(int i); virtual bool stopStreamByIndex(int i); virtual bool enableStreaming(); virtual bool disableStreaming(); virtual std::string getNickname(); virtual bool setNickname(std::string name); protected: // streaming stuff typedef std::vector< Streaming::StreamProcessor * > StreamProcessorVector; typedef std::vector< Streaming::StreamProcessor * >::iterator StreamProcessorVectorIterator; StreamProcessorVector m_receiveProcessors; StreamProcessorVector m_transmitProcessors; private: // streaming & port helpers enum EPortTypes { ePT_Analog, ePT_MIDI, }; typedef struct { std::string name; enum EPortTypes portType; unsigned int streamPosition; unsigned int streamLocation; } diceChannelInfo; bool addChannelToProcessor( diceChannelInfo *, Streaming::StreamProcessor *, Streaming::Port::E_Direction direction); int allocateIsoChannel(unsigned int packet_size); bool deallocateIsoChannel(int channel); private: // active config enum eDiceConfig { eDC_Unknown, eDC_Low, eDC_Mid, eDC_High, }; enum eDiceConfig getCurrentConfig(); private: // helper functions bool enableIsoStreaming(); bool disableIsoStreaming(); bool isIsoStreamingEnabled(); bool maskedCheckZeroGlobalReg(fb_nodeaddr_t offset, fb_quadlet_t mask); bool maskedCheckNotZeroGlobalReg(fb_nodeaddr_t offset, fb_quadlet_t mask); stringlist splitNameString(std::string in); stringlist getTxNameString(unsigned int i); stringlist getRxNameString(unsigned int i); stringlist getCptrNameString(unsigned int); stringlist getPbckNameString(unsigned int); stringlist getClockSourceNameString(); enum eClockSourceType clockIdToType(unsigned int id); bool isClockSourceIdLocked(unsigned int id, quadlet_t ext_status_reg); bool isClockSourceIdSlipping(unsigned int id, quadlet_t ext_status_reg); // EAP stuff private: EAP* m_eap; protected: virtual EAP* createEAP(); public: EAP* getEAP() {return m_eap;}; private: // register I/O routines bool initIoFunctions(); // functions used for RX/TX abstraction bool startstopStreamByIndex(int i, const bool start); bool prepareSP (unsigned int, const Streaming::Port::E_Direction direction_requested); void setRXTXfuncs (const Streaming::Port::E_Direction direction); // quadlet read/write routines bool readReg(fb_nodeaddr_t, fb_quadlet_t *); bool writeReg(fb_nodeaddr_t, fb_quadlet_t); bool readRegBlock(fb_nodeaddr_t, fb_quadlet_t *, size_t); bool writeRegBlock(fb_nodeaddr_t, fb_quadlet_t *, size_t); bool readGlobalReg(fb_nodeaddr_t, fb_quadlet_t *); bool writeGlobalReg(fb_nodeaddr_t, fb_quadlet_t); bool readGlobalRegBlock(fb_nodeaddr_t, fb_quadlet_t *, size_t); bool writeGlobalRegBlock(fb_nodeaddr_t, fb_quadlet_t *, size_t); fb_nodeaddr_t globalOffsetGen(fb_nodeaddr_t, size_t); bool readTxReg(unsigned int i, fb_nodeaddr_t, fb_quadlet_t *); bool writeTxReg(unsigned int i, fb_nodeaddr_t, fb_quadlet_t); bool readTxRegBlock(unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length); bool writeTxRegBlock(unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length); fb_nodeaddr_t txOffsetGen(unsigned int, fb_nodeaddr_t, size_t); bool readRxReg(unsigned int i, fb_nodeaddr_t, fb_quadlet_t *); bool writeRxReg(unsigned int i, fb_nodeaddr_t, fb_quadlet_t); bool readRxRegBlock(unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length); bool writeRxRegBlock(unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t *data, size_t length); fb_nodeaddr_t rxOffsetGen(unsigned int, fb_nodeaddr_t, size_t); fb_quadlet_t m_global_reg_offset; fb_quadlet_t m_global_reg_size; fb_quadlet_t m_tx_reg_offset; fb_quadlet_t m_tx_reg_size; fb_quadlet_t m_rx_reg_offset; fb_quadlet_t m_rx_reg_size; fb_quadlet_t m_unused1_reg_offset; fb_quadlet_t m_unused1_reg_size; fb_quadlet_t m_unused2_reg_offset; fb_quadlet_t m_unused2_reg_size; fb_quadlet_t m_nb_tx; fb_quadlet_t m_tx_size; fb_quadlet_t m_nb_rx; fb_quadlet_t m_rx_size; fb_quadlet_t audio_base_register; fb_quadlet_t midi_base_register; char dir[3]; // Function pointers to call readTxReg/readRxReg or writeTxReg/writeRxReg respectively bool (Device::*writeFunc) (unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t data); bool (Device::*readFunc) (unsigned int i, fb_nodeaddr_t offset, fb_quadlet_t *result); // private: public: /** * this class reacts on the DICE device writing to the * hosts notify address */ #define DICE_NOTIFIER_BASE_ADDRESS 0x0000FFFFE0000000ULL #define DICE_NOTIFIER_BLOCK_LENGTH 4 class Notifier : public Ieee1394Service::ARMHandler { public: Notifier(Device &, nodeaddr_t start); virtual ~Notifier(); private: Device &m_device; }; // notification Notifier *m_notifier; }; } #endif libffado-2.4.5/src/dice/dice_defines.h0000644000175000001440000002703214206145246017135 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DICEDEFINES_H #define DICEDEFINES_H // #define DICE_VER_1_0_4_0 #define DICE_VER_1_0_7_0 #define DICE_INVALID_OFFSET 0xFFFFF00000000000ULL /* * This header is based upon the DICE II driver specification * version 1.0.7.0 and: * dicedriverExtStatus.h,v 1.2 2006/09/27 20:35:45 * dicedriverInterface.h,v 1.1.1.1 2006/08/10 20:00:57 * */ // Register addresses & offsets // DICE_PRIVATE_SPACE registers #define DICE_REGISTER_BASE 0x0000FFFFE0000000ULL #define DICE_REGISTER_GLOBAL_PAR_SPACE_OFF 0x0000 #define DICE_REGISTER_GLOBAL_PAR_SPACE_SZ 0x0004 #define DICE_REGISTER_TX_PAR_SPACE_OFF 0x0008 #define DICE_REGISTER_TX_PAR_SPACE_SZ 0x000C #define DICE_REGISTER_RX_PAR_SPACE_OFF 0x0010 #define DICE_REGISTER_RX_PAR_SPACE_SZ 0x0014 #define DICE_REGISTER_UNUSED1_SPACE_OFF 0x0018 #define DICE_REGISTER_UNUSED1_SPACE_SZ 0x001C #define DICE_REGISTER_UNUSED2_SPACE_OFF 0x0020 #define DICE_REGISTER_UNUSED2_SPACE_SZ 0x0024 // GLOBAL_PAR_SPACE registers #define DICE_REGISTER_GLOBAL_OWNER 0x0000 #define DICE_REGISTER_GLOBAL_NOTIFICATION 0x0008 #define DICE_REGISTER_GLOBAL_NICK_NAME 0x000C #define DICE_REGISTER_GLOBAL_CLOCK_SELECT 0x004C #define DICE_REGISTER_GLOBAL_ENABLE 0x0050 #define DICE_REGISTER_GLOBAL_STATUS 0x0054 #define DICE_REGISTER_GLOBAL_EXTENDED_STATUS 0x0058 #define DICE_REGISTER_GLOBAL_SAMPLE_RATE 0x005C #define DICE_REGISTER_GLOBAL_VERSION 0x0060 #define DICE_REGISTER_GLOBAL_CLOCKCAPABILITIES 0x0064 #define DICE_REGISTER_GLOBAL_CLOCKSOURCENAMES 0x0068 // TX_PAR_SPACE registers #define DICE_REGISTER_TX_NB_TX 0x0000 #define DICE_REGISTER_TX_SZ_TX 0x0004 #define DICE_REGISTER_TX_ISOC_BASE 0x0008 #define DICE_REGISTER_TX_NB_AUDIO_BASE 0x000C #define DICE_REGISTER_TX_MIDI_BASE 0x0010 #define DICE_REGISTER_TX_SPEED_BASE 0x0014 #define DICE_REGISTER_TX_NAMES_BASE 0x0018 #define DICE_REGISTER_TX_AC3_CAPABILITIES_BASE 0x0118 #define DICE_REGISTER_TX_AC3_ENABLE_BASE 0x011C #define DICE_REGISTER_TX_PARAM(size, i, offset) \ ( ((i) * (size) ) + (offset) ) // RX_PAR_SPACE registers #define DICE_REGISTER_RX_NB_RX 0x0000 #define DICE_REGISTER_RX_SZ_RX 0x0004 #ifdef DICE_VER_1_0_4_0 #define DICE_REGISTER_RX_ISOC_BASE 0x0008 #define DICE_REGISTER_RX_SEQ_START_BASE 0x0014 #define DICE_REGISTER_RX_NB_AUDIO_BASE 0x000C #define DICE_REGISTER_RX_MIDI_BASE 0x0010 #define DICE_REGISTER_RX_NAMES_BASE 0x0018 #define DICE_REGISTER_RX_AC3_CAPABILITIES_BASE 0x0118 #define DICE_REGISTER_RX_AC3_ENABLE_BASE 0x011C #endif #ifdef DICE_VER_1_0_7_0 #define DICE_REGISTER_RX_ISOC_BASE 0x0008 #define DICE_REGISTER_RX_SEQ_START_BASE 0x000C #define DICE_REGISTER_RX_NB_AUDIO_BASE 0x0010 #define DICE_REGISTER_RX_MIDI_BASE 0x0014 #define DICE_REGISTER_RX_NAMES_BASE 0x0018 #define DICE_REGISTER_RX_AC3_CAPABILITIES_BASE 0x0118 #define DICE_REGISTER_RX_AC3_ENABLE_BASE 0x011C #endif #define DICE_REGISTER_RX_PARAM(size, i, offset) \ ( ((i) * (size) ) + (offset) ) // Register Bitfields // GLOBAL_PAR_SPACE registers // OWNER register defines #define DICE_OWNER_NO_OWNER 0xFFFF000000000000LLU // NOTIFICATION register defines #define DICE_NOTIFY_RX_CFG_CHG_BIT (1UL << 0) #define DICE_NOTIFY_TX_CFG_CHG_BIT (1UL << 1) #define DICE_NOTIFY_DUP_ISOC_BIT (1UL << 2) #define DICE_NOTIFY_BW_ERR_BIT (1UL << 3) #define DICE_NOTIFY_LOCK_CHG_BIT (1UL << 4) #define DICE_NOTIFY_CLOCK_ACCEPTED (1UL << 5) // bits 6..15 are RESERVED // FIXME: // diceDriverInterface.h defines the following bitfield // that is undocumented by spec 1.0.7.0 #define DICE_INTERFACE_CHG_BIT (1UL << 6) // FIXME: // The spec 1.0.7.0 defines these as USER notifications // however diceDriverInterface.h defines these as // 'reserved bits for future system wide use'. #define DICE_NOTIFY_RESERVED1 (1UL << 16) #define DICE_NOTIFY_RESERVED2 (1UL << 17) #define DICE_NOTIFY_RESERVED3 (1UL << 18) #define DICE_NOTIFY_RESERVED4 (1UL << 19) // FIXME: // The spec 1.0.7.0 does not specify anything about // the format of the user messages // however diceDriverInterface.h indicates: // "When DD_NOTIFY_MESSAGE is set DD_NOTIFY_USER4 through // DD_NOTIFY_USER11 are defined as an 8 bit message so // you can have 256 seperate messages (like gray encoder // movements)." #define DICE_NOTIFY_MESSAGE (1UL << 20) #define DICE_NOTIFY_USER1 (1UL << 21) #define DICE_NOTIFY_USER2 (1UL << 22) #define DICE_NOTIFY_USER3 (1UL << 23) #define DICE_NOTIFY_USER4 (1UL << 24) #define DICE_NOTIFY_USER5 (1UL << 25) #define DICE_NOTIFY_USER6 (1UL << 26) #define DICE_NOTIFY_USER7 (1UL << 27) #define DICE_NOTIFY_USER8 (1UL << 28) #define DICE_NOTIFY_USER9 (1UL << 29) #define DICE_NOTIFY_USER10 (1UL << 30) #define DICE_NOTIFY_USER11 (1UL << 31) #define DICE_NOTIFY_USER_IS_MESSAGE(x) \ ( ((x) & DICE_NOTIFY_MESSAGE) != 0 ) #define DICE_NOTIFY_USER_GET_MESSAGE(x) \ ( ((x) >> 24 ) & 0xFF ) // NICK_NAME register // NOTE: in bytes #define DICE_NICK_NAME_SIZE 64 // CLOCK_SELECT register // Clock sources supported #define DICE_CLOCKSOURCE_AES1 0x00 #define DICE_CLOCKSOURCE_AES2 0x01 #define DICE_CLOCKSOURCE_AES3 0x02 #define DICE_CLOCKSOURCE_AES4 0x03 #define DICE_CLOCKSOURCE_AES_ANY 0x04 #define DICE_CLOCKSOURCE_ADAT 0x05 #define DICE_CLOCKSOURCE_TDIF 0x06 #define DICE_CLOCKSOURCE_WC 0x07 #define DICE_CLOCKSOURCE_ARX1 0x08 #define DICE_CLOCKSOURCE_ARX2 0x09 #define DICE_CLOCKSOURCE_ARX3 0x0A #define DICE_CLOCKSOURCE_ARX4 0x0B #define DICE_CLOCKSOURCE_INTERNAL 0x0C #define DICE_CLOCKSOURCE_COUNT (DICE_CLOCKSOURCE_INTERNAL+1) #define DICE_CLOCKSOURCE_MASK 0x0000FFFFLU #define DICE_GET_CLOCKSOURCE(reg) (((reg) & DICE_CLOCKSOURCE_MASK)) #define DICE_SET_CLOCKSOURCE(reg,clk) (((reg) & ~DICE_CLOCKSOURCE_MASK) | ((clk) & DICE_CLOCKSOURCE_MASK)) // Supported rates #define DICE_RATE_32K 0x00 #define DICE_RATE_44K1 0x01 #define DICE_RATE_48K 0x02 #define DICE_RATE_88K2 0x03 #define DICE_RATE_96K 0x04 #define DICE_RATE_176K4 0x05 #define DICE_RATE_192K 0x06 #define DICE_RATE_ANY_LOW 0x07 #define DICE_RATE_ANY_MID 0x08 #define DICE_RATE_ANY_HIGH 0x09 #define DICE_RATE_NONE 0x0A #define DICE_RATE_MASK 0x0000FF00LU #define DICE_GET_RATE(reg) (((reg) & DICE_RATE_MASK) >> 8) #define DICE_SET_RATE(reg,rate) (((reg) & ~DICE_RATE_MASK) | (((rate) << 8) & DICE_RATE_MASK) ) // ENABLE register #define DICE_ISOSTREAMING_ENABLE (1UL << 0) #define DICE_ISOSTREAMING_DISABLE (0) // CLOCK_STATUS register #define DICE_STATUS_SOURCE_LOCKED (1UL << 0) #define DICE_STATUS_RATE_CONFLICT (1UL << 1) #define DICE_STATUS_GET_NOMINAL_RATE(x) ( ((x) >> 8 ) & 0xFF ) // EXTENDED_STATUS register #define DICE_EXT_STATUS_AES0_LOCKED (1UL << 0) #define DICE_EXT_STATUS_AES1_LOCKED (1UL << 1) #define DICE_EXT_STATUS_AES2_LOCKED (1UL << 2) #define DICE_EXT_STATUS_AES3_LOCKED (1UL << 3) #define DICE_EXT_STATUS_AES_ANY_LOCKED ( 0x0F) #define DICE_EXT_STATUS_ADAT_LOCKED (1UL << 4) #define DICE_EXT_STATUS_TDIF_LOCKED (1UL << 5) #define DICE_EXT_STATUS_ARX1_LOCKED (1UL << 6) #define DICE_EXT_STATUS_ARX2_LOCKED (1UL << 7) #define DICE_EXT_STATUS_ARX3_LOCKED (1UL << 8) #define DICE_EXT_STATUS_ARX4_LOCKED (1UL << 9) // FIXME: this one is missing in dicedriverExtStatus.h #define DICE_EXT_STATUS_WC_LOCKED (1UL << 10) #define DICE_EXT_STATUS_AES0_SLIP (1UL << 16) #define DICE_EXT_STATUS_AES1_SLIP (1UL << 17) #define DICE_EXT_STATUS_AES2_SLIP (1UL << 18) #define DICE_EXT_STATUS_AES3_SLIP (1UL << 19) #define DICE_EXT_STATUS_ADAT_SLIP (1UL << 20) #define DICE_EXT_STATUS_TDIF_SLIP (1UL << 21) #define DICE_EXT_STATUS_ARX1_SLIP (1UL << 22) #define DICE_EXT_STATUS_ARX2_SLIP (1UL << 23) #define DICE_EXT_STATUS_ARX3_SLIP (1UL << 24) #define DICE_EXT_STATUS_ARX4_SLIP (1UL << 25) // FIXME: does this even exist? #define DICE_EXT_STATUS_WC_SLIP (1UL << 26) // SAMPLE_RATE register // nothing here // VERSION register #define DICE_DRIVER_SPEC_VERSION_NUMBER_GET(x,y) \ ( ( (x) >> (y)) & 0xFF ) #define DICE_DRIVER_SPEC_VERSION_NUMBER_GET_A(x) \ DICE_DRIVER_SPEC_VERSION_NUMBER_GET(x,24) #define DICE_DRIVER_SPEC_VERSION_NUMBER_GET_B(x) \ DICE_DRIVER_SPEC_VERSION_NUMBER_GET(x,16) #define DICE_DRIVER_SPEC_VERSION_NUMBER_GET_C(x) \ DICE_DRIVER_SPEC_VERSION_NUMBER_GET(x,8) #define DICE_DRIVER_SPEC_VERSION_NUMBER_GET_D(x) \ DICE_DRIVER_SPEC_VERSION_NUMBER_GET(x,0) // CLOCKCAPABILITIES register #define DICE_CLOCKCAP_RATE_32K (1UL << 0) #define DICE_CLOCKCAP_RATE_44K1 (1UL << 1) #define DICE_CLOCKCAP_RATE_48K (1UL << 2) #define DICE_CLOCKCAP_RATE_88K2 (1UL << 3) #define DICE_CLOCKCAP_RATE_96K (1UL << 4) #define DICE_CLOCKCAP_RATE_176K4 (1UL << 5) #define DICE_CLOCKCAP_RATE_192K (1UL << 6) #define DICE_CLOCKCAP_SOURCE_AES1 (1UL << 16) #define DICE_CLOCKCAP_SOURCE_AES2 (1UL << 17) #define DICE_CLOCKCAP_SOURCE_AES3 (1UL << 18) #define DICE_CLOCKCAP_SOURCE_AES4 (1UL << 19) #define DICE_CLOCKCAP_SOURCE_AES_ANY (1UL << 20) #define DICE_CLOCKCAP_SOURCE_ADAT (1UL << 21) #define DICE_CLOCKCAP_SOURCE_TDIF (1UL << 22) #define DICE_CLOCKCAP_SOURCE_WORDCLOCK (1UL << 23) #define DICE_CLOCKCAP_SOURCE_ARX1 (1UL << 24) #define DICE_CLOCKCAP_SOURCE_ARX2 (1UL << 25) #define DICE_CLOCKCAP_SOURCE_ARX3 (1UL << 26) #define DICE_CLOCKCAP_SOURCE_ARX4 (1UL << 27) #define DICE_CLOCKCAP_SOURCE_INTERNAL (1UL << 28) // CLOCKSOURCENAMES // note: in bytes #define DICE_CLOCKSOURCENAMES_SIZE 256 // TX_PAR_SPACE registers // note: in bytes #define DICE_TX_NAMES_SIZE 256 // RX_PAR_SPACE registers // note: in bytes #define DICE_RX_NAMES_SIZE 256 #endif // DICEDEFINES_H libffado-2.4.5/src/dice/dice_eap.cpp0000644000175000001440000021023614206145246016620 0ustar jwoitheusers/* * Copyright (C) 2005-2009 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "dice_avdevice.h" #include "dice_eap.h" #include "dice_defines.h" #include "libutil/SystemTimeSource.h" #include "libutil/ByteSwap.h" #include namespace Dice { // ----------- helper functions ------------- #if 0 static const char * srcBlockToString(const char id) { switch(id) { case eRS_AES: return "AES "; case eRS_ADAT: return "ADAT"; case eRS_Mixer: return "MXR "; case eRS_InS0: return "INS0"; case eRS_InS1: return "INS1"; case eRS_ARM: return "ARM "; case eRS_ARX0: return "AVS0"; case eRS_ARX1: return "AVS1"; case eRS_Muted: return "MUTE"; default : return "RSVD"; } } static const char * dstBlockToString(const char id) { switch(id) { case eRD_AES: return "AES "; case eRD_ADAT: return "ADAT"; case eRD_Mixer0: return "MXR0"; case eRD_Mixer1: return "MXR1"; case eRD_InS0: return "INS0"; case eRD_InS1: return "INS1"; case eRD_ARM: return "ARM "; case eRD_ATX0: return "AVS0"; case eRD_ATX1: return "AVS1"; case eRD_Muted: return "MUTE"; default : return "RSVD"; } } #endif IMPL_DEBUG_MODULE( EAP, EAP, DEBUG_LEVEL_NORMAL ); EAP::EAP(Device &d) : Control::Container(&d, "EAP") , m_device(d) , m_mixer( NULL ) , m_router( NULL ) , m_standalone( NULL ) , m_current_cfg_routing_low ( RouterConfig(*this, eRT_CurrentCfg, DICE_EAP_CURRCFG_LOW_ROUTER ) ) , m_current_cfg_routing_mid ( RouterConfig(*this, eRT_CurrentCfg, DICE_EAP_CURRCFG_MID_ROUTER ) ) , m_current_cfg_routing_high( RouterConfig(*this, eRT_CurrentCfg, DICE_EAP_CURRCFG_HIGH_ROUTER) ) , m_current_cfg_stream_low ( StreamConfig(*this, eRT_CurrentCfg, DICE_EAP_CURRCFG_LOW_STREAM ) ) , m_current_cfg_stream_mid ( StreamConfig(*this, eRT_CurrentCfg, DICE_EAP_CURRCFG_MID_STREAM ) ) , m_current_cfg_stream_high ( StreamConfig(*this, eRT_CurrentCfg, DICE_EAP_CURRCFG_HIGH_STREAM) ) { } EAP::~EAP() { // remove all control elements registered to this device (w/o free) clearElements(false); // delete the helper classes if(m_mixer) delete m_mixer; if(m_router) delete m_router; if(m_standalone) delete m_standalone; } // offsets and sizes are returned in quadlets, but we use byte values, hence the *= 4 #define DICE_EAP_READREG_AND_CHECK(base, addr, var) { \ if(!readReg(base, addr, &var)) { \ debugError("Could not initialize " #var "\n"); \ return false; \ } \ var *= 4; \ } bool EAP::init() { if(!supportsEAP(m_device)) { debugWarning("no EAP mixer (device does not support EAP)\n"); return false; } // offsets and sizes are returned in quadlets, but we use byte values DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_CAPABILITY_SPACE_OFF, m_capability_offset); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_CAPABILITY_SPACE_SZ, m_capability_size); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_CMD_SPACE_OFF, m_cmd_offset); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_CMD_SPACE_SZ, m_cmd_size); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_MIXER_SPACE_OFF, m_mixer_offset); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_MIXER_SPACE_SZ, m_mixer_size); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_PEAK_SPACE_OFF, m_peak_offset); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_PEAK_SPACE_SZ, m_peak_size); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_NEW_ROUTING_SPACE_OFF, m_new_routing_offset); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_NEW_ROUTING_SPACE_SZ, m_new_routing_size); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_NEW_STREAM_CFG_SPACE_OFF, m_new_stream_cfg_offset); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_NEW_STREAM_CFG_SPACE_SZ, m_new_stream_cfg_size); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_CURR_CFG_SPACE_OFF, m_curr_cfg_offset); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_CURR_CFG_SPACE_SZ, m_curr_cfg_size); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_STAND_ALONE_CFG_SPACE_OFF, m_standalone_offset); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_STAND_ALONE_CFG_SPACE_SZ, m_standalone_size); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_APP_SPACE_OFF, m_app_offset); DICE_EAP_READREG_AND_CHECK(eRT_Base, DICE_EAP_APP_SPACE_SZ, m_app_size); // initialize the capability info quadlet_t tmp; if(!readReg(eRT_Capability, DICE_EAP_CAPABILITY_ROUTER, &tmp)) { debugError("Could not read router capabilities\n"); return false; } m_router_exposed = (tmp >> DICE_EAP_CAP_ROUTER_EXPOSED) & 0x01; m_router_readonly = (tmp >> DICE_EAP_CAP_ROUTER_READONLY) & 0x01; m_router_flashstored = (tmp >> DICE_EAP_CAP_ROUTER_FLASHSTORED) & 0x01; m_router_nb_entries = (tmp >> DICE_EAP_CAP_ROUTER_MAXROUTES) & 0xFFFF; if(!readReg(eRT_Capability, DICE_EAP_CAPABILITY_MIXER, &tmp)) { debugError("Could not read mixer capabilities\n"); return false; } m_mixer_exposed = (tmp >> DICE_EAP_CAP_MIXER_EXPOSED) & 0x01; m_mixer_readonly = (tmp >> DICE_EAP_CAP_MIXER_READONLY) & 0x01; m_mixer_flashstored = (tmp >> DICE_EAP_CAP_MIXER_FLASHSTORED) & 0x01; m_mixer_tx_id = (tmp >> DICE_EAP_CAP_MIXER_IN_DEV) & 0x000F; m_mixer_rx_id = (tmp >> DICE_EAP_CAP_MIXER_OUT_DEV) & 0x000F; m_mixer_nb_tx = (tmp >> DICE_EAP_CAP_MIXER_INPUTS) & 0x00FF; m_mixer_nb_rx = (tmp >> DICE_EAP_CAP_MIXER_OUTPUTS) & 0x00FF; if(!readReg(eRT_Capability, DICE_EAP_CAPABILITY_GENERAL, &tmp)) { debugError("Could not read general capabilities\n"); return false; } m_general_support_dynstream = (tmp >> DICE_EAP_CAP_GENERAL_STRM_CFG_EN) & 0x01; m_general_support_flash = (tmp >> DICE_EAP_CAP_GENERAL_FLASH_EN) & 0x01; m_general_peak_enabled = (tmp >> DICE_EAP_CAP_GENERAL_PEAK_EN) & 0x01; m_general_max_tx = (tmp >> DICE_EAP_CAP_GENERAL_MAX_TX_STREAM) & 0x0F; m_general_max_rx = (tmp >> DICE_EAP_CAP_GENERAL_MAX_RX_STREAM) & 0x0F; m_general_stream_cfg_stored = (tmp >> DICE_EAP_CAP_GENERAL_STRM_CFG_FLS) & 0x01; m_general_chip = (tmp >> DICE_EAP_CAP_GENERAL_CHIP) & 0xFFFF; // update our view on the current configuration if(!updateConfigurationCache()) { debugError("Could not initialize configuration cache\n"); return false; } // initialize the helper classes if (m_mixer_exposed) { // initialize the mixer m_mixer = new EAP::Mixer(*this); if(m_mixer == NULL) { debugError("Could not allocate memory for mixer\n"); return false; } if(!m_mixer->init()) { debugError("Could not initialize mixer\n"); delete m_mixer; m_mixer = NULL; return false; } // add the mixer to the EAP control container if(!addElement(m_mixer)) { debugWarning("Failed to add mixer to control tree\n"); } // initialize the peak meter m_router = new EAP::Router(*this); if(m_router == NULL) { debugError("Could not allocate memory for router\n"); return false; } m_router->update(); // add the router to the EAP control container if(!addElement(m_router)) { debugWarning("Failed to add router to control tree\n"); } m_standalone = new EAP::StandaloneConfig(*this); if(m_standalone == NULL) { debugError("Could not allocate memory for standalone config\n"); return false; } } return true; } void EAP::update() { // update EAP from the last init // update router sources and destinations if (m_router) { m_router->update(); } } void EAP::setupSources() { // define router sources (possibly depending on the samplerate) switch(m_device.getCurrentConfig()) { case Device::eDC_Low: setupSources_low(); return; case Device::eDC_Mid: setupSources_mid(); return; case Device::eDC_High: setupSources_high(); return; default: debugError("Unsupported configuration mode\n"); return; } } void EAP::setupSources_low() { // add the routing sources for a DICE chip switch(m_general_chip) { case DICE_EAP_CAP_GENERAL_CHIP_DICEII: // router/EAP currently not supported break; case DICE_EAP_CAP_GENERAL_CHIP_DICEJR: // second audio port (unique to the junior) addSource("InS1", 0, 8, eRS_InS1, 1); case DICE_EAP_CAP_GENERAL_CHIP_DICEMINI: /// these are common to the mini and junior // the AES receiver addSource("AES", 0, 8, eRS_AES, 1); // the ADAT receiver addSource("ADAT", 0, 8, eRS_ADAT, 1); // the Mixer outputs addSource("MixerOut", 0, 16, eRS_Mixer, 1); // the first audio port addSource("InS0", 0, 8, eRS_InS0, 1); // the ARM audio port addSource("ARM", 0, 8, eRS_ARM, 1); // the 1394 stream receivers addSource("1394_0", 0, 16, eRS_ARX0, 1); addSource("1394_1", 0, 16, eRS_ARX1, 1); // mute addSource("Mute", 0, 1, eRS_Muted); break; default: // this is an unsupported chip break; } } void EAP::setupSources_mid() { setupSources_low(); } void EAP::setupSources_high() { setupSources_low(); } unsigned int EAP::getSMuteId() { return m_router->getSourceIndex("Mute:00"); } void EAP::setupDestinations() { switch(m_device.getCurrentConfig()) { case Device::eDC_Low: setupDestinations_low(); return; case Device::eDC_Mid: setupDestinations_mid(); return; case Device::eDC_High: setupDestinations_high(); return; default: debugError("Unsupported configuration mode\n"); return; } } void EAP::setupDestinations_low() { // add the routing destinations for a DICE chip switch(m_general_chip) { case DICE_EAP_CAP_GENERAL_CHIP_DICEII: // router/EAP currently not supported break; case DICE_EAP_CAP_GENERAL_CHIP_DICEJR: // second audio port (unique to the junior) addDestination("InS1", 0, 8, eRD_InS1, 1); case DICE_EAP_CAP_GENERAL_CHIP_DICEMINI: /// these are common to the mini and junior // the AES receiver addDestination("AES", 0, 8, eRD_AES, 1); // the ADAT receiver addDestination("ADAT", 0, 8, eRD_ADAT, 1); // the Mixer outputs addDestination("MixerIn", 0, 16, eRD_Mixer0, 1); addDestination("MixerIn", 0, 2, eRD_Mixer1, 17); // the first audio port addDestination("InS0", 0, 8, eRD_InS0, 1); // the ARM audio port addDestination("ARM", 0, 8, eRD_ARM, 1); // the 1394 stream receivers addDestination("1394_0", 0, 16, eRD_ATX0, 1); addDestination("1394_1", 0, 16, eRD_ATX1, 1); // mute addDestination("Mute", 0, 1, eRD_Muted, 1); break; default: // this is an unsupported chip break; } } void EAP::setupDestinations_mid() { setupDestinations_low(); } void EAP::setupDestinations_high() { setupDestinations_low(); } void EAP::addSource(const std::string name, unsigned int base, unsigned int count, enum eRouteSource srcid, unsigned int offset) { m_router->addSource(name, srcid, base, count, offset); } void EAP::addDestination(const std::string name, unsigned int base, unsigned int count, enum eRouteDestination destid, unsigned int offset) { m_router->addDestination(name, destid, base, count, offset); } bool EAP::updateConfigurationCache() { if(!m_current_cfg_routing_low.read()) { debugError("Could not initialize current routing configuration (low rates)\n"); return false; } if(!m_current_cfg_routing_mid.read()) { debugError("Could not initialize current routing configuration (mid rates)\n"); return false; } if(!m_current_cfg_routing_high.read()) { debugError("Could not initialize current routing configuration (high rates)\n"); return false; } if(!m_current_cfg_stream_low.read()) { debugError("Could not initialize current stream configuration (low rates)\n"); return false; } if(!m_current_cfg_stream_mid.read()) { debugError("Could not initialize current stream configuration (mid rates)\n"); return false; } if(!m_current_cfg_stream_high.read()) { debugError("Could not initialize current stream configuration (high rates)\n"); return false; } if(m_mixer) m_mixer->updateNameCache(); return true; } // Get capture and playback names // If the device has a router, capture and playback are destinations and sources, respectively, // as defined above // If no router is found, transmitters and receivers names are returned stringlist EAP::getCptrNameString(unsigned int i) { std::vector destid; unsigned int destid_0; stringlist cptr_names; std::string dest_name, src_name; if (m_router) { switch (i) { case 0: destid_0 = (eRD_ATX0<<4); break; case 1: destid_0 = (eRD_ATX1<<4); break; // Only 2 transmitter possible (?) default: return cptr_names; } // At most 16 destinations per eRD for (int j=0; j<16; j++) { destid.push_back(destid_0+j); } for (unsigned it=0; itgetDestinationName(destid.at(it)); if (dest_name.size() > 0) { // get a source possibly routed to the destination (only one source per destination) src_name = m_router->getSourceForDestination(dest_name); if (src_name.size() > 0) { dest_name.append(" ("); dest_name.append(src_name); dest_name.append(")"); } cptr_names.push_back(dest_name); } } } else { StreamConfig *scfg = getActiveStreamConfig(); if(scfg) { cptr_names = scfg->getTxNamesString(i); } } return cptr_names; } stringlist EAP::getPbckNameString(unsigned int i) { std::vector srcid; unsigned int srcid_0; stringlist pbck_names, dest_names; std::string src_name; if (m_router) { switch (i) { case 0: srcid_0 = (eRS_ARX0<<4); break; case 1: srcid_0 = (eRS_ARX1<<4); break; // Only 2 receiver possible (?) default: return pbck_names; } // At most 16 destinations per eRD for (int j=0; j<16; j++) { srcid.push_back(srcid_0+j); } for (unsigned it=0; itgetSourceName(srcid.at(it)); if (src_name.size() > 0) { // Search for destinations routed to this source // Multiple destinations for a single source are possible dest_names = m_router->getDestinationsForSource(src_name); if (dest_names.size() > 0) { src_name.append(" ("); stringlist::iterator it_d = dest_names.begin(); stringlist::iterator it_d_end_m1 = dest_names.end(); --it_d_end_m1; while (it_d != it_d_end_m1) { src_name.append((*it_d).c_str()); src_name.append("; "); it_d++; } src_name.append((*it_d).c_str()); src_name.append(")"); } pbck_names.push_back(src_name); } } } else { StreamConfig *scfg = getActiveStreamConfig(); if(scfg) { pbck_names = scfg->getRxNamesString(i); } } return pbck_names; } /** * Returns the router configuration for the current rate mode */ EAP::RouterConfig * EAP::getActiveRouterConfig() { switch(m_device.getCurrentConfig()) { case Device::eDC_Low: return &m_current_cfg_routing_low; case Device::eDC_Mid: return &m_current_cfg_routing_mid; case Device::eDC_High: return &m_current_cfg_routing_high; default: debugError("Unsupported configuration mode\n"); return NULL; } } /** * Returns the stream configuration for the current rate mode */ EAP::StreamConfig * EAP::getActiveStreamConfig() { switch(m_device.getCurrentConfig()) { case Device::eDC_Low: return &m_current_cfg_stream_low; case Device::eDC_Mid: return &m_current_cfg_stream_mid; case Device::eDC_High: return &m_current_cfg_stream_high; default: debugError("Unsupported configuration mode\n"); return NULL; } } /** * Uploads a new router configuration to the device * @param rcfg The new RouterConfig * @param low store as config for the low rates * @param mid store as config for the mid rates * @param high store as config for the high rates * @return true if successful, false otherwise */ bool EAP::updateRouterConfig(RouterConfig& rcfg, bool low, bool mid, bool high) { // write the router config to the appropriate memory space on the device if(!rcfg.write(eRT_NewRouting, 0)) { debugError("Could not write new router configuration\n"); return false; } // perform the store operation if(!loadRouterConfig(low, mid, high)) { debugError("Could not activate new router configuration\n"); updateConfigurationCache(); // for consistency return false; } return updateConfigurationCache(); } /** * Uploads a new router configuration to replace the configuration * for the current rate. * @param rcfg The new RouterConfig * @return true if successful, false otherwise */ bool EAP::updateCurrentRouterConfig(RouterConfig& rcfg) { switch(m_device.getCurrentConfig()) { case Device::eDC_Low: return updateRouterConfig(rcfg, true, false, false); case Device::eDC_Mid: return updateRouterConfig(rcfg, false, true, false); case Device::eDC_High: return updateRouterConfig(rcfg, false, false, true); default: debugError("Unsupported configuration mode\n"); return false; } } /** * Set a default configuration for the router. * This is necessary for somes devices for hardware coherence, in which case the next * has to be customized * @param rcfg The new RouterConfig * @return true if successful, false otherwise */ void EAP::setupDefaultRouterConfig_low() { unsigned int i; // add the routing destinations for a DICE chip switch(m_general_chip) { case DICE_EAP_CAP_GENERAL_CHIP_DICEII: // router/EAP currently not supported break; case DICE_EAP_CAP_GENERAL_CHIP_DICEJR: // second audio port (unique to the junior) // Ensure it is not muted for (i=0; i<8; i++) { addRoute(eRS_ARX0, i+8, eRD_InS1, i); } case DICE_EAP_CAP_GENERAL_CHIP_DICEMINI: // the 1394 stream receivers for (i=0; i<8; i++) { addRoute(eRS_InS0, i, eRD_ATX0, i); } for (i=0; i<8; i++) { addRoute(eRS_InS1, i, eRD_ATX0, i+8); } for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_ATX1, i); } for (i=0; i<8; i++) { addRoute(eRS_AES, i, eRD_ATX1, i+8); } // The audio ports // Ensure that audio port are not muted for (i=0; i<8; i++) { addRoute(eRS_ARX0, i, eRD_InS0, i); } // the AES receiver for (i=0; i<8; i++) { addRoute(eRS_Muted, 0, eRD_AES, i); } // the ADAT receiver for (i=0; i<8; i++) { addRoute(eRS_Muted, 0, eRD_ADAT, i); } // the Mixer outputs for (i=0; i<8; i++) { addRoute(eRS_InS0, i, eRD_Mixer0, i); } for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_Mixer0, i+8); } for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_Mixer0, i+16); } // the ARM audio port for (i=0; i<8; i++) { addRoute(eRS_Muted, 0, eRD_ARM, i); } // mute addRoute(eRS_Muted, 0, eRD_Muted, 0); break; default: // this is an unsupported chip break; } } void EAP::setupDefaultRouterConfig_mid() { setupDefaultRouterConfig_low(); } void EAP::setupDefaultRouterConfig_high() { setupDefaultRouterConfig_low(); } void EAP::setupDefaultRouterConfig() { // First clear routes RouterConfig *rcfg = getActiveRouterConfig(); rcfg->clearRoutes(); // Then define the configuration depending on the samplerate switch(m_device.getCurrentConfig()) { case Device::eDC_Low: setupDefaultRouterConfig_low(); break; case Device::eDC_Mid: setupDefaultRouterConfig_mid(); break; case Device::eDC_High: setupDefaultRouterConfig_high(); break; default: debugError("Unsupported configuration mode\n"); return; } updateCurrentRouterConfig(*rcfg); } /** * Add (create) a route from source to destination */ bool EAP::addRoute(enum eRouteSource srcid, unsigned int base_src, enum eRouteDestination dstid, unsigned int base_dst) { RouterConfig *rcfg = getActiveRouterConfig(); return rcfg->createRoute((srcid<<4) + base_src, (dstid<<4) + base_dst); } /** * Uploads a new stream configuration to the device * @param scfg The new StreamConfig * @param low store as config for the low rates * @param mid store as config for the mid rates * @param high store as config for the high rates * @return true if successful, false otherwise */ bool EAP::updateStreamConfig(StreamConfig& scfg, bool low, bool mid, bool high) { // write the stream config to the appropriate memory space on the device if(!scfg.write(eRT_NewStreamCfg, 0)) { debugError("Could not write new stream configuration\n"); return false; } // perform the store operation if(!loadStreamConfig(low, mid, high)) { debugError("Could not activate new stream configuration\n"); updateConfigurationCache(); // for consistency return false; } return updateConfigurationCache(); } /** * Uploads a new router and stream configuration to the device * @param rcfg The new RouterConfig * @param scfg The new StreamConfig * @param low store as config for the low rates * @param mid store as config for the mid rates * @param high store as config for the high rates * @return true if successful, false otherwise */ bool EAP::updateStreamConfig(RouterConfig& rcfg, StreamConfig& scfg, bool low, bool mid, bool high) { // write the router config to the appropriate memory space on the device if(!rcfg.write(eRT_NewRouting, 0)) { debugError("Could not write new router configuration\n"); return false; } // write the stream config to the appropriate memory space on the device if(!scfg.write(eRT_NewStreamCfg, 0)) { debugError("Could not write new stream configuration\n"); return false; } // perform the store operation if(!loadRouterAndStreamConfig(low, mid, high)) { debugError("Could not activate new router/stream configuration\n"); updateConfigurationCache(); // for consistency return false; } return updateConfigurationCache(); } bool EAP::loadFlashConfig() { bool retval = true; debugWarning("Untested code\n"); fb_quadlet_t cmd = DICE_EAP_CMD_OPCODE_LD_FLASH_CFG; cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_EXECUTE; if(!commandHelper(cmd)) { debugWarning("Command failed\n"); retval = false; } retval &= updateConfigurationCache(); return retval; } bool EAP::storeFlashConfig() { //debugWarning("Untested code\n") // Works. -Arnold; fb_quadlet_t cmd = DICE_EAP_CMD_OPCODE_ST_FLASH_CFG; cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_EXECUTE; return commandHelper(cmd); } // helpers void EAP::show() { printMessage("== DICE EAP ==\n"); printMessage("Parameter Space info:\n"); printMessage(" Capability : offset=%04X size=%06d\n", m_capability_offset, m_capability_size); printMessage(" Command : offset=%04X size=%06d\n", m_cmd_offset, m_cmd_size); printMessage(" Mixer : offset=%04X size=%06d\n", m_mixer_offset, m_mixer_size); printMessage(" Peak : offset=%04X size=%06d\n", m_peak_offset, m_peak_size); printMessage(" New Routing Cfg : offset=%04X size=%06d\n", m_new_routing_offset, m_new_routing_size); printMessage(" New Stream Cfg : offset=%04X size=%06d\n", m_new_stream_cfg_offset, m_new_stream_cfg_size); printMessage(" Current Cfg : offset=%04X size=%06d\n", m_curr_cfg_offset, m_curr_cfg_size); printMessage(" Standalone Cfg : offset=%04X size=%06d\n", m_standalone_offset, m_standalone_size); printMessage(" Application Space : offset=%04X size=%06d\n", m_app_offset, m_app_size); printMessage("Capabilities:\n"); printMessage(" Router: %sexposed, %swritable, %sstored, %d routes\n", (m_router_exposed?"":"not "), (m_router_readonly?"not ":""), (m_router_flashstored?"":"not "), m_router_nb_entries); printMessage(" Mixer : %sexposed, %swritable, %sstored\n", (m_mixer_exposed?"":"not "), (m_mixer_readonly?"not ":""), (m_mixer_flashstored?"":"not ")); printMessage(" tx id: (%d==eRD_Mixer0) ? %s, rx id: (%d==eRS_Mixer) ? %s\n", m_mixer_tx_id, (m_mixer_tx_id == eRD_Mixer0)?"true":"false", m_mixer_rx_id, (m_mixer_rx_id == eRS_Mixer) ?"true":"false"); printMessage(" nb tx channels: %d, nb rx channels: %d\n", m_mixer_nb_tx, m_mixer_nb_rx); printMessage(" General: dynamic stream config %ssupported\n", (m_general_support_dynstream?"":"not ")); printMessage(" flash load and store %ssupported\n", (m_general_support_flash?"":"not ")); printMessage(" peak metering %s\n", (m_general_peak_enabled?"enabled":"disabled")); printMessage(" stream config %sstored\n", (m_general_stream_cfg_stored?"":"not ")); printMessage(" max TX streams: %d, max RX streams: %d\n", m_general_max_tx, m_general_max_rx); if(m_general_chip == DICE_EAP_CAP_GENERAL_CHIP_DICEII) { printMessage(" Chip: DICE-II\n"); } else if(m_general_chip == DICE_EAP_CAP_GENERAL_CHIP_DICEMINI) { printMessage(" Chip: DICE Mini (TCD2210)\n"); } else if(m_general_chip == DICE_EAP_CAP_GENERAL_CHIP_DICEJR) { printMessage(" Chip: DICE Junior (TCD2220)\n"); } printMessage("--- Mixer configuration ---\n"); if(m_mixer) { m_mixer->show(); } printMessage("--- Router/Peak space ---\n"); if(m_router) { m_router->show(); } printMessage("--- Active Router ---\n"); RouterConfig *rcfg = getActiveRouterConfig(); if(rcfg) { rcfg->show(); } printMessage("--- Active Stream ---\n"); StreamConfig *scfg = getActiveStreamConfig(); if(scfg) { scfg->show(); } printMessage("--- Standalone configuration ---\n"); if(m_standalone) { m_standalone->show(); } // fixme // size_t len = 0x1000; // quadlet_t tmp[len]; // if(!readRegBlock( eRT_CurrentCfg, DICE_EAP_CURRCFG_LOW_STREAM, tmp, len*4) ) { // debugError("Failed to read block\n"); // } else { // hexDumpQuadlets(tmp, len); // } } void EAP::showApplication() { printMessage("--- Application space ---\n"); printMessage(" application space size: %06d\n", m_app_size); fb_quadlet_t* tmp = (fb_quadlet_t *)calloc(128, sizeof(fb_quadlet_t)); int appsize = m_app_size/sizeof(fb_quadlet_t); unsigned int offset = 0; unsigned int to_read; while ( appsize > 0 ) { to_read = (appsize<128)?appsize:128; if ( ! readRegBlock( eRT_Application, offset, tmp, to_read*sizeof(fb_quadlet_t) ) ) appsize = 0; else { hexDumpQuadlets(tmp, to_read); offset += 128*sizeof(fb_quadlet_t); appsize -= to_read; } } } void EAP::showFullRouter() { printMessage("--- Full router content ---\n"); printMessage(" %d entries to read\n", m_router_nb_entries); unsigned int offset; switch(m_device.getCurrentConfig()) { case Device::eDC_Low: offset = DICE_EAP_CURRCFG_LOW_ROUTER; break; case Device::eDC_Mid: offset = DICE_EAP_CURRCFG_MID_ROUTER; break; case Device::eDC_High: offset = DICE_EAP_CURRCFG_HIGH_ROUTER; break; default: offset = 0; break; } // Current config printMessage(" Current Configuration:\n"); // First bloc is the effective number of routes uint32_t nb_routes; if(!readRegBlock(eRT_CurrentCfg, offset, &nb_routes, 4)) { printMessage("Failed to read number of entries\n"); return; } printMessage(" %d routes\n", nb_routes); // read the route info uint32_t tmp_entries[m_router_nb_entries]; if(!readRegBlock(eRT_CurrentCfg, offset+4, tmp_entries, m_router_nb_entries*4)) { printMessage("Failed to read router config block information\n"); return; } // decode and print for(unsigned int i=0; i < m_router_nb_entries; i++) { printMessage(" %d: 0x%02x <- 0x%02x;\n", i, tmp_entries[i]&0xff, (tmp_entries[i]>>8)&0xff); } // New config printMessage(" New Configuration:\n"); // First bloc is the effective number of routes if(!readRegBlock(eRT_NewRouting, 0, &nb_routes, 4)) { printMessage("Failed to read number of entries\n"); return; } printMessage(" %d routes\n", nb_routes); // read the route info if(!readRegBlock(eRT_NewRouting, 4, tmp_entries, m_router_nb_entries*4)) { printMessage("Failed to read router config block information\n"); return; } // decode and print for(unsigned int i=0; i < m_router_nb_entries; i++) { printMessage(" %d: 0x%02x <- 0x%02x;\n", i, tmp_entries[i]&0xff, (tmp_entries[i]>>8)&0xff); } return; } void EAP::showFullPeakSpace() { printMessage("--- Full Peak space content ---\n"); // read the peak/route info uint32_t tmp_entries[m_router_nb_entries]; if(!readRegBlock(eRT_Peak, 0, tmp_entries, m_router_nb_entries*4)) { debugError("Failed to read peak block information\n"); return; } // decode and print for (unsigned int i=0; i>16); } return; } // EAP load/store operations enum EAP::eWaitReturn EAP::operationBusy() { fb_quadlet_t tmp; if(!readReg(eRT_Command, DICE_EAP_COMMAND_OPCODE, &tmp)) { debugError("Could not read opcode register\n"); return eWR_Error; } if( (tmp & DICE_EAP_CMD_OPCODE_FLAG_LD_EXECUTE) == DICE_EAP_CMD_OPCODE_FLAG_LD_EXECUTE) { return eWR_Busy; } else { return eWR_Done; } } enum EAP::eWaitReturn EAP::waitForOperationEnd(int max_wait_time_ms) { int max_waits = max_wait_time_ms; while(max_waits--) { enum eWaitReturn retval = operationBusy(); switch(retval) { case eWR_Busy: break; // not done yet, keep waiting case eWR_Done: return eWR_Done; case eWR_Error: case eWR_Timeout: debugError("Error while waiting for operation to end. (%d)\n", retval); } Util::SystemTimeSource::SleepUsecRelative(1000); } return eWR_Timeout; } bool EAP::commandHelper(fb_quadlet_t cmd) { // check whether another command is still running if(operationBusy() == eWR_Busy) { debugError("Other operation in progress\n"); return false; } // execute the command if(!writeReg(eRT_Command, DICE_EAP_COMMAND_OPCODE, cmd)) { debugError("Could not write opcode register\n"); return false; } // wait for the operation to end enum eWaitReturn retval = waitForOperationEnd(); switch(retval) { case eWR_Done: break; // do nothing case eWR_Timeout: debugWarning("Time-out while waiting for operation to end. (%d)\n", retval); return false; case eWR_Error: case eWR_Busy: // can't be returned debugError("Error while waiting for operation to end. (%d)\n", retval); return false; } // check the return value if(!readReg(eRT_Command, DICE_EAP_COMMAND_RETVAL, &cmd)) { debugError("Could not read return value register\n"); return false; } if(cmd != 0) { debugWarning("Command failed\n"); return false; } else { debugOutput(DEBUG_LEVEL_VERBOSE, "Command successful\n"); return true; } } bool EAP::loadRouterConfig(bool low, bool mid, bool high) { fb_quadlet_t cmd = DICE_EAP_CMD_OPCODE_LD_ROUTER; if(low) cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_LOW; if(mid) cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_MID; if(high) cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_HIGH; cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_EXECUTE; return commandHelper(cmd); } bool EAP::loadStreamConfig(bool low, bool mid, bool high) { debugWarning("Untested code\n"); fb_quadlet_t cmd = DICE_EAP_CMD_OPCODE_LD_STRM_CFG; if(low) cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_LOW; if(mid) cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_MID; if(high) cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_HIGH; cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_EXECUTE; return commandHelper(cmd); } bool EAP::loadRouterAndStreamConfig(bool low, bool mid, bool high) { debugWarning("Untested code\n"); fb_quadlet_t cmd = DICE_EAP_CMD_OPCODE_LD_RTR_STRM_CFG; if(low) cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_LOW; if(mid) cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_MID; if(high) cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_HIGH; cmd |= DICE_EAP_CMD_OPCODE_FLAG_LD_EXECUTE; return commandHelper(cmd); } /* I/O operations */ bool EAP::readReg(enum eRegBase base, unsigned offset, fb_quadlet_t *result) { fb_nodeaddr_t addr = offsetGen(base, offset, 4); return m_device.readReg(addr, result); } bool EAP::writeReg(enum eRegBase base, unsigned offset, fb_quadlet_t data) { fb_nodeaddr_t addr = offsetGen(base, offset, 4); return m_device.writeReg(addr, data); } bool EAP::readRegBlock(enum eRegBase base, unsigned offset, fb_quadlet_t *data, size_t length) { fb_nodeaddr_t addr = offsetGen(base, offset, length); return m_device.readRegBlock(addr, data, length); } bool EAP::writeRegBlock(enum eRegBase base, unsigned offset, fb_quadlet_t *data, size_t length) { fb_nodeaddr_t addr = offsetGen(base, offset, length); return m_device.writeRegBlock(addr, data, length); } fb_nodeaddr_t EAP::offsetGen(enum eRegBase base, unsigned offset, size_t length) { fb_nodeaddr_t addr; fb_nodeaddr_t maxlen; switch(base) { case eRT_Base: addr = 0; maxlen = DICE_EAP_MAX_SIZE; break; case eRT_Capability: addr = m_capability_offset; maxlen = m_capability_size; break; case eRT_Command: addr = m_cmd_offset; maxlen = m_cmd_size; break; case eRT_Mixer: addr = m_mixer_offset; maxlen = m_mixer_size; break; case eRT_Peak: addr = m_peak_offset; maxlen = m_peak_size; break; case eRT_NewRouting: addr = m_new_routing_offset; maxlen = m_new_routing_size; break; case eRT_NewStreamCfg: addr = m_new_stream_cfg_offset; maxlen = m_new_stream_cfg_size; break; case eRT_CurrentCfg: addr = m_curr_cfg_offset; maxlen = m_curr_cfg_size; break; case eRT_Standalone: addr = m_standalone_offset; maxlen = m_standalone_size; break; case eRT_Application: addr = m_app_offset; maxlen = m_app_size; break; default: debugError("Unsupported base address\n"); return 0; }; // out-of-range check if(length > maxlen) { debugError("requested length too large: %zd > %" PRIu64 "\n", length, maxlen); return DICE_INVALID_OFFSET; } return DICE_EAP_BASE + addr + offset; } /** * Check whether a device supports eap * @param d the device to check * @return true if the device supports EAP */ bool EAP::supportsEAP(Device &d) { DebugModule &m_debugModule = d.m_debugModule; quadlet_t tmp; if(!d.readReg(DICE_EAP_BASE, &tmp)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Could not read from DICE EAP base address\n"); return false; } if(!d.readReg(DICE_EAP_BASE + DICE_EAP_ZERO_MARKER_1, &tmp)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Could not read from DICE EAP zero marker\n"); return false; } if(tmp != 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "DICE EAP zero marker not zero\n"); return false; } return true; } // -------------------------------- Mixer ---------------------------------- // Dice matrix-mixer is a one-dimensional array with inputs index varying // first // // "ffado convention" is that inputs are rows and outputs are columns // EAP::Mixer::Mixer(EAP &p) : Control::MatrixMixer(&p.m_device, "MatrixMixer") , m_eap(p) , m_coeff(NULL) , m_debugModule(p.m_debugModule) { } EAP::Mixer::~Mixer() { if (m_coeff) { free(m_coeff); m_coeff = NULL; } } bool EAP::Mixer::init() { if(!m_eap.m_mixer_exposed) { debugError("Device does not expose mixer\n"); return false; } // remove previous coefficient array if(m_coeff) { free(m_coeff); m_coeff = NULL; } // allocate coefficient array int nb_inputs = m_eap.m_mixer_nb_tx; int nb_outputs = m_eap.m_mixer_nb_rx; m_coeff = (fb_quadlet_t *)calloc(nb_outputs * nb_inputs, sizeof(fb_quadlet_t)); // load initial values if(!loadCoefficients()) { debugWarning("Could not initialize coefficients\n"); return false; } updateNameCache(); return true; } bool EAP::Mixer::loadCoefficients() { if(m_coeff == NULL) { debugError("Coefficient cache not initialized\n"); return false; } int nb_inputs = m_eap.m_mixer_nb_tx; int nb_outputs = m_eap.m_mixer_nb_rx; if(!m_eap.readRegBlock(eRT_Mixer, 4, m_coeff, nb_inputs * nb_outputs * 4)) { debugError("Failed to read coefficients\n"); return false; } return true; } bool EAP::Mixer::storeCoefficients() { if(m_coeff == NULL) { debugError("Coefficient cache not initialized\n"); return false; } if(m_eap.m_mixer_readonly) { debugWarning("Mixer is read-only\n"); return false; } int nb_inputs = m_eap.m_mixer_nb_tx; int nb_outputs = m_eap.m_mixer_nb_rx; if(!m_eap.writeRegBlock(eRT_Mixer, 4, m_coeff, nb_inputs * nb_outputs * 4)) { debugError("Failed to read coefficients\n"); return false; } return true; } void EAP::Mixer::updateNameCache() { // debugWarning("What is this function about?\n"); debugOutput(DEBUG_LEVEL_VERBOSE, "What is this function about?\n"); #if 0 // figure out the number of i/o's int nb_inputs = m_eap.m_mixer_nb_tx; int nb_outputs = m_eap.m_mixer_nb_rx; // clear the previous map m_input_route_map.clear(); m_output_route_map.clear(); // find the active router configuration RouterConfig * rcfg = m_eap.getActiveRouterConfig(); if(rcfg == NULL) { debugError("Could not get active routing info\n"); return; } // find the inputs for(int i=0; i < nb_inputs; i++) { int ch = i; // the destination id of the mixer input int dest_int = m_eap.m_mixer_tx_id; // from the DICE mixer spec: // we can have 16 channels per "block" // if there are more, consecutive block id's are assumed while(ch > 15) { ch -= 16; dest_int += 1; } // the destination block and channel corresponding with this // mixer input is now known enum eRouteDestination dest = rcfg->intToRouteDestination(dest_int); // get the source for this mixer channel m_input_route_map[i] = rcfg->getRouteForDestination(dest, ch); debugOutput(DEBUG_LEVEL_VERBOSE, "Mixer input channel %2d source: %s (%d)\n", i, srcBlockToString(m_input_route_map[i].src), m_input_route_map[i].srcChannel); } // find where the outputs are connected to for(int i=0; i < nb_outputs; i++) { int ch = i; // the source id of the mixer input int src_int = m_eap.m_mixer_rx_id; // from the DICE mixer spec: // we can have 16 channels per "block" // if there are more, consecutive block id's are assumed while(ch > 15) { ch -= 16; src_int += 1; } // the source block and channel corresponding with this // mixer output is now known enum eRouteSource src = rcfg->intToRouteSource(src_int); // get the routing destinations for this mixer channel m_output_route_map[i] = rcfg->getRoutesForSource(src, ch); #ifdef DEBUG std::string destinations; for ( RouterConfig::RouteVectorIterator it = m_output_route_map[i].begin(); it != m_output_route_map[i].end(); ++it ) { RouterConfig::Route r = *it; // check whether the destination is valid if((r.dst != eRD_Invalid) && (r.dstChannel >= 0)) { char tmp[128]; snprintf(tmp, 128, "%s:%d,", dstBlockToString(r.dst), r.dstChannel); destinations += tmp; } } debugOutput(DEBUG_LEVEL_VERBOSE, "Mixer output channel %2d destinations: %s\n", i, destinations.c_str()); #endif } #endif } void EAP::Mixer::show() { int nb_inputs = m_eap.m_mixer_nb_tx; int nb_outputs = m_eap.m_mixer_nb_rx; updateNameCache(); const size_t bufflen = 4096; char tmp[bufflen]; int cnt; // // Caution the user that, displaying as further, because inputs index varies first // inputs will appears as columns at the opposite of the "ffado convention" printMessage(" -- inputs index -->>\n"); cnt = 0; for(int j=0; j < nb_inputs; j++) { cnt += snprintf(tmp+cnt, bufflen-cnt, " %02d ", j); } printMessage("%s\n", tmp); cnt = 0; for(int j=0; j < nb_inputs; j++) { cnt += snprintf(tmp+cnt, bufflen-cnt, "%s ", getRowName(j).data()); } printMessage("%s\n", tmp); // display coefficients for(int i=0; i < nb_outputs; i++) { cnt = 0; for(int j=0; j < nb_inputs; j++) { cnt += snprintf(tmp+cnt, bufflen-cnt, "%07d ", *(m_coeff + nb_inputs * i + j)); } // Display destinations name cnt += snprintf(tmp+cnt, bufflen-cnt, "=[%02d]=> %s", i, getColName(i).data()); printMessage("%s\n", tmp); } } int EAP::Mixer::canWrite( const int row, const int col) { if(m_eap.m_mixer_readonly) { return false; } return (row >= 0 && row < m_eap.m_mixer_nb_tx && col >= 0 && col < m_eap.m_mixer_nb_rx); } double EAP::Mixer::setValue( const int row, const int col, const double val) { if(m_eap.m_mixer_readonly) { debugWarning("Mixer is read-only\n"); return false; } int nb_outputs = m_eap.m_mixer_nb_tx; int addr = ((nb_outputs * col) + row) * 4; quadlet_t tmp = (quadlet_t) val; if(!m_eap.writeRegBlock(eRT_Mixer, 4+addr, &tmp, 4)) { debugError("Failed to write coefficient\n"); return 0; } return (double)(tmp); } double EAP::Mixer::getValue( const int row, const int col) { int nb_outputs = m_eap.m_mixer_nb_tx; int addr = ((nb_outputs * col) + row) * 4; quadlet_t tmp; if(!m_eap.readRegBlock(eRT_Mixer, 4+addr, &tmp, 4)) { debugError("Failed to read coefficient\n"); return 0; } return (double)(tmp); } int EAP::Mixer::getRowCount() { return m_eap.m_mixer_nb_tx; } int EAP::Mixer::getColCount() { return m_eap.m_mixer_nb_rx; } // full map updates are unsupported bool EAP::Mixer::getCoefficientMap(int &) { return false; } bool EAP::Mixer::storeCoefficientMap(int &) { if(m_eap.m_mixer_readonly) { debugWarning("Mixer is read-only\n"); return false; } return false; } // Names std::string EAP::Mixer::getRowName(const int row) { std::string mixer_src, row_name; if (row < 0 || row > m_eap.m_mixer_nb_tx) return "Invalid"; unsigned int dstid = (eRD_Mixer0<<4) + row; // Mixer has consecutive ID's debugOutput(DEBUG_LEVEL_VERBOSE, "EAP::Mixer::getRowName( %d ): ID's %d\n", row, dstid); if (m_eap.m_router){ std::string mixer_dst = m_eap.m_router->getDestinationName(dstid); mixer_src = m_eap.m_router->getSourceForDestination(mixer_dst); debugOutput(DEBUG_LEVEL_VERBOSE, "EAP::Mixer::found %s as source for %s\n", mixer_src.data(), mixer_dst.data()); row_name = mixer_dst + "\n(" + mixer_src + ")"; } else { char tmp[32]; snprintf(tmp, 32, "MixIn:%d", row); row_name = tmp; } return row_name; } std::string EAP::Mixer::getColName(const int col) { std::string mixer_dst; stringlist dest_names; // invalid col index if (col < 0 || col > m_eap.m_mixer_nb_rx) { mixer_dst.append("Invalid"); return mixer_dst; } unsigned int srcid = (eRS_Mixer<<4) + col; // Mixer has consecutive ID's debugOutput(DEBUG_LEVEL_VERBOSE, "EAP::Mixer::getColName( %d ): ID's %d\n", col, srcid); if (m_eap.m_router){ std::string mixer_src = m_eap.m_router->getSourceName(srcid); dest_names = m_eap.m_router->getDestinationsForSource(mixer_src); mixer_dst = mixer_src + "\n("; if (dest_names.size() > 0) { stringlist::iterator it_d = dest_names.begin(); stringlist::iterator it_d_end_m1 = dest_names.end(); --it_d_end_m1; while (it_d != it_d_end_m1) { mixer_dst.append((*it_d).c_str()); mixer_dst.append(";\n"); it_d++; } mixer_dst.append((*it_d).c_str()); } mixer_dst.append(")"); } else { char tmp[32]; snprintf(tmp, 32, "MixOut:%d", col); mixer_dst.append(tmp); } return mixer_dst; } // // ----------- Router ------------- // EAP::Router::Router(EAP &p) : Control::CrossbarRouter(&p.m_device, "Router") , m_eap(p) , m_peak( *(new PeakSpace(p)) ) , m_debugModule(p.m_debugModule) { } EAP::Router::~Router() { delete &m_peak; } void EAP::Router::update() { // Clear and // define new sources and destinations for the router m_sources.clear(); m_eap.setupSources(); m_destinations.clear(); m_eap.setupDestinations(); return; } void EAP::Router::addSource(const std::string& basename, enum eRouteSource srcid, unsigned int base, unsigned int cnt, unsigned int offset) { std::string name = basename + ":"; char tmp[4]; for (unsigned int i=0; i::iterator it=m_sources.begin(); it!=m_sources.end(); ++it) { if (it->second == srcid) { return it->first; } } return ""; } std::string EAP::Router::getDestinationName(const int dstid) { for (std::map::iterator it=m_destinations.begin(); it!=m_destinations.end(); ++it) { if (it->second == dstid) { return it->first; } } return ""; } int EAP::Router::getSourceIndex(std::string name) { if (m_sources.count(name) < 1) return -1; return m_sources[name]; } int EAP::Router::getDestinationIndex(std::string name) { if (m_destinations.count(name) < 1) return -1; return m_destinations[name]; } stringlist EAP::Router::getSourceNames() { stringlist n; for (std::map::iterator it=m_sources.begin(); it!=m_sources.end(); ++it) n.push_back(it->first); return n; } stringlist EAP::Router::getDestinationNames() { stringlist n; for (std::map::iterator it=m_destinations.begin(); it!=m_destinations.end(); ++it) n.push_back(it->first); return n; } stringlist EAP::Router::getDestinationsForSource(const std::string& srcname) { RouterConfig* rcfg = m_eap.getActiveRouterConfig(); if(rcfg == NULL) { debugError("Could not request active router configuration\n"); return stringlist(); } stringlist ret; std::vector dests = rcfg->getDestinationsForSource(m_sources[srcname]); std::string name; for (unsigned int i=0; igetSourceForDestination(m_destinations[dstname]); return getSourceName(source); } bool EAP::Router::canConnect(const int source, const int dest) { debugWarning("TODO: Implement canConnect(0x%02x, 0x%02x)\n", source, dest); // we can connect anything // FIXME: can we? return true; } bool EAP::Router::setConnectionState(const int source, const int dest, const bool enable) { debugOutput(DEBUG_LEVEL_VERBOSE,"Router::setConnectionState(0x%02x -> 0x%02x ? %i)\n", source, dest, enable); // get the routing configuration RouterConfig *rcfg = m_eap.getActiveRouterConfig(); if(rcfg == NULL) { debugError("Could not request active router configuration\n"); return false; } bool ret = false; if (enable) { ret = rcfg->setupRoute(source, dest); } else { ret = rcfg->muteRoute(dest); // FixMe: // Not all devices are intended to work correctly with a variable number of router entries // so muting the destination is preferable // Now, this might be useful for some other ones, but is it the right place for this ? // ret = rcfg->removeRoute(source, dest); } m_eap.updateCurrentRouterConfig(*rcfg); return ret; } bool EAP::Router::getConnectionState(const int source, const int dest) { // get the routing configuration RouterConfig *rcfg = m_eap.getActiveRouterConfig(); if(rcfg == NULL) { debugError("Could not request active router configuration\n"); return false; } if (rcfg->getSourceForDestination(dest) == source) { return true; } return false; } bool EAP::Router::canConnect(const std::string& src, const std::string& dst) { int srcidx = getSourceIndex(src); int dstidx = getDestinationIndex(dst); return canConnect(srcidx, dstidx); } bool EAP::Router::setConnectionState(const std::string& src, const std::string& dst, const bool enable) { int srcidx = getSourceIndex(src); int dstidx = getDestinationIndex(dst); return setConnectionState(srcidx, dstidx, enable); } bool EAP::Router::getConnectionState(const std::string& src, const std::string& dst) { int srcidx = getSourceIndex(src); int dstidx = getDestinationIndex(dst); return getConnectionState(srcidx, dstidx); } bool EAP::Router::clearAllConnections() { // build a new empty routing configuration RouterConfig newcfg = EAP::RouterConfig(m_eap); // upload the new router config if(!m_eap.updateCurrentRouterConfig(newcfg)) { debugError("Could not update router config\n"); return false; } return true; } bool EAP::Router::hasPeakMetering() { return m_eap.m_router_exposed; } double EAP::Router::getPeakValue(const std::string& dest) { m_peak.read(); unsigned char dst = m_destinations[dest]; return m_peak.getPeak(dst); } std::map EAP::Router::getPeakValues() { m_peak.read(); std::map ret; std::map peaks = m_peak.getPeaks(); std::string name; for (std::map::iterator it=peaks.begin(); it!=peaks.end(); ++it) { name = getDestinationName(it->first); if (name.size() != 0) { ret[name] = it->second; } } return ret; } void EAP::Router::show() { // print the peak space as it also contains the routing configuration printMessage("Router sources:\n"); printMessage(" %llu sources:\n", (unsigned long long)m_sources.size()); for ( std::map::iterator it=m_sources.begin(); it!=m_sources.end(); ++it ) { printMessage(" 0x%02x : %s\n", (*it).second, (*it).first.c_str()); } printMessage("Router destinations:\n"); printMessage(" %llu destinations:\n", (unsigned long long)m_destinations.size()); for ( std::map::iterator it=m_destinations.begin(); it!=m_destinations.end(); ++it ) { printMessage(" 0x%02x : %s\n", (*it).second, (*it).first.c_str()); } printMessage("Router connections:\n"); stringlist sources = getSourceNames(); stringlist destinations = getDestinationNames(); for (stringlist::iterator it1=sources.begin(); it1!=sources.end(); ++it1) { for (stringlist::iterator it2=destinations.begin(); it2!=destinations.end(); ++it2) { if (getConnectionState(*it1, *it2)) { printMessage(" %s -> %s\n", it1->c_str(), it2->c_str()); } } } printMessage("Active router config:\n"); m_eap.getActiveRouterConfig()->show(); printMessage("Active peak config:\n"); m_peak.read(); m_peak.show(); } // ----------- routing config ------------- EAP::RouterConfig::RouterConfig(EAP &p) : m_eap(p) , m_base(eRT_None), m_offset(0) , m_debugModule(p.m_debugModule) {} EAP::RouterConfig::RouterConfig(EAP &p, enum eRegBase b, unsigned int o) : m_eap(p) , m_base(b), m_offset(o) , m_debugModule(p.m_debugModule) {} EAP::RouterConfig::~RouterConfig() {} bool EAP::RouterConfig::read(enum eRegBase base, unsigned offset) { // first clear the current route vector clearRoutes(); uint32_t nb_routes; if(!m_eap.readRegBlock(base, offset, &nb_routes, 4)) { debugError("Failed to read number of entries\n"); return false; } if(nb_routes == 0) { debugWarning("No routes found. Base 0x%x, offset 0x%x\n", base, offset); } // read the route info uint32_t tmp_entries[nb_routes]; if(!m_eap.readRegBlock(base, offset+4, tmp_entries, nb_routes*4)) { debugError("Failed to read router config block information\n"); return false; } // decode into the routing map for(unsigned int i=0; i < nb_routes; i++) { m_routes2.push_back(std::make_pair(tmp_entries[i]&0xff, (tmp_entries[i]>>8)&0xff)); } return true; } bool EAP::RouterConfig::write(enum eRegBase base, unsigned offset) { uint32_t nb_routes = m_routes2.size(); if(nb_routes == 0) { debugWarning("Writing 0 routes? This will deactivate routing and make the device very silent...\n"); } if (nb_routes > 128) { debugError("More then 128 are not possible, only the first 128 routes will get saved!\n"); nb_routes = 128; } uint32_t tmp_entries[nb_routes]; // encode from the routing vector int i=0; for (RouteVectorV2::iterator it=m_routes2.begin(); it!=m_routes2.end(); ++it) { tmp_entries[i] = ((it->second<<8) + it->first)&0xffff; ++i; } unsigned int nb_routes_max = m_eap.getMaxNbRouterEntries(); uint32_t zeros[nb_routes_max+1]; for (unsigned int i=0; ifirst == dest) { it->second = src; return true; } } // destination does not yet exist; create it return createRoute(src, dest); } bool EAP::RouterConfig::removeRoute(unsigned char src, unsigned char dest) { debugOutput(DEBUG_LEVEL_VERBOSE,"RouterConfig::removeRoute( 0x%02x, 0x%02x )\n", src, dest); for (RouteVectorV2::iterator it=m_routes2.begin(); it!=m_routes2.end(); ++it) { if (it->first == dest) { if (it->second != src) { return false; } m_routes2.erase(it); return true; } } return false; } bool EAP::RouterConfig::muteRoute(unsigned char dest) { for (RouteVectorV2::iterator it=m_routes2.begin(); it!=m_routes2.end(); ++it) { if (it->first == dest) { it->second = m_eap.getSMuteId(); return true; } } return false; } bool EAP::RouterConfig::removeRoute(unsigned char dest) { debugOutput(DEBUG_LEVEL_VERBOSE,"RouterConfig::removeRoute( 0x%02x )\n", dest); for (RouteVectorV2::iterator it=m_routes2.begin(); it!=m_routes2.end(); ++it) { if (it->first == dest) { m_routes2.erase(it); return true; } } return false; } unsigned char EAP::RouterConfig::getSourceForDestination(unsigned char dest) { for (RouteVectorV2::iterator it=m_routes2.begin(); it!=m_routes2.end(); ++it) { if (it->first == dest) { return it->second; } } return -1; } std::vector EAP::RouterConfig::getDestinationsForSource(unsigned char source) { std::vector ret; for (RouteVectorV2::iterator it=m_routes2.begin(); it!=m_routes2.end(); ++it) { if (it->second == source) { ret.push_back(it->first); } } return ret; } void EAP::RouterConfig::show() { printMessage("%llu routes\n", (unsigned long long)m_routes2.size()); for ( RouteVectorV2::iterator it=m_routes2.begin(); it!=m_routes2.end(); ++it ) { printMessage("0x%02x -> 0x%02x\n", it->second, it->first); } } // // ----------- peak space ------------- // bool EAP::PeakSpace::read(enum eRegBase base, unsigned offset) { uint32_t nb_routes; // we have to figure out the number of entries through the currently // active router config RouterConfig *rcfg = m_eap.getActiveRouterConfig(); if(rcfg == NULL) { debugError("Could not get active router config\n"); return false; } nb_routes = rcfg->getNbRoutes(); // read the peak/route info uint32_t tmp_entries[nb_routes]; if(!m_eap.readRegBlock(base, offset, tmp_entries, nb_routes*4)) { debugError("Failed to read peak block information\n"); return false; } // parse the peaks into the map for (unsigned int i=0; i>16; if (m_peaks.count(dest) == 0 || m_peaks[dest] < peak) { m_peaks[dest] = peak; } } return true; } void EAP::PeakSpace::show() { printMessage(" %zi peaks\n", m_peaks.size()); for (std::map::iterator it=m_peaks.begin(); it!=m_peaks.end(); ++it) { printMessage("0x%02x : %i\n", it->first, it->second); } } int EAP::PeakSpace::getPeak(unsigned char dst) { int ret = m_peaks[dst]; m_peaks.erase(dst); return ret; } std::map EAP::PeakSpace::getPeaks() { // Create a new empty map std::map ret; // Swap the peak map with the new and empty one ret.swap(m_peaks); // Return the now filled map of the peaks :-) return ret; } // // ----------- stream config block ------------- // EAP::StreamConfig::StreamConfig(EAP &p, enum eRegBase b, unsigned int o) : m_eap(p) , m_base(b), m_offset(o) , m_nb_tx(0), m_nb_rx(0) , m_tx_configs(NULL), m_rx_configs(NULL) , m_debugModule(p.m_debugModule) { } EAP::StreamConfig::~StreamConfig() { if(m_tx_configs) delete[]m_tx_configs; if(m_rx_configs) delete[]m_rx_configs; } bool EAP::StreamConfig::read(enum eRegBase base, unsigned offset) { if(!m_eap.readRegBlock(base, offset, &m_nb_tx, 4)) { debugError("Failed to read number of tx entries\n"); return false; } if(!m_eap.readRegBlock(base, offset+4, &m_nb_rx, 4)) { debugError("Failed to read number of rx entries\n"); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, " Entries: TX: %u, RX: %u\n", m_nb_tx, m_nb_rx); if(m_tx_configs) { delete[]m_tx_configs; m_tx_configs = NULL; } if(m_rx_configs) { delete[]m_rx_configs; m_rx_configs = NULL; } offset += 8; if(m_nb_tx > 0) { m_tx_configs = new struct ConfigBlock[m_nb_tx]; for(unsigned int i=0; i(&(m_tx_configs[i])); if(!m_eap.readRegBlock(base, offset, ptr, sizeof(struct ConfigBlock))) { debugError("Failed to read tx entry %d\n", i); return false; } offset += sizeof(struct ConfigBlock); } } if(m_nb_rx > 0) { m_rx_configs = new struct ConfigBlock[m_nb_rx]; for(unsigned int i=0; i(&(m_rx_configs[i])); if(!m_eap.readRegBlock(base, offset, ptr, sizeof(struct ConfigBlock))) { debugError("Failed to read rx entry %d\n", i); return false; } offset += sizeof(struct ConfigBlock); } } return true; } bool EAP::StreamConfig::write(enum eRegBase base, unsigned offset) { if(!m_eap.writeRegBlock(base, offset, &m_nb_tx, 4)) { debugError("Failed to write number of tx entries\n"); return false; } if(!m_eap.writeRegBlock(base, offset+4, &m_nb_rx, 4)) { debugError("Failed to write number of rx entries\n"); return false; } offset += 8; for(unsigned int i=0; i(&(m_tx_configs[i])); if(!m_eap.writeRegBlock(base, offset, ptr, sizeof(struct ConfigBlock))) { debugError("Failed to write tx entry %d\n", i); return false; } offset += sizeof(struct ConfigBlock); } for(unsigned int i=0; i(&(m_rx_configs[i])); if(!m_eap.writeRegBlock(base, offset, ptr, sizeof(struct ConfigBlock))) { debugError("Failed to write rx entry %d\n", i); return false; } offset += sizeof(struct ConfigBlock); } return true; } stringlist EAP::StreamConfig::getNamesForBlock(struct ConfigBlock &b) { stringlist names; char namestring[DICE_EAP_CHANNEL_CONFIG_NAMESTR_LEN_BYTES+1]; memcpy(namestring, b.names, DICE_EAP_CHANNEL_CONFIG_NAMESTR_LEN_BYTES); // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)namestring, DICE_EAP_CHANNEL_CONFIG_NAMESTR_LEN_QUADS); #endif namestring[DICE_EAP_CHANNEL_CONFIG_NAMESTR_LEN_BYTES]='\0'; return m_eap.m_device.splitNameString(std::string(namestring)); } stringlist EAP::StreamConfig::getTxNamesString(unsigned int i) { return getNamesForBlock(m_tx_configs[i]); } stringlist EAP::StreamConfig::getRxNamesString(unsigned int i) { return getNamesForBlock(m_rx_configs[i]); } void EAP::StreamConfig::showConfigBlock(struct ConfigBlock &b) { printMessage(" Channel count : %u audio, %u midi\n", b.nb_audio, b.nb_midi); printMessage(" AC3 Map : 0x%08X\n", b.ac3_map); stringlist channel_names = getNamesForBlock(b); printMessage(" Channel names :\n"); for ( stringlist::iterator it = channel_names.begin(); it != channel_names.end(); ++it ) { printMessage(" %s\n", (*it).c_str()); } } void EAP::StreamConfig::show() { for(unsigned int i=0; i. * */ #ifndef __DICE_EAP_H #define __DICE_EAP_H #include "dice_avdevice.h" #define DICE_EAP_BASE 0x0000000000200000ULL #define DICE_EAP_MAX_SIZE 0x0000000000F00000ULL #define DICE_EAP_CAPABILITY_SPACE_OFF 0x0000 #define DICE_EAP_CAPABILITY_SPACE_SZ 0x0004 #define DICE_EAP_CMD_SPACE_OFF 0x0008 #define DICE_EAP_CMD_SPACE_SZ 0x000C #define DICE_EAP_MIXER_SPACE_OFF 0x0010 #define DICE_EAP_MIXER_SPACE_SZ 0x0014 #define DICE_EAP_PEAK_SPACE_OFF 0x0018 #define DICE_EAP_PEAK_SPACE_SZ 0x001C #define DICE_EAP_NEW_ROUTING_SPACE_OFF 0x0020 #define DICE_EAP_NEW_ROUTING_SPACE_SZ 0x0024 #define DICE_EAP_NEW_STREAM_CFG_SPACE_OFF 0x0028 #define DICE_EAP_NEW_STREAM_CFG_SPACE_SZ 0x002C #define DICE_EAP_CURR_CFG_SPACE_OFF 0x0030 #define DICE_EAP_CURR_CFG_SPACE_SZ 0x0034 #define DICE_EAP_STAND_ALONE_CFG_SPACE_OFF 0x0038 #define DICE_EAP_STAND_ALONE_CFG_SPACE_SZ 0x003C #define DICE_EAP_APP_SPACE_OFF 0x0040 #define DICE_EAP_APP_SPACE_SZ 0x0044 #define DICE_EAP_ZERO_MARKER_1 0x0048 // CAPABILITY registers #define DICE_EAP_CAPABILITY_ROUTER 0x0000 #define DICE_EAP_CAPABILITY_MIXER 0x0004 #define DICE_EAP_CAPABILITY_GENERAL 0x0008 #define DICE_EAP_CAPABILITY_RESERVED 0x000C // CAPABILITY bit definitions #define DICE_EAP_CAP_ROUTER_EXPOSED 0 #define DICE_EAP_CAP_ROUTER_READONLY 1 #define DICE_EAP_CAP_ROUTER_FLASHSTORED 2 #define DICE_EAP_CAP_ROUTER_MAXROUTES 16 #define DICE_EAP_CAP_MIXER_EXPOSED 0 #define DICE_EAP_CAP_MIXER_READONLY 1 #define DICE_EAP_CAP_MIXER_FLASHSTORED 2 #define DICE_EAP_CAP_MIXER_IN_DEV 4 #define DICE_EAP_CAP_MIXER_OUT_DEV 8 #define DICE_EAP_CAP_MIXER_INPUTS 16 #define DICE_EAP_CAP_MIXER_OUTPUTS 24 #define DICE_EAP_CAP_GENERAL_STRM_CFG_EN 0 #define DICE_EAP_CAP_GENERAL_FLASH_EN 1 #define DICE_EAP_CAP_GENERAL_PEAK_EN 2 #define DICE_EAP_CAP_GENERAL_MAX_TX_STREAM 4 #define DICE_EAP_CAP_GENERAL_MAX_RX_STREAM 8 #define DICE_EAP_CAP_GENERAL_STRM_CFG_FLS 12 #define DICE_EAP_CAP_GENERAL_CHIP 16 #define DICE_EAP_CAP_GENERAL_CHIP_DICEII 0 #define DICE_EAP_CAP_GENERAL_CHIP_DICEMINI 1 #define DICE_EAP_CAP_GENERAL_CHIP_DICEJR 2 // COMMAND registers #define DICE_EAP_COMMAND_OPCODE 0x0000 #define DICE_EAP_COMMAND_RETVAL 0x0004 // opcodes #define DICE_EAP_CMD_OPCODE_NO_OP 0x0000 #define DICE_EAP_CMD_OPCODE_LD_ROUTER 0x0001 #define DICE_EAP_CMD_OPCODE_LD_STRM_CFG 0x0002 #define DICE_EAP_CMD_OPCODE_LD_RTR_STRM_CFG 0x0003 #define DICE_EAP_CMD_OPCODE_LD_FLASH_CFG 0x0004 #define DICE_EAP_CMD_OPCODE_ST_FLASH_CFG 0x0005 #define DICE_EAP_CMD_OPCODE_FLAG_LD_LOW (1U<<16) #define DICE_EAP_CMD_OPCODE_FLAG_LD_MID (1U<<17) #define DICE_EAP_CMD_OPCODE_FLAG_LD_HIGH (1U<<18) #define DICE_EAP_CMD_OPCODE_FLAG_LD_EXECUTE (1U<<31) // MIXER registers // TODO // PEAK registers // TODO // NEW ROUTER registers // TODO // NEW STREAM CFG registers // TODO // CURRENT CFG registers #define DICE_EAP_CURRCFG_LOW_ROUTER 0x0000 #define DICE_EAP_CURRCFG_LOW_STREAM 0x1000 #define DICE_EAP_CURRCFG_MID_ROUTER 0x2000 #define DICE_EAP_CURRCFG_MID_STREAM 0x3000 #define DICE_EAP_CURRCFG_HIGH_ROUTER 0x4000 #define DICE_EAP_CURRCFG_HIGH_STREAM 0x5000 #define DICE_EAP_CHANNEL_CONFIG_NAMESTR_LEN_QUADS (64) #define DICE_EAP_CHANNEL_CONFIG_NAMESTR_LEN_BYTES (4*DICE_EAP_CHANNEL_CONFIG_NAMESTR_LEN_QUADS) namespace Dice { /** @brief Sources for audio hitting the router */ enum eRouteSource { eRS_AES = 0, eRS_ADAT = 1, eRS_Mixer = 2, eRS_InS0 = 4, eRS_InS1 = 5, eRS_ARM = 10, eRS_ARX0 = 11, eRS_ARX1 = 12, eRS_Muted = 15, eRS_Invalid = 16, }; /** @brief Destinations for audio exiting the router */ enum eRouteDestination { eRD_AES = 0, eRD_ADAT = 1, eRD_Mixer0 = 2, eRD_Mixer1 = 3, eRD_InS0 = 4, eRD_InS1 = 5, eRD_ARM = 10, eRD_ATX0 = 11, eRD_ATX1 = 12, eRD_Muted = 15, eRD_Invalid = 16, }; /** @brief represents the EAP interface available on some devices When available, the mixer and router are visible. This class is also the base for custom implementations of EAP extensions. */ class EAP : public Control::Container { public: /** @brief Command status */ enum eWaitReturn { eWR_Error, eWR_Timeout, eWR_Busy, eWR_Done, }; /** @brief Constants for the EAP spaces @see offsetGen for the calculation of the real offsets. */ enum eRegBase { eRT_Base, eRT_Capability, eRT_Command, eRT_Mixer, eRT_Peak, eRT_NewRouting, eRT_NewStreamCfg, eRT_CurrentCfg, eRT_Standalone, eRT_Application, eRT_None, }; private: /** @brief Description of the routing in the hardware */ class RouterConfig { private: friend class Dice::EAP; RouterConfig(EAP &); RouterConfig(EAP &, enum eRegBase, unsigned int offset); ~RouterConfig(); public: bool read() {return read(m_base, m_offset);}; bool write() {return write(m_base, m_offset);}; bool read(enum eRegBase b, unsigned offset); bool write(enum eRegBase b, unsigned offset); void show(); /** @brief Create a route between src and dest Clear the route vector To be used with many care ! */ bool clearRoutes(); /** @brief Create a route between src and dest This will effectively create a new destination. If a destination exists or its status is unknown, rather use the next setupRoute function */ bool createRoute(unsigned char src, unsigned char dest); /** @brief Set up a route between src and dest If a route with that destination exists, it will be replaced. If no route to that destination exists, a new route will be established. */ bool setupRoute(unsigned char src, unsigned char dest); /** @brief Mute a route for dest All EAP devices will support muting. Not all of them support (at least full support) removing a dest (see comment below) */ bool muteRoute(unsigned char dest); /** @brief Remove a route @todo is this really necessary? */ bool removeRoute(unsigned char src, unsigned char dest); /** @brief Remove the destinations route */ bool removeRoute(unsigned char dest); /** @brief Return the source for the given destination Returns -1 if the destination is not connected. */ unsigned char getSourceForDestination(unsigned char dest); /** @brief Return a list of destinations for a given source Returns an empty list if no destination is connected to this source. */ std::vector getDestinationsForSource(unsigned char src); unsigned int getNbRoutes() {return m_routes2.size();}; private: EAP &m_eap; enum eRegBase m_base; unsigned int m_offset; /** @brief map for the routes The key is the destination as each destination can only have audio from one source. Sources can be routed to several destinations though. */ // typedef std::map RouteVectorV2; typedef std::vector< std::pair > RouteVectorV2; RouteVectorV2 m_routes2; private: DECLARE_DEBUG_MODULE_REFERENCE; }; /** @brief Description of the peak space in hardware */ class PeakSpace { private: friend class Dice::EAP; PeakSpace(EAP &p) : m_eap(p), m_base(eRT_Peak), m_offset(0), m_debugModule(p.m_debugModule) {}; ~PeakSpace() {}; public: bool read() {return read(m_base, m_offset);}; bool read(enum eRegBase b, unsigned offset); void show(); std::map getPeaks(); int getPeak(unsigned char dest); private: EAP &m_eap; enum eRegBase m_base; unsigned int m_offset; /** @brief maps peaks to destinations */ std::map m_peaks; DECLARE_DEBUG_MODULE_REFERENCE; }; /** @brief Description of the streams in the hardware */ class StreamConfig { private: friend class Dice::EAP; StreamConfig(EAP &, enum eRegBase, unsigned int offset); ~StreamConfig(); public: struct ConfigBlock { // FIXME: somewhere in the DICE avdevice this is present too uint32_t nb_audio; uint32_t nb_midi; uint32_t names[DICE_EAP_CHANNEL_CONFIG_NAMESTR_LEN_QUADS]; uint32_t ac3_map; }; void showConfigBlock(struct ConfigBlock &); stringlist getNamesForBlock(struct ConfigBlock &b); stringlist getTxNamesString(unsigned int); stringlist getRxNamesString(unsigned int); bool read() {return read(m_base, m_offset);}; bool write() {return write(m_base, m_offset);}; bool read(enum eRegBase b, unsigned offset); bool write(enum eRegBase b, unsigned offset); void show(); private: EAP &m_eap; enum eRegBase m_base; unsigned int m_offset; uint32_t m_nb_tx; uint32_t m_nb_rx; struct ConfigBlock *m_tx_configs; struct ConfigBlock *m_rx_configs; DECLARE_DEBUG_MODULE_REFERENCE; }; /** @brief Description of the standalone mode configuration */ class StandaloneConfig { private: friend class Dice::EAP; StandaloneConfig(EAP &p) : m_eap(p), m_base(eRT_Standalone), m_offset(0), m_debugModule(p.m_debugModule) {}; ~StandaloneConfig() {}; public: bool read() {return read(m_base, m_offset);}; bool write() {return write(m_base, m_offset);}; bool read(enum eRegBase b, unsigned offset); bool write(enum eRegBase b, unsigned offset); void show(); private: EAP &m_eap; enum eRegBase m_base; unsigned int m_offset; uint32_t m_clk_src; uint32_t m_aes_ext; uint32_t m_adat_ext; uint32_t m_wc_ext; uint32_t m_int_ext; DECLARE_DEBUG_MODULE_REFERENCE; }; public: /** @brief The matrixmixer exposed */ class Mixer : public Control::MatrixMixer { public: Mixer(EAP &); ~Mixer(); bool init(); void show(); void updateNameCache(); /** * load the coefficients from the device into the local cache * @return */ bool loadCoefficients(); /** * Stores the coefficients from the cache to the device * @return */ bool storeCoefficients(); virtual int getRowCount( ); virtual int getColCount( ); virtual int canWrite( const int, const int ); virtual double setValue( const int, const int, const double ); virtual double getValue( const int, const int ); // bool hasNames() const { return false; } std::string getRowName( const int ); std::string getColName( const int ); // TODO: implement connections. bool canConnect() const { return false; } // full map updates are unsupported virtual bool getCoefficientMap(int &); virtual bool storeCoefficientMap(int &); private: EAP & m_eap; fb_quadlet_t *m_coeff; //std::map m_input_route_map; //std::map m_output_route_map; DECLARE_DEBUG_MODULE_REFERENCE; }; /** @brief The router exposed */ class Router : public Control::CrossbarRouter { public: Router(EAP &); ~Router(); void update(); void show(); void addDestination(const std::string& name, enum eRouteDestination dstid, unsigned int base, unsigned int cnt, unsigned int offset=0); void addSource(const std::string& name, enum eRouteSource srcid, unsigned int base, unsigned int cnt, unsigned int offset=0); // per-coefficient access virtual std::string getSourceName(const int); virtual std::string getDestinationName(const int); virtual int getSourceIndex(std::string); virtual int getDestinationIndex(std::string); virtual stringlist getSourceNames(); virtual stringlist getDestinationNames(); std::string getSourceForDestination(const std::string& dstname); stringlist getDestinationsForSource(const std::string& srcname); virtual bool canConnect(const int srcidx, const int dstidx); virtual bool setConnectionState(const int srcidx, const int dstidx, const bool enable); virtual bool getConnectionState(const int srcidx, const int dstidx); virtual bool canConnect(const std::string& srcname, const std::string& dstname); virtual bool setConnectionState(const std::string& srcname, const std::string& dstname, const bool enable); virtual bool getConnectionState(const std::string& srcname, const std::string& dstname); virtual bool clearAllConnections(); // peak metering support virtual bool hasPeakMetering(); virtual double getPeakValue(const std::string& dest); virtual std::map getPeakValues(); private: EAP &m_eap; /** @{ @brief Name-Index pairs for the sources and destinations The index is 'artificial' and is the block/channel combination used in the dice. */ std::map m_sources; std::map m_destinations; // @} PeakSpace &m_peak; DECLARE_DEBUG_MODULE_REFERENCE; }; public: /** constructor */ EAP(Device &); /** destructor */ virtual ~EAP(); /** @brief Does this device support the EAP? When subclassing EAP, return true only on devices that actually have an EAP. @todo Shouldn't this be inside Dice::Device? */ static bool supportsEAP(Device &); /** @brief Initialize the EAP */ bool init(); /// update EAP void update(); /// Show information about the EAP void show(); /// Dump the first parts of the application space void showApplication(); /// Show the full router content void showFullRouter(); /// Show the full peak space content void showFullPeakSpace(); /// Restore from flash bool loadFlashConfig(); /// Store to flash bool storeFlashConfig(); /// Is the current operation still busy? enum eWaitReturn operationBusy(); /// Block until the current operation is done enum eWaitReturn waitForOperationEnd(int max_wait_time_ms = 100); /// Update all configurations from the device bool updateConfigurationCache(); /** @{ @brief Read and write registers on the device */ bool readReg(enum eRegBase, unsigned offset, quadlet_t *); bool writeReg(enum eRegBase, unsigned offset, quadlet_t); bool readRegBlock(enum eRegBase, unsigned, fb_quadlet_t *, size_t); bool writeRegBlock(enum eRegBase, unsigned, fb_quadlet_t *, size_t); bool readRegBlockSwapped(enum eRegBase, unsigned, fb_quadlet_t *, size_t); bool writeRegBlockSwapped(enum eRegBase, unsigned, fb_quadlet_t *, size_t); //@} /** @brief Get access to the mixer */ Mixer* getMixer() {return m_mixer;}; /** @brief Get access to the router */ Router* getRouter() {return m_router;}; /** @brief Get capture and playback names */ stringlist getCptrNameString(unsigned int); stringlist getPbckNameString(unsigned int); /** @brief Utility function to reset router config at hardware default */ void setupDefaultRouterConfig(); protected: /** @brief Setup all the available sources This adds the needed entries for sources to the router. The default implementation decides on the chip which sources to add, subclasses should only add the sources actually usable for the device. To ease custom device support, this function is not in EAP::Router but here. */ void setupSources(); virtual void setupSources_low(); virtual void setupSources_mid(); virtual void setupSources_high(); virtual unsigned int getSMuteId(); /** @brief Setup all the available destinations This adds the needed entries for destinations to the router. The default implementation decides on the chip which destinations to add, subclasses should only add the destinations actually usable for the device. To ease custom device support, this function is not in EAP::Router but here. */ void setupDestinations(); virtual void setupDestinations_low(); virtual void setupDestinations_mid(); virtual void setupDestinations_high(); /** @brief Set up a default configuration for the router To ease custom device support, these functions are not in EAP::RouterConfig but here. */ virtual void setupDefaultRouterConfig_low(); virtual void setupDefaultRouterConfig_mid(); virtual void setupDefaultRouterConfig_high(); bool addRoute(enum eRouteSource srcid, unsigned int base_src, enum eRouteDestination dstid, unsigned int base_dst); /** @brief Actually add the source */ void addSource(const std::string name, unsigned int base, unsigned int count, enum eRouteSource srcid, unsigned int offset=0); /** @brief Actually add the destination */ void addDestination(const std::string name, unsigned int base, unsigned int count, enum eRouteDestination destid, unsigned int offset=0); uint16_t getMaxNbRouterEntries() {return m_router_nb_entries;}; private: /// Return the router configuration for the current rate RouterConfig * getActiveRouterConfig(); /// Return the stream configuration for the current rate StreamConfig * getActiveStreamConfig(); /// Write a new router configuration to the device bool updateRouterConfig(RouterConfig&, bool low, bool mid, bool high); /// Write a new router configuration to the device bool updateCurrentRouterConfig(RouterConfig&); /// Write a new stream configuration to the device bool updateStreamConfig(StreamConfig&, bool low, bool mid, bool high); /// Write a new stream configuration to the device bool updateStreamConfig(RouterConfig&, StreamConfig&, bool low, bool mid, bool high); bool loadRouterConfig(bool low, bool mid, bool high); bool loadStreamConfig(bool low, bool mid, bool high); bool loadRouterAndStreamConfig(bool low, bool mid, bool high); private: bool m_router_exposed; bool m_router_readonly; bool m_router_flashstored; uint16_t m_router_nb_entries; bool m_mixer_exposed; bool m_mixer_readonly; bool m_mixer_flashstored; uint8_t m_mixer_tx_id; uint8_t m_mixer_rx_id; uint8_t m_mixer_nb_tx; uint8_t m_mixer_nb_rx; bool m_general_support_dynstream; bool m_general_support_flash; bool m_general_peak_enabled; uint8_t m_general_max_tx; uint8_t m_general_max_rx; bool m_general_stream_cfg_stored; uint16_t m_general_chip; bool commandHelper(fb_quadlet_t cmd); /// Calculate the real offset for the different spaces fb_nodeaddr_t offsetGen(enum eRegBase, unsigned, size_t); private: Device & m_device; Mixer* m_mixer; Router* m_router; StandaloneConfig *m_standalone; RouterConfig m_current_cfg_routing_low; RouterConfig m_current_cfg_routing_mid; RouterConfig m_current_cfg_routing_high; StreamConfig m_current_cfg_stream_low; StreamConfig m_current_cfg_stream_mid; StreamConfig m_current_cfg_stream_high; fb_quadlet_t m_capability_offset; fb_quadlet_t m_capability_size; fb_quadlet_t m_cmd_offset; fb_quadlet_t m_cmd_size; fb_quadlet_t m_mixer_offset; fb_quadlet_t m_mixer_size; fb_quadlet_t m_peak_offset; fb_quadlet_t m_peak_size; fb_quadlet_t m_new_routing_offset; fb_quadlet_t m_new_routing_size; fb_quadlet_t m_new_stream_cfg_offset; fb_quadlet_t m_new_stream_cfg_size; fb_quadlet_t m_curr_cfg_offset; fb_quadlet_t m_curr_cfg_size; fb_quadlet_t m_standalone_offset; fb_quadlet_t m_standalone_size; fb_quadlet_t m_app_offset; fb_quadlet_t m_app_size; protected: DECLARE_DEBUG_MODULE; }; }; #endif // __DICE_EAP_H libffado-2.4.5/src/dice/dice_firmware_loader.cpp0000644000175000001440000004272014206145246021216 0ustar jwoitheusers/* * Copyright (C) 2012 by Peter Hinkel * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Implementation of the DICE firmware loader specification based upon * TCAT public available document v3.2.0.0 (2008-06-20) * */ #include #include "config.h" #include "dice/dice_avdevice.h" #include "dice/dice_firmware_loader.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "debugmodule/debugmodule.h" #include "libutil/ByteSwap.h" #include #include #include #include #include #include #include #include #include "libutil/Configuration.h" #include "devicemanager.h" #include "focusrite/saffire_pro40.h" #include "focusrite/saffire_pro24.h" #include "focusrite/saffire_pro14.h" using namespace std; namespace Dice { fb_quadlet_t tmp_quadlet; bool Device::showDiceInfoFL() { DICE_FL_GET_VENDOR_IMAGE_DESC_RETURN image_desc; writeReg(DICE_FL_OFFSET + DICE_FL_OPCODE, (1UL<<31) | DICE_FL_OP_GET_RUNNING_IMAGE_VINFO); do { usleep(10000); readReg(DICE_FL_OFFSET + DICE_FL_OPCODE, &tmp_quadlet); } while (tmp_quadlet & (1UL<<31)); readReg(DICE_FL_OFFSET + DICE_FL_RETURN_STATUS, &tmp_quadlet); if (tmp_quadlet == DICE_FL_RETURN_NO_ERROR) { readRegBlock(DICE_FL_OFFSET + DICE_FL_PARAMETER, (fb_quadlet_t*) &image_desc, sizeof(image_desc)); printMessage("Dice image vendor and product information:\n"); printMessage(" uiVProductID: %i\n", image_desc.uiProductID); printMessage(" uiVendorID: %s\n", image_desc.uiVendorID); printMessage(" uiVMajor: %i\n", image_desc.uiVMajor); printMessage(" uiVMajor: %i\n", image_desc.uiVMinor); printMessage(" user1: %i\n", image_desc.user1); printMessage(" user2: %i\n", image_desc.user2); } else { printMessage("Cannot read firmware info\n"); } return true; } bool Device::dumpFirmwareFL(const char* filename) { DICE_FL_INFO_PARAM* pflash_info = showFlashInfoFL(false); DICE_FL_READ_MEMORY memory; if (pflash_info) { ofstream file(filename, ios::out|ios::binary); if (file.is_open()) { uint32_t size = pflash_info->uiNumBlocks * pflash_info->uiBlockSize; uint32_t start = pflash_info->uiStartAddress; uint32_t end = pflash_info->uiEndAddress; printMessage("Downloading complete DICE flash into file (flash size = %i KBytes)\n", size/1024); printMessage("Please wait, dumping will take about a minute\n"); printMessage("Dump in progress ...\n"); while (start(end-start, sizeof(memory.ReadBuffer)); memory.uiStartAddress = start; writeRegBlock(DICE_FL_OFFSET + DICE_FL_PARAMETER, (fb_quadlet_t*) &memory, sizeof(memory)); writeReg(DICE_FL_OFFSET + DICE_FL_OPCODE, (1UL<<31) | DICE_FL_OP_READ_MEMORY); do { usleep(4000); readReg(DICE_FL_OFFSET + DICE_FL_OPCODE, &tmp_quadlet); } while (tmp_quadlet & (1UL<<31)); readReg(DICE_FL_OFFSET + DICE_FL_RETURN_STATUS, &tmp_quadlet); if (tmp_quadlet == DICE_FL_RETURN_NO_ERROR) { readRegBlock(DICE_FL_OFFSET + DICE_FL_PARAMETER, (fb_quadlet_t*) &memory, sizeof(memory)); file.write(memory.ReadBuffer, memory.uiLen); } else { printMessage("in dumpFirmwareFL, unknown error = 0x%X \nSTOP.\n", tmp_quadlet); return false; } start += memory.uiLen; } } file.close(); printMessage("Dumping successfully finished to file %s\n", filename); return true; } else { printMessage("Downloading not supported for this device\n"); return false; } } DICE_FL_INFO_PARAM* Device::showFlashInfoFL(bool v) { DICE_FL_INFO_PARAM flash_info; DICE_FL_INFO_PARAM* pflash_info = new DICE_FL_INFO_PARAM; writeReg(DICE_FL_OFFSET + DICE_FL_OPCODE, (1UL<<31) | DICE_FL_OP_GET_FLASH_INFO); do { usleep(10000); readReg(DICE_FL_OFFSET + DICE_FL_OPCODE, &tmp_quadlet); } while (tmp_quadlet & (1UL<<31)); readReg(DICE_FL_OFFSET + DICE_FL_RETURN_STATUS, &tmp_quadlet); if (tmp_quadlet == DICE_FL_RETURN_NO_ERROR) { readRegBlock(DICE_FL_OFFSET + DICE_FL_PARAMETER, (fb_quadlet_t*) &flash_info, sizeof(flash_info)); if (v) { printMessage("Flash Information:\n"); printMessage(" uiStartAddress: 0x%X\n", flash_info.uiStartAddress); printMessage(" uiEndAddress: 0x%X\n", flash_info.uiEndAddress); printMessage(" uiNumBlocks: %i\n", flash_info.uiNumBlocks); printMessage(" uiBlockSize: %i\n", flash_info.uiBlockSize); } memcpy(pflash_info, &flash_info, sizeof(flash_info)); return pflash_info; } else { printMessage("Cannot read flash information\n"); return pflash_info = NULL; } } bool Device::showImgInfoFL() { DICE_FL_GET_IMAGE_DESC_RETURN img_desc; uint32_t imageID=0; do { writeReg(DICE_FL_OFFSET + DICE_FL_PARAMETER, imageID); writeReg(DICE_FL_OFFSET + DICE_FL_OPCODE, (1UL<<31) | DICE_FL_OP_GET_IMAGE_DESC); do { usleep(100); readReg(DICE_FL_OFFSET + DICE_FL_OPCODE, &tmp_quadlet); } while (tmp_quadlet & (1UL<<31)); readReg(DICE_FL_OFFSET + DICE_FL_RETURN_STATUS, &tmp_quadlet); if (tmp_quadlet == DICE_FL_RETURN_NO_ERROR) { readRegBlock(DICE_FL_OFFSET + DICE_FL_PARAMETER, (fb_quadlet_t*) &img_desc, sizeof(img_desc)); printMessage("Detailed information of:\n"); printMessage(" image: %s\n", img_desc.name); printMessage(" flashBase @addr: 0x%X\n", img_desc.flashBase); printMessage(" memBase @addr:0x%X\n", img_desc.memBase); printMessage(" size: %i Bytes (0x%X)\n", img_desc.size, img_desc.size); //size in flash (blocks) printMessage(" entryPoint: 0x%X\n", img_desc.entryPoint); printMessage(" length: %i Bytes\n", img_desc.length); //real number of bytes of image printMessage(" checksum: %i\n", img_desc.chkSum); printMessage(" uiBoardSerialNumber: %i\n", img_desc.uiBoardSerialNumber); printMessage(" uiVersionHigh: %i\n", img_desc.uiVersionHigh); printMessage(" uiVersionLow: %i\n", img_desc.uiVersionLow); printMessage(" uiConfigurationFlags: %i\n", img_desc.uiConfigurationFlags); printMessage(" Build Time: %s\n", img_desc.BuildTime); printMessage(" Build Date: %s\n", img_desc.BuildDate); } else if (tmp_quadlet == DICE_FL_E_GEN_NOMATCH) { //printMessage("in showImgInfoFL(): Image not exists.\nSTOP.\n"); return false; } else { //printMessage("in showImgInfoFL(): unknown error = 0x%X\nSTOP.\n", tmp_quadlet); return false; } imageID++; } while (tmp_quadlet == DICE_FL_RETURN_NO_ERROR); return true; } bool Device::showAppInfoFL() { DICE_FL_GET_APP_INFO_RETURN app_info; writeReg(DICE_FL_OFFSET + DICE_FL_OPCODE, (1UL<<31) | DICE_FL_OP_GET_APP_INFO); do { usleep(10000); readReg(DICE_FL_OFFSET + DICE_FL_OPCODE, &tmp_quadlet); } while (tmp_quadlet & (1UL<<31)); readReg(DICE_FL_OFFSET + DICE_FL_RETURN_STATUS, &tmp_quadlet); if (tmp_quadlet == DICE_FL_RETURN_NO_ERROR) { readRegBlock(DICE_FL_OFFSET + DICE_FL_PARAMETER, (fb_quadlet_t*) &app_info, sizeof(app_info)); printMessage("Application information of 'dice' image:\n"); printMessage(" uiBaseSDKVersion: %X\n", app_info.uiBaseSDKVersion); //value needs to be parsed, but how? printMessage(" uiApplicationVersion: %X\n", app_info.uiApplicationVersion); //value needs to be parsed, but how? printMessage(" uiVendorID: %X\n", app_info.uiVendorID); printMessage(" uiProductID: %X\n", app_info.uiProductID); printMessage(" BuildTime: %s\n", app_info.BuildTime); printMessage(" BuildDate: %s\n", app_info.BuildDate); printMessage(" uiBoardSerialNumber: %d\n", app_info.uiBoardSerialNumber); return true; } else { printMessage("in showAppInfoFL(): unknown error = 0x%X\nSTOP.\n", tmp_quadlet); printMessage("Cannot read application information\n"); return false; } } bool Device::testDiceFL(int action) { DICE_FL_TEST_ACTION_PARAM testParam; DICE_FL_TEST_ACTION_RETURN testReturn; char pvalue0[11]; char pvalue1[11]; char* pEnd; switch (action) { case 1 : testParam.cmdID = action; printMessage("Use for input (quadlet = 32 bit) hex values only, i.e. '0x8080'\n"); printMessage("Writeable address range in RAM: 0x000000 - 0x7FFFFF\n"); printMessage("The address must be 32 bit aligned\n"); printMessage("Enter the @addr to write: "); cin >> pvalue0; testParam.lvalue0 = strtoul(pvalue0, &pEnd, 16); if (testParam.lvalue0 > 0x7FFFFF) { printMessage("@addr out of range. Aborting.\nSTOP.\n"); return false; } printMessage("Enter the value to write: "); cin >> pvalue1; testParam.lvalue1 = strtoul(pvalue1, &pEnd, 16); writeRegBlock(DICE_FL_OFFSET + DICE_FL_PARAMETER, (fb_quadlet_t*) &testParam, sizeof(testParam)); writeReg(DICE_FL_OFFSET + DICE_FL_OPCODE, (1UL<<31) | DICE_FL_OP_TEST_ACTION); do { usleep(10000); readReg(DICE_FL_OFFSET + DICE_FL_OPCODE, &tmp_quadlet); } while (tmp_quadlet & (1UL<<31)); readReg(DICE_FL_OFFSET + DICE_FL_RETURN_STATUS, &tmp_quadlet); if (tmp_quadlet == DICE_FL_RETURN_NO_ERROR) { printMessage("Quadlet written successfully\n"); return true; } else { printMessage("in testDiceFL(): unknown error = 0x%X\nSTOP.\n", tmp_quadlet); return false; } break; case 2 : testParam.cmdID = action; printMessage("Use for input hex values only, i.e. '0x8080'\n"); printMessage("The address must be 32 bit aligned\n"); printMessage("Enter the @addr to read: "); cin >> pvalue0; testParam.lvalue0 = strtoul(pvalue0, &pEnd, 16); writeRegBlock(DICE_FL_OFFSET + DICE_FL_PARAMETER, (fb_quadlet_t*) &testParam, sizeof(testParam)); writeReg(DICE_FL_OFFSET + DICE_FL_OPCODE, (1UL<<31) | DICE_FL_OP_TEST_ACTION); do { usleep(10000); readReg(DICE_FL_OFFSET + DICE_FL_OPCODE, &tmp_quadlet); } while (tmp_quadlet & (1UL<<31)); readReg(DICE_FL_OFFSET + DICE_FL_RETURN_STATUS, &tmp_quadlet); if (tmp_quadlet == DICE_FL_RETURN_NO_ERROR) { readRegBlock(DICE_FL_OFFSET + DICE_FL_PARAMETER, (fb_quadlet_t*) &testReturn, sizeof(testReturn)); printMessage("Value @addr 0x%X = 0x%X\n", testParam.lvalue0, testReturn.data[0]); printMessage("Quadlet read successfully\n"); return true; } else { printMessage("in testDiceFL(): unknown error = 0x%X\nSTOP.\n", tmp_quadlet); return false; } break; default : printMessage("Test&Debug command not found.\n"); return false; break; } } bool Device::deleteImgFL(const char* image, bool v) { DICE_FL_DELETE_IMAGE_PARAM imageDelete; memcpy(imageDelete.name, image, strlen(image)+1); printMessage("Deleting image '%s'\n", image); printMessage("Please wait, this will take some time\n"); printMessage("Deletion in progress ...\n"); writeRegBlock(DICE_FL_OFFSET + DICE_FL_PARAMETER, (fb_quadlet_t*) &imageDelete, sizeof(imageDelete)); writeReg(DICE_FL_OFFSET + DICE_FL_OPCODE, (1UL<<31) | DICE_FL_OP_DELETE_IMAGE); do { usleep(300000); readReg(DICE_FL_OFFSET + DICE_FL_OPCODE, &tmp_quadlet); } while (tmp_quadlet & (1UL<<31)); readReg(DICE_FL_OFFSET + DICE_FL_RETURN_STATUS, &tmp_quadlet); if (tmp_quadlet == DICE_FL_RETURN_NO_ERROR) { printMessage("Deletion successfully finished\n"); return true; } else if (tmp_quadlet == DICE_FL_E_FIS_ILLEGAL_IMAGE) { if (v) { printMessage("in deleteImgFL(): FIS illegal image\nSTOP.\n"); return false; } else { printMessage("No image with name '%s' in firmware. Nothing to delete.\n", image); return true; } } else { printMessage("in deleteImgFL(): unknown error = 0x%X\nSTOP.\n", tmp_quadlet); return false; } } bool Device::flashDiceFL(const char* filename, const char* image) { /*************************** * CHECKING CAPABILITY BITS ***************************/ readReg(DICE_FL_OFFSET + DICE_FL_CAPABILITIES, &tmp_quadlet); printMessage("CAPABILITIES = 0x%X\n", tmp_quadlet); /* * this operation is necessary to determine if IMAGE-AUTOERASE and PROGRESS-INFORMATION are supported * to be implemented in a future release */ /*************** * UPLOAD IMAGE ***************/ DICE_FL_UPLOAD_HEADER_PARAM upload_header; DICE_FL_UPLOAD_DATA_PARAM upload_data; uint32_t imageSize = 0; uint32_t index = 0; uint32_t chksum = 0; unsigned char* byteValue; ifstream file(filename, ios::in|ios::binary|ios::ate); if (file.is_open()) { imageSize = file.tellg(); file.seekg(0, ios::beg); printMessage("Uploading DICE image (image size = %i Bytes)\n", imageSize); printMessage("Please wait, this will take some time\n"); printMessage("Upload in progress ...\n"); while (file.good()) { file.read((char*) upload_data.buffer, sizeof(upload_data)); if ((upload_header.length = (uint32_t) file.gcount()) > 0) { upload_header.index = index; byteValue = (unsigned char*) upload_data.buffer; for (uint32_t i=0; i> query; if (query == 'y') { DICE_FL_CREATE_IMAGE_PARAM imageCreate; imageCreate.length = imageSize; imageCreate.execAddr = 0x30000; imageCreate.entryAddr = 0x30040; memcpy(imageCreate.name, image, strlen(image)+1); deleteImgFL(image); printMessage("Writing image '%s' to device\n", image); printMessage("Please wait, this will take some time\n"); printMessage("Flash in progress ...\n"); writeRegBlock(DICE_FL_OFFSET + DICE_FL_PARAMETER, (fb_quadlet_t*) &imageCreate, sizeof(imageCreate)); writeReg(DICE_FL_OFFSET + DICE_FL_OPCODE, (1UL<<31) | DICE_FL_OP_CREATE_IMAGE); do { usleep(300000); readReg(DICE_FL_OFFSET + DICE_FL_OPCODE, &tmp_quadlet); } while (tmp_quadlet & (1UL<<31)); readReg(DICE_FL_OFFSET + DICE_FL_RETURN_STATUS, &tmp_quadlet); if (tmp_quadlet == DICE_FL_RETURN_NO_ERROR) { printMessage("Flashing successfully finished\n"); printMessage("You have to restart the device manually to load the new image\n"); return true; } else if (tmp_quadlet == DICE_FL_E_FIS_ILLEGAL_IMAGE) { printMessage("in flashDiceFL(): FIS illegal image\nSTOP.\n"); return false; } else { printMessage("in flashDiceFL(): unknown error = 0x%X\nSTOP.\n", tmp_quadlet); return false; } /************** * RESET_IMAGE **************/ //writeReg(DICE_FL_OFFSET + DICE_FL_OPCODE, (1UL<<31) | DICE_FL_OP_RESET_IMAGE); /* * this would lead to a segmentation fault due to a BUS reset by DICE device */ } else { return false; } } else { printMessage("Checksum mismatch. Flash operation aborted.\n"); return false; } } } //namespace Dice libffado-2.4.5/src/dice/dice_firmware_loader.h0000644000175000001440000001231514206145246020660 0ustar jwoitheusers/* * Copyright (C) 2012 by Peter Hinkel * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Implementation of the DICE firmware loader specification based upon * TCAT public available document v3.2.0.0 (2008-06-20) * */ #ifndef __DICE_FIRMWARE_LOADER_H__ #define __DICE_FIRMWARE_LOADER_H__ #include #include "dice_defines.h" /* private memory space offset for command interface */ #define DICE_FL_INTERFACE_SPACE 0x0000FFFFE0100000ULL #define DICE_FL_OFFSET (DICE_FL_INTERFACE_SPACE ^ DICE_REGISTER_BASE) /* memory space offsets relative to DICE_FL_INTERFACE_SPACE */ #define DICE_FL_VERSION 0x0 //unused #define DICE_FL_OPCODE 0x4 #define DICE_FL_RETURN_STATUS 0x8 #define DICE_FL_PROGRESS 0xC #define DICE_FL_CAPABILITIES 0x10 #define DICE_FL_RESERVED 0x14 //unused #define DICE_FL_PARAMETER 0x2C #define DICE_FL_TESTDELAY 0xFD8 #define DICE_FL_TESTBUF 0xFDC #define DICE_FL_BUFFER 0x34 //offset for Upload buffer /* Opcode IDs for implemented functions (firmware dependent) */ #define DICE_FL_OP_GET_IMAGE_DESC 0x0 // parameters: imageId return: imageDesc #define DICE_FL_OP_DELETE_IMAGE 0x1 // parameters: name return: none #define DICE_FL_OP_CREATE_IMAGE 0x2 // parameters: execAddr, entryAddr, name return: none #define DICE_FL_OP_UPLOAD 0x3 // parameters: index, length, data, return none #define DICE_FL_OP_UPLOAD_STAT 0x4 // parameters: length return checksum #define DICE_FL_OP_RESET_IMAGE 0x5 // parameters: none #define DICE_FL_OP_TEST_ACTION 0x6 // parameters: none #define DICE_FL_OP_GET_FLASH_INFO 0x7 // parameters: none #define DICE_FL_OP_READ_MEMORY 0x8 // parameters: none #define DICE_FL_OP_NOOP 0x9 // parameters: none #define DICE_FL_OP_GET_RUNNING_IMAGE_VINFO 0xA // parameters: none return: vendorInfo #define DICE_FL_OP_CREATE_IMAGE2 0xB // parameters: none return: vendorInfo #define DICE_FL_OP_GET_APP_INFO 0xC // parameters: none return: new info structure /* return status #defines */ #define DICE_FL_RETURN_NO_ERROR 0x00000000 //Operation successful #define DICE_FL_E_GEN_NOMATCH 0xFF000000 //for Opcode ID: 0x0 #define DICE_FL_E_FIS_ILLEGAL_IMAGE 0xC5000001 //for Opcode ID: 0x1, 0x2 //#define DICE_FL_E_FIS_FLASH_OP_FAILED 0x00000000 //for Opcode ID: 0x1, 0x2 //#define DICE_FL_E_FIS_NO_SPACE 0x00000000 //for Opcode ID: 0x2 #define DICE_FL_E_BAD_INPUT_PARAM 0xC3000003 //for Opcode ID: 0x3 //#define DICE_FL_E_FAIL 0x00000000 //for Opcode ID: 0x6 /* data structure #defines */ #define MAX_IMAGE_NAME 16 /* data structures input parameters */ typedef struct { uint32_t length; uint32_t execAddr; uint32_t entryAddr; char name[MAX_IMAGE_NAME]; } DICE_FL_CREATE_IMAGE_PARAM; typedef struct { char name[MAX_IMAGE_NAME]; } DICE_FL_DELETE_IMAGE_PARAM; //decomposition of upload parameter due to limited transfer-bandwidth (max 128 quadlets = 512 bytes) //header part of upload parameter typedef struct { uint32_t index; uint32_t length; } DICE_FL_UPLOAD_HEADER_PARAM; //data part of upload parameter typedef struct { uint32_t buffer[128]; } DICE_FL_UPLOAD_DATA_PARAM; typedef struct { uint32_t cmdID; uint32_t lvalue0; uint32_t lvalue1; } DICE_FL_TEST_ACTION_PARAM; typedef struct { uint32_t uiStartAddress; uint32_t uiEndAddress; uint32_t uiNumBlocks; uint32_t uiBlockSize; } DICE_FL_INFO_PARAM; typedef struct { uint32_t uiStartAddress; uint32_t uiLen; char ReadBuffer[500]/*[4000]*/; } DICE_FL_READ_MEMORY; /* data structures output parameters */ typedef struct { char name[MAX_IMAGE_NAME]; uint32_t flashBase; uint32_t memBase; uint32_t size; uint32_t entryPoint; uint32_t length; uint32_t chkSum; uint32_t uiBoardSerialNumber; uint32_t uiVersionHigh; uint32_t uiVersionLow; uint32_t uiConfigurationFlags; char BuildTime[64]; char BuildDate[64]; } DICE_FL_GET_IMAGE_DESC_RETURN; typedef struct { uint32_t uiProductID; char uiVendorID[8]; uint32_t uiVMajor; uint32_t uiVMinor; uint32_t user1; uint32_t user2; } DICE_FL_GET_VENDOR_IMAGE_DESC_RETURN; typedef struct { uint32_t data[100]; } DICE_FL_TEST_ACTION_RETURN; typedef struct { uint32_t uiBaseSDKVersion; //The full version/revision of the SDK this build was based on uint32_t uiApplicationVersion; //The full version/revision of the Application uint32_t uiVendorID; //The Vendor ID uint32_t uiProductID; //The product ID char BuildTime[64]; //Build time char BuildDate[64]; //Build date uint32_t uiBoardSerialNumber; //The serial number of the board as obtained from persist. storage } DICE_FL_GET_APP_INFO_RETURN; #endif libffado-2.4.5/src/dice/focusrite/0000755000175000001440000000000014206145612016357 5ustar jwoitheuserslibffado-2.4.5/src/dice/focusrite/focusrite_eap.cpp0000644000175000001440000001274614206145246021730 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2009 by Arnold Krille * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "focusrite_eap.h" namespace Dice { namespace Focusrite { FocusriteEAP::FocusriteEAP(Dice::Device& dev) : Dice::EAP(dev) { } /** * Application space register read/write */ bool FocusriteEAP::readApplicationReg(unsigned offset, quadlet_t* quadlet) { bool ret = readReg(Dice::EAP::eRT_Application, offset, quadlet); return ret; } bool FocusriteEAP::writeApplicationReg(unsigned offset, quadlet_t quadlet) { // Do not write beyond the limit if (offset > FOCUSRITE_EAP_REGISTER_APP_MONITORING_LIMIT) { debugWarning(" Writing beyond address 0x%02x prohibited\n", FOCUSRITE_EAP_REGISTER_APP_MONITORING_LIMIT); return false; } bool ret = writeReg(Dice::EAP::eRT_Application, offset, quadlet); if (!ret) { debugWarning("Couldn't write %i to register %x!\n", quadlet, offset); return false; } return ret; } // Message set specific register bool FocusriteEAP::messageSet(unsigned offset, quadlet_t quadlet) { // Do not write beyond the limit if (offset > FOCUSRITE_EAP_REGISTER_APP_MONITORING_LIMIT) { debugWarning(" Message set register can not be beyond address 0x%02x\n", FOCUSRITE_EAP_REGISTER_APP_MONITORING_LIMIT); return false; } bool ret = writeApplicationReg(offset, quadlet); // Send NO_MESSAGE after any non-zero messages (Focusrite recommandation) writeApplicationReg(offset, (quadlet_t) FOCUSRITE_EAP_MESSAGE_SET_NO_MESSAGE); return ret; } /** * Potentiometer Class */ FocusriteEAP::Poti::Poti(Dice::Focusrite::FocusriteEAP* eap, std::string name, size_t offset, size_t msgset_offset, int msgset_value) : Control::Discrete(eap, name) , m_eap(eap) , m_name(name) , m_offset(offset) , m_msgset_offset(msgset_offset) , m_msgset_value(msgset_value) { debugOutput( DEBUG_LEVEL_VERBOSE, "Create Poti %s)\n", m_name.c_str()); } int FocusriteEAP::Poti::getValue() { int m_value; quadlet_t tmp; m_eap->readApplicationReg(m_offset, &tmp); m_value = -tmp; return m_value; } bool FocusriteEAP::Poti::setValue(int n) { // Might be the value has been modified via hardware; better to read the current value int m_value = getValue(); if (n == m_value) return true; m_value = -n; m_eap->writeApplicationReg(m_offset, (quadlet_t) m_value); m_eap->messageSet(m_msgset_offset, (quadlet_t) m_msgset_value); return true; } /** * Switch class */ FocusriteEAP::Switch::Switch(Dice::Focusrite::FocusriteEAP* eap, std::string name, size_t offset, int activevalue, size_t msgset_offset, int msgset_value ) : Control::Boolean(eap, name) , m_eap(eap) , m_name(name) , m_offset(offset) , m_activevalue(activevalue) , m_msgset_offset(msgset_offset) , m_msgset_value(msgset_value) { debugOutput( DEBUG_LEVEL_VERBOSE, "Create Switch %s)\n", m_name.c_str()); } bool FocusriteEAP::Switch::selected() { quadlet_t state_tmp; m_eap->readApplicationReg(m_offset, &state_tmp); bool is_selected = (state_tmp&m_activevalue)?true:false; return is_selected; } bool FocusriteEAP::Switch::select(bool n) { quadlet_t state_tmp; m_eap->readApplicationReg(m_offset, &state_tmp); bool is_selected = (state_tmp&m_activevalue)?true:false; // Switch the corresponding bit(s) if ( n != is_selected ) { m_eap->writeApplicationReg(m_offset, state_tmp^m_activevalue); m_eap->messageSet(m_msgset_offset, (quadlet_t) m_msgset_value); is_selected = n; } return is_selected; } /** * Volume Control Class */ FocusriteEAP::VolumeControl::VolumeControl(Dice::Focusrite::FocusriteEAP* eap, std::string name, size_t offset, int bitshift, size_t msgset_offset, int msgset_value) : Control::Discrete(eap, name) , m_eap(eap) , m_name(name) , m_offset(offset) , m_bitshift(bitshift) , m_msgset_offset(msgset_offset) , m_msgset_value(msgset_value) { debugOutput( DEBUG_LEVEL_VERBOSE, "Create Volume Control %s)\n", m_name.c_str()); } int FocusriteEAP::VolumeControl::getValue() { int m_value; quadlet_t tmp; m_eap->readApplicationReg(m_offset, &tmp); m_value = - ((tmp>>m_bitshift)&0xff); return m_value; } bool FocusriteEAP::VolumeControl::setValue(int n) { int m_value; quadlet_t tmp; m_eap->readApplicationReg(m_offset, &tmp); m_value = - ((tmp>>m_bitshift)&0xff); if (n == m_value) return true; m_value = n; tmp &= ~(0xff<writeApplicationReg(m_offset, ((-n)<messageSet(m_msgset_offset, (quadlet_t) m_msgset_value); return ret; } } } // vim: et libffado-2.4.5/src/dice/focusrite/focusrite_eap.h0000644000175000001440000001174214206145246021370 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2009 by Arnold Krille * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DICE_FOCUSRITE_FOCUSRITE_EAP_H #define DICE_FOCUSRITE_FOCUSRITE_EAP_H #include "dice/dice_eap.h" #include "libieee1394/configrom.h" /** * Focusrite EAP application space * Prescribed values common to all devices */ // The limit of the monitoring setting register space in the EAP application space // (included) // All subsequent addresses can not be beyond this limit #define FOCUSRITE_EAP_REGISTER_APP_MONITORING_LIMIT 0x68 // To be sent after any message set #define FOCUSRITE_EAP_MESSAGE_SET_NO_MESSAGE 0 // Global switches // One register for each #define FOCUSRITE_EAP_GLOBAL_MUTE_SWITCH_VALUE 1 #define FOCUSRITE_EAP_GLOBAL_DIM_SWITCH_VALUE 1 #define FOCUSRITE_EAP_ADATSPDIF_SWITCH_VALUE 1 // Per Line/Out switch monitor registers; bit encoding // Each register controls two (left and rigth) Line/Out #define FOCUSRITE_EAP_SWITCH_BIT_1 1 // Activate left Line/Out #define FOCUSRITE_EAP_SWITCH_BIT_2 2 // Activate right Line/Out #define FOCUSRITE_EAP_SWITCH_BIT_1_2 3 // Activate both #define FOCUSRITE_EAP_SWITCH_BIT_3 4 // Mute left Line/Out #define FOCUSRITE_EAP_SWITCH_BIT_4 8 // Mute right Line/Out // Per Line/Out mute, dim and mono register; bit encoding // One register controls all Line/Out #define FOCUSRITE_EAP_SWITCH_CONTROL_MUTE_SHIFT 0 #define FOCUSRITE_EAP_SWITCH_CONTROL_DIM_SHIFT 10 #define FOCUSRITE_EAP_SWITCH_CONTROL_MONO_SHIFT 20 #define FOCUSRITE_EAP_GLOBAL_MUTE_SWITCH_VALUE 1 #define FOCUSRITE_EAP_GLOBAL_DIM_SWITCH_VALUE 1 #define FOCUSRITE_EAP_SWITCH_CONTROL_VALUE 1 // Per Line/Out volume monitor registers // Each register controls two (left and rigth) Line/Out // The two last bytes (little endian) of each Line/Out volume register // control the right and line line respectively #define FOCUSRITE_EAP_LINEOUT_VOLUME_SET_1 0 #define FOCUSRITE_EAP_LINEOUT_VOLUME_SET_2 8 // Per Line/Out Instrument/Line and Hi/Lo gain switches // Each register controls two (left and rigth) Line/Out #define FOCUSRITE_EAP_LINEOUT_SWITCH_INST_SHIFT 16 #define FOCUSRITE_EAP_LINEOUT_SWITCH_GAIN_SHIFT 16 namespace Dice { namespace Focusrite { class FocusriteEAP : public Dice::EAP { public: FocusriteEAP(Dice::Device&); bool readApplicationReg(unsigned, quadlet_t*); bool writeApplicationReg(unsigned, quadlet_t); bool messageSet(unsigned, quadlet_t); public: /** * @brief A quite standard potentiometer. */ class Poti : public Control::Discrete { public: Poti(Dice::Focusrite::FocusriteEAP*, std::string, size_t, size_t, int); int getValue(int) { return getValue(); } bool setValue(int, int n) { return setValue(n); } int getMinimum() { return -127; } int getMaximum() { return 0; } int getValue(); bool setValue(int); private: Dice::Focusrite::FocusriteEAP* m_eap; std::string m_name; size_t m_offset; size_t m_msgset_offset; int m_msgset_value; }; /** * @brief A standard-switch for boolean. * * If you don't like True and False for the labels, subclass and return your own. */ class Switch : public Control::Boolean { public: Switch(Dice::Focusrite::FocusriteEAP*, std::string, size_t, int, size_t, int); bool selected(); bool select(bool); private: Dice::Focusrite::FocusriteEAP* m_eap; std::string m_name; size_t m_offset; int m_activevalue; size_t m_msgset_offset; int m_msgset_value; }; /** * @brief A less standard potentiometer. */ class VolumeControl : public Control::Discrete { public: VolumeControl(Dice::Focusrite::FocusriteEAP*, std::string, size_t, int, size_t, int); int getValue(int) { return getValue(); } bool setValue(int, int n) { return setValue(n); } int getMinimum() { return -255; } int getMaximum() { return 0; } int getValue(); bool setValue(int n); private: Dice::Focusrite::FocusriteEAP* m_eap; std::string m_name; size_t m_offset; int m_bitshift; size_t m_msgset_offset; int m_msgset_value; }; }; } } #endif // vim: et libffado-2.4.5/src/dice/focusrite/saffire_56.cpp0000644000175000001440000005200314206145246021017 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2014 by Philippe Carriere * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "saffire_56.h" #include "focusrite_eap.h" #include "libutil/ByteSwap.h" #include namespace Dice { namespace Focusrite { // ADAT as SPDIF state bool Saffire56::Saffire56EAP::getADATSPDIF_state() { quadlet_t state_tmp; bool adatspdif = false; if (!readReg(Dice::EAP::eRT_Application, SAFFIRE_56_REGISTER_APP_ADATSPDIF_SWITCH_CONTROL, &state_tmp)) { debugWarning("Could not read ADAT/SPDIF switch register: assume ADAT \n"); } else { adatspdif = (state_tmp&FOCUSRITE_EAP_ADATSPDIF_SWITCH_VALUE)?true:false; } return adatspdif; } // // Under 48kHz Saffire 56 has // - 8 analogic inputs (mic/line) // - 2x8 ADAT inputs or 8 ADAT and 2 additional optical SPDIF inputs // - 2 SPDIF inputs // - 28 ieee1394 inputs // - 18 mixer inputs // // - 10 analogic outputs // - 2x8 ADAT outputs or 4 ADAT and ? SPDIF outputs // - 2 SPDIF outputs // - 28 ieee1394 outputs // - 16 mixer outputs // void Saffire56::Saffire56EAP::setupSources_low() { bool adatspdif = getADATSPDIF_state(); addSource("SPDIF/In", 0, 2, eRS_AES, 1); if (!adatspdif) { addSource("ADAT/In", 0, 16, eRS_ADAT, 1); } else { addSource("ADAT/In", 0, 8, eRS_ADAT, 1); // FIXME: there is two additional SPDIF, location unknown addSource("SPDIF/In", 4, 2, eRS_AES, 3); } addSource("Mic/Lin/Inst", 0, 2, eRS_InS0, 1); addSource("Mic/Lin/In", 2, 6, eRS_InS1, 3); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 16, eRS_ARX0, 1); addSource("1394/In", 0, 12, eRS_ARX1, 17); addSource("Mute", 0, 1, eRS_Muted); } void Saffire56::Saffire56EAP::setupDestinations_low() { bool adatspdif = getADATSPDIF_state(); addDestination("SPDIF/Out", 0, 2, eRD_AES, 1); if (!adatspdif) { addDestination("ADAT/Out", 0, 16, eRD_ADAT, 1); } addDestination("Line/Out", 0, 2, eRD_InS0, 1); addDestination("Line/Out", 0, 8, eRD_InS1, 3); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 16, eRD_ATX0, 1); addDestination("1394/Out", 0, 10, eRD_ATX1, 17); addDestination("Loop", 10, 2, eRD_ATX1, 27); // Is a Mute destination useful ? // addDestination("Mute", 0, 1, eRD_Muted); } // // Under 96kHz Saffire 56 has // - 8 analogic inputs (mic/line) // - 2x4 ADAT inputs or 4 ADAT and 2 additional SPDIF inputs // - 2 SPDIF inputs // - 20 ieee1394 inputs // - 18 mixer inputs // // - 10 analogic outputs // - 2x4 ADAT outputs // - 2 SPDIF outputs // - 20 ieee1394 outputs // - 16 mixer outputs // void Saffire56::Saffire56EAP::setupSources_mid() { bool adatspdif = getADATSPDIF_state(); addSource("SPDIF/In", 0, 2, eRS_AES, 1); if (!adatspdif) { addSource("ADAT/In", 0, 8, eRS_ADAT, 1); } else { addSource("ADAT/In", 0, 4, eRS_ADAT, 1); // FIXME: there is two additional SPDIF, location unknown addSource("SPDIF/In", 4, 2, eRS_AES, 3); } addSource("Mic/Lin/Inst", 0, 2, eRS_InS0, 1); addSource("Mic/Lin/In", 2, 6, eRS_InS1, 3); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 16, eRS_ARX0, 1); addSource("1394/In", 0, 4, eRS_ARX1, 17); addSource("Mute", 0, 1, eRS_Muted); } void Saffire56::Saffire56EAP::setupDestinations_mid() { bool adatspdif = getADATSPDIF_state(); addDestination("SPDIF/Out", 0, 2, eRD_AES, 1); if (!adatspdif) { addDestination("ADAT/Out", 0, 4, eRD_ADAT, 1); } addDestination("Line/Out", 0, 2, eRD_InS0, 1); addDestination("Line/Out", 0, 8, eRD_InS1, 3); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 16, eRD_ATX0, 1); addDestination("1394/Out", 0, 2, eRD_ATX1, 17); addDestination("Loop", 2, 2, eRD_ATX1, 1); // Is a Mute destination useful ? // addDestination("Mute", 0, 1, eRD_Muted); } // // 192 kHz is not supported // void Saffire56::Saffire56EAP::setupSources_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 56\n"); } void Saffire56::Saffire56EAP::setupDestinations_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 56\n"); } /** * The default configurations for the Saffire 56 router. * For coherence with hardware, destinations must follow a specific ordering * There must be 76 destinations at low samplerate * Front LEDs are connected to the first eight router entries */ void Saffire56::Saffire56EAP::setupDefaultRouterConfig_low() { unsigned int i; // the 1394 stream receivers except the two "loops" one for (i=0; i<2; i++) { addRoute(eRS_InS0, i, eRD_ATX0, i); } for (i=0; i<6; i++) { addRoute(eRS_InS1, i+2, eRD_ATX0, i+2); } for (i=0; i<2; i++) { addRoute(eRS_AES, i, eRD_ATX0, i+8); } for (i=0; i<6; i++) { addRoute(eRS_ADAT, i, eRD_ATX0, i+10); } for (i=0; i<10; i++) { addRoute(eRS_ADAT, i+6, eRD_ATX1, i); } // The audio ports // Ensure that audio port are not muted for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_InS0, i); } for (i=0; i<8; i++) { addRoute(eRS_ARX0, i%2, eRD_InS1, i); } // the SPDIF receiver for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_AES, i); } // the ADAT receiver for (i=0; i<16; i++) { addRoute(eRS_Muted, 0, eRD_ADAT, i); } // the "loop" 1394 stream receivers for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_ATX1, i+10); } // the Mixer inputs for (i=0; i<2; i++) { addRoute(eRS_InS0, i, eRD_Mixer0, i); } for (i=0; i<6; i++) { addRoute(eRS_InS1, i+2, eRD_Mixer0, i+2); } for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_Mixer0, i+8); } for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_Mixer1, i); } // The two mute destinations for (i=0; i<2; i++) { addRoute(eRS_Mixer, i, eRD_Muted, 0); } } /** * There must be 60 destinations at mid samplerate * Front LEDs are connected to the first eight router entries */ void Saffire56::Saffire56EAP::setupDefaultRouterConfig_mid() { unsigned int i; // the 1394 stream receivers except the two "loop" ones for (i=0; i<2; i++) { addRoute(eRS_InS0, i, eRD_ATX0, i); } for (i=0; i<6; i++) { addRoute(eRS_InS1, i+2, eRD_ATX0, i+2); } for (i=0; i<2; i++) { addRoute(eRS_AES, i, eRD_ATX0, i+8); } for (i=0; i<4; i++) { addRoute(eRS_ADAT, i, eRD_ATX0, i+10); } for (i=0; i<4; i++) { addRoute(eRS_ADAT, i+4, eRD_ATX1, i); } // The audio ports // Ensure that audio port are not muted for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_InS0, i); } for (i=0; i<8; i++) { addRoute(eRS_ARX0, i%2, eRD_InS1, i); } // the SPDIF receiver for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_AES, i); } // the ADAT receiver for (i=0; i<8; i++) { addRoute(eRS_Muted, 0, eRD_ADAT, i); } // the "loop" 1394 stream receivers for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_ATX1, i+4); } // the Mixer inputs for (i=0; i<2; i++) { addRoute(eRS_InS0, i, eRD_Mixer0, i); } for (i=0; i<6; i++) { addRoute(eRS_InS1, i+2, eRD_Mixer0, i+2); } for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_Mixer0, i+8); } for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_Mixer1, i); } // The two mute destinations for (i=0; i<2; i++) { addRoute(eRS_Mixer, i, eRD_Muted, 0); } } /** * High rate not supported */ void Saffire56::Saffire56EAP::setupDefaultRouterConfig_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 56\n"); } /** * Pro 56 Monitor section * FIXME Must be thoroughly tested and registers location checked before enabling */ // Subclassed switch Saffire56::Saffire56EAP::Switch::Switch(Dice::Focusrite::FocusriteEAP* eap, std::string name, size_t offset, int activevalue, size_t msgset_offset, int msgset_value) : FocusriteEAP::Switch(eap, name, offset, activevalue, msgset_offset, msgset_value) , m_eap(eap) , m_name(name) , m_offset(offset) , m_activevalue(activevalue) , m_msgset_offset(msgset_offset) , m_msgset_value(msgset_value) { debugOutput( DEBUG_LEVEL_VERBOSE, "Create Saffire 56 Switch %s)\n", m_name.c_str()); } bool Saffire56::Saffire56EAP::Switch::select(bool n) { bool is_selected = FocusriteEAP::Switch::select(n); m_eap->update(); return is_selected; } Saffire56::Saffire56EAP::MonitorSection::MonitorSection(Dice::Focusrite::FocusriteEAP* eap, std::string name) : Control::Container(eap, name) , m_eap(eap) { // Global Mute control Control::Container* grp_globalmute = new Control::Container(m_eap, "GlobalMute"); addElement(grp_globalmute); FocusriteEAP::Switch* mute = new FocusriteEAP::Switch(m_eap, "State", SAFFIRE_56_REGISTER_APP_GLOBAL_MUTE_SWITCH, FOCUSRITE_EAP_GLOBAL_MUTE_SWITCH_VALUE, SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH); grp_globalmute->addElement(mute); // ADAT as optical SPDIF switch control Control::Container* grp_adatspdif = new Control::Container(m_eap, "AdatSpdif"); addElement(grp_adatspdif); Saffire56::Saffire56EAP::Switch* adatspdif = new Saffire56::Saffire56EAP::Switch( m_eap, "State", SAFFIRE_56_REGISTER_APP_ADATSPDIF_SWITCH_CONTROL, FOCUSRITE_EAP_ADATSPDIF_SWITCH_VALUE, SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_INSTLINE); grp_adatspdif->addElement(adatspdif); // Global Dim control Control::Container* grp_globaldim = new Control::Container(m_eap, "GlobalDim"); addElement(grp_globaldim); FocusriteEAP::Switch* dim = new FocusriteEAP::Switch(m_eap, "State", SAFFIRE_56_REGISTER_APP_GLOBAL_DIM_SWITCH, FOCUSRITE_EAP_GLOBAL_DIM_SWITCH_VALUE, SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH); grp_globaldim->addElement(dim); FocusriteEAP::Poti* dimlevel = new FocusriteEAP::Poti(m_eap, "Level", SAFFIRE_56_REGISTER_APP_GLOBAL_DIM_VOLUME, SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_globaldim->addElement(dimlevel); FocusriteEAP::Switch* s; // Mono/stereo switch Control::Container* grp_mono = new Control::Container(m_eap, "Mono"); addElement(grp_mono); for (unsigned int i=0; iaddElement(s); } // Independent control of each line/out Control::Container* grp_perchannel = new Control::Container(m_eap, "LineOut"); addElement(grp_perchannel); FocusriteEAP::VolumeControl* vol; // per Line/Out monitoring for (unsigned int i=0; iaddElement(s); stream.str(std::string()); stream << "UnActivate" << i*2+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_56_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_2, SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); // per Line/Out mute/unmute stream.str(std::string()); stream << "Mute" << i*2+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_56_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_3, SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); stream.str(std::string()); stream << "Mute" << i*2+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_56_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_4, SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); // per Line/Out global mute activation/unactivation stream.str(std::string()); stream << "GMute" << 2*i+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_56_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_MUTE_SHIFT+2*i), SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); stream.str(std::string()); stream << "GMute" << 2*i+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_56_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_MUTE_SHIFT+2*i+1), SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); // per Line/Out global dim activation/unactivation stream.str(std::string()); stream << "GDim" << 2*i+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_56_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_DIM_SHIFT+2*i), SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); stream.str(std::string()); stream << "GDim" << 2*i+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_56_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_DIM_SHIFT+2*i+1), SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); // per Line/Out volume control stream.str(std::string()); stream << "Volume" << i*2+1; vol = new FocusriteEAP::VolumeControl(m_eap, stream.str(), SAFFIRE_56_REGISTER_APP_LINEOUT_MONITOR_VOLUME +i*sizeof(quadlet_t), FOCUSRITE_EAP_LINEOUT_VOLUME_SET_1, SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(vol); stream.str(std::string()); stream << "Volume" << i*2+2; vol = new FocusriteEAP::VolumeControl(m_eap, stream.str(), SAFFIRE_56_REGISTER_APP_LINEOUT_MONITOR_VOLUME +i*sizeof(quadlet_t), FOCUSRITE_EAP_LINEOUT_VOLUME_SET_2, SAFFIRE_56_REGISTER_APP_MESSAGE_SET, SAFFIRE_56_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(vol); } } /** Device */ Saffire56::Saffire56( DeviceManager& d, ffado_smartptr( configRom )) : Dice::Device( d , configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Dice::Focusrite::Saffire56 (NodeID %d)\n", getConfigRom().getNodeId() ); } Saffire56::~Saffire56() { getEAP()->storeFlashConfig(); } bool Saffire56::discover() { if (Dice::Device::discover()) { FocusriteEAP* eap = dynamic_cast(getEAP()); Saffire56EAP::MonitorSection* monitor = new Saffire56EAP::MonitorSection(eap, "Monitoring"); eap->addElement(monitor); return true; } return false; } void Saffire56::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a Dice::Focusrite::Saffire56\n"); Dice::Device::showDevice(); } Dice::EAP* Saffire56::createEAP() { return new Saffire56EAP(*this); } /** * Nickname */ bool Saffire56::setNickname(std::string name) { char nickname[SAFFIRE_56_APP_NICK_NAME_SIZE+1]; // The device has room for SAFFIRE_56_APP_NICK_NAME_SIZE characters. // Erase supplementary characters or fill-in with NULL character if necessary strncpy(nickname, name.c_str(), SAFFIRE_56_APP_NICK_NAME_SIZE); // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)nickname, SAFFIRE_56_APP_NICK_NAME_SIZE/4); #endif if (!getEAP()->writeRegBlock(Dice::EAP::eRT_Application, SAFFIRE_56_REGISTER_APP_NICK_NAME, (quadlet_t*)nickname, SAFFIRE_56_APP_NICK_NAME_SIZE)) { debugError("Could not write nickname string \n"); return false; } return true; } std::string Saffire56::getNickname() { char nickname[SAFFIRE_56_APP_NICK_NAME_SIZE+1]; if (!getEAP()->readRegBlock(Dice::EAP::eRT_Application, SAFFIRE_56_REGISTER_APP_NICK_NAME, (quadlet_t*)nickname, SAFFIRE_56_APP_NICK_NAME_SIZE)){ debugError("Could not read nickname string \n"); return std::string("(unknown)"); } // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)nickname, SAFFIRE_56_APP_NICK_NAME_SIZE/4); #endif // The device supplies at most SAFFIRE_56_APP_NICK_NAME_SIZE characters. Ensure the string is // NULL terminated. nickname[SAFFIRE_56_APP_NICK_NAME_SIZE] = 0; return std::string(nickname); } } } // vim: et libffado-2.4.5/src/dice/focusrite/saffire_56.h0000644000175000001440000001156414206145246020473 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2014 by Philippe Carriere * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DICE_FOCUSRITE_SAFFIRE_56_H #define DICE_FOCUSRITE_SAFFIRE_56_H #include "dice/dice_avdevice.h" #include "libieee1394/configrom.h" #include "focusrite_eap.h" /** * Liquid Saffire 56 application space * FIXME Reversed by analysing register state compared to the Pro 40 * from 0x64 to 0xC0 register functions are unknown (probably the "liquid" preamp settings") * Message set register location is in doubt */ // Versioning registers #define SAFFIRE_56_REGISTER_APP_VERSION 0x00 #define SAFFIRE_56_REGISTER_APP_RELEASE 0x04 #define SAFFIRE_56_REGISTER_APP_BUILDNR 0x08 // Nickname register #define SAFFIRE_56_REGISTER_APP_NICK_NAME 0x44 // NOTE: in bytes #define SAFFIRE_56_APP_NICK_NAME_SIZE 16 // Global monitor registers (application space) #define SAFFIRE_56_REGISTER_APP_GLOBAL_MUTE_SWITCH 0x0C #define SAFFIRE_56_REGISTER_APP_GLOBAL_DIM_SWITCH 0x10 #define SAFFIRE_56_REGISTER_APP_GLOBAL_DIM_VOLUME 0x58 #define SAFFIRE_56_REGISTER_APP_GLOBAL_MONITOR_VOLUME 0x54 // Per line/out monitor volume and switches: registers are expected to be one after the other // each register controlling two output lines // The whole number of physical analog output is thus 2*SAFFIRE_56_APP_STEREO_LINEOUT_SIZE #define SAFFIRE_56_APP_STEREO_LINEOUT_SIZE 5 // Volume and switch monitor register #define SAFFIRE_56_REGISTER_APP_LINEOUT_MONITOR_VOLUME 0x14 #define SAFFIRE_56_REGISTER_APP_LINEOUT_MONITOR_SWITCH 0x28 // Switch controls // per line/out mute, dim and mono #define SAFFIRE_56_REGISTER_APP_LINEOUT_SWITCH_CONTROL 0x3C // ADAT as SPDIF #define SAFFIRE_56_REGISTER_APP_ADATSPDIF_SWITCH_CONTROL 0x5C // Message set // The location of the message register and the values for each setting // FIXME Message set register location mainly unknown #define SAFFIRE_56_REGISTER_APP_MESSAGE_SET 0xC8 #define SAFFIRE_56_MESSAGE_SET_NO_MESSAGE 0 #define SAFFIRE_56_MESSAGE_SET_LINEOUT_MONITOR_VOLUME 1 #define SAFFIRE_56_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH 2 #define SAFFIRE_56_MESSAGE_SET_LINEOUT_SWITCH_CONTROL 3 #define SAFFIRE_56_MESSAGE_SET_INSTLINE 4 #define SAFFIRE_56_MESSAGE_SET_MESSAGE_END 5 // Standalone #define SAFFIRE_56_REGISTER_STANDALONE_SWITCH 0x60 #define SAFFIRE_56_REGISTER_STANDALONE_SRC_SMPL 0xC4 // Automatically stored namespace Dice { namespace Focusrite { class Saffire56 : public Dice::Device { public: Saffire56( DeviceManager& d, ffado_smartptr( configRom )); virtual ~Saffire56(); bool discover(); virtual void showDevice(); bool canChangeNickname() { return true; } bool setNickname(std::string); std::string getNickname(); private: class Saffire56EAP : public FocusriteEAP { private: // Adat as Spdif register state required to adapt the router settings bool getADATSPDIF_state(); public: Saffire56EAP(Dice::Device& dev) : FocusriteEAP(dev) { } void setupSources_low(); void setupDestinations_low(); void setupSources_mid(); void setupDestinations_mid(); void setupSources_high(); void setupDestinations_high(); void setupDefaultRouterConfig_low(); void setupDefaultRouterConfig_mid(); void setupDefaultRouterConfig_high(); // Pro 40 requires a specific switch which updates the view of the routing // (essentially the ADAT/SPDIF switch) class Switch : public FocusriteEAP::Switch { public: Switch(Dice::Focusrite::FocusriteEAP*, std::string, size_t, int, size_t, int); bool select(bool); private: Dice::Focusrite::FocusriteEAP* m_eap; std::string m_name; size_t m_offset; int m_activevalue; size_t m_msgset_offset; int m_msgset_value; }; class MonitorSection : public Control::Container { public: MonitorSection(Dice::Focusrite::FocusriteEAP*, std::string); private: Dice::Focusrite::FocusriteEAP* m_eap; }; }; Dice::EAP* createEAP(); }; } } #endif // vim: et libffado-2.4.5/src/dice/focusrite/saffire_pro14.cpp0000644000175000001440000004237114206145246021541 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2012 by Philippe Carriere * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "saffire_pro14.h" #include "focusrite_eap.h" #include "libutil/ByteSwap.h" #include namespace Dice { namespace Focusrite { // // From Focusrite doc, whatever is the samplerate, Pro 14 has // - 4 analogic inputs (mic/line) // - 2 SPDIF inputs // - 8 ieee1394 inputs // - 18 mixer inputs (this is the standart for all Dice Jr) // // - 4 analogic outputs // - 2 SPDIF outputs // - 12 ieee1394 outputs // - 16 mixer outputs // void SaffirePro14::SaffirePro14EAP::setupSources_low() { addSource("SPDIF/In", 6, 2, eRS_AES, 1); addSource("Mic/Lin/Inst", 0, 2, eRS_InS0, 1); addSource("Mic/Lin/In", 2, 2, eRS_InS0, 3); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 12, eRS_ARX0, 1); addSource("Mute", 0, 1, eRS_Muted); } void SaffirePro14::SaffirePro14EAP::setupDestinations_low() { addDestination("SPDIF/Out", 6, 2, eRD_AES, 1); addDestination("Line/Out", 0, 4, eRD_InS0, 1); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 6, eRD_ATX0, 1); addDestination("Loop", 6, 2, eRD_ATX0, 1); // Is a Mute destination useful ? // addDestination("Mute", 0, 1, eRD_Muted); } // 88.2/96 kHz // void SaffirePro14::SaffirePro14EAP::setupSources_mid() { setupSources_low(); } void SaffirePro14::SaffirePro14EAP::setupDestinations_mid() { setupDestinations_low(); } // // 192 kHz is not supported // void SaffirePro14::SaffirePro14EAP::setupSources_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 14\n"); } void SaffirePro14::SaffirePro14EAP::setupDestinations_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 14\n"); } /** * The default configurations for the Saffire Pro 14 router. * For coherence with hardware, destinations must follow a specific ordering */ void SaffirePro14::SaffirePro14EAP::setupDefaultRouterConfig_low() { unsigned int i; // the 1394 stream receivers except the two "loops" one for (i=0; i<4; i++) { addRoute(eRS_InS1, i, eRD_ATX0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i, eRD_ATX0, i+4); } // The audio ports // Ensure that audio port are not muted for (i=0; i<4; i++) { addRoute(eRS_ARX0, i, eRD_InS0, i); } // the SPDIF receiver for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_AES, i); } // the "loops" 1394 stream receivers for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_ATX0, i+6); } // the Mixer inputs for (i=0; i<4; i++) { addRoute(eRS_InS1, i, eRD_Mixer0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i, eRD_Mixer0, i+4); } for (i=0; i<10; i++) { addRoute(eRS_Muted, 0, eRD_Mixer0, i+6); } for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_Mixer1, i); } // The two mute destinations for (i=0; i<2; i++) { addRoute(eRS_Mixer, i, eRD_Muted, 0); } } /** * Assume to be identical to low rate */ void SaffirePro14::SaffirePro14EAP::setupDefaultRouterConfig_mid() { setupDefaultRouterConfig_low(); } /** * High rate not supported */ void SaffirePro14::SaffirePro14EAP::setupDefaultRouterConfig_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 14\n"); } /** * Pro 14 Monitor section */ SaffirePro14::SaffirePro14EAP::MonitorSection::MonitorSection(Dice::Focusrite::FocusriteEAP* eap, std::string name) : Control::Container(eap, name) , m_eap(eap) { // Global Mute control Control::Container* grp_globalmute = new Control::Container(m_eap, "GlobalMute"); addElement(grp_globalmute); FocusriteEAP::Switch* mute = new FocusriteEAP::Switch(m_eap, "State", SAFFIRE_PRO14_REGISTER_APP_GLOBAL_MUTE_SWITCH, FOCUSRITE_EAP_GLOBAL_MUTE_SWITCH_VALUE, SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH); grp_globalmute->addElement(mute); // Global Dim control Control::Container* grp_globaldim = new Control::Container(m_eap, "GlobalDim"); addElement(grp_globaldim); FocusriteEAP::Switch* dim = new FocusriteEAP::Switch(m_eap, "State", SAFFIRE_PRO14_REGISTER_APP_GLOBAL_DIM_SWITCH, FOCUSRITE_EAP_GLOBAL_DIM_SWITCH_VALUE, SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH); grp_globaldim->addElement(dim); FocusriteEAP::Poti* dimlevel = new FocusriteEAP::Poti(m_eap, "Level", SAFFIRE_PRO14_REGISTER_APP_GLOBAL_DIM_VOLUME, SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_globaldim->addElement(dimlevel); FocusriteEAP::Switch* s; // Mono/stereo switch Control::Container* grp_mono = new Control::Container(m_eap, "Mono"); addElement(grp_mono); for (unsigned int i=0; iaddElement(s); } // Independent control of each line/out Control::Container* grp_perchannel = new Control::Container(m_eap, "LineOut"); addElement(grp_perchannel); FocusriteEAP::VolumeControl* vol; // per Line/Out monitoring for (unsigned int i=0; iaddElement(s); stream.str(std::string()); stream << "UnActivate" << i*2+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO14_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_2, SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); // per Line/Out mute/unmute stream.str(std::string()); stream << "Mute" << i*2+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO14_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_3, SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); stream.str(std::string()); stream << "Mute" << i*2+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO14_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_4, SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); // per Line/Out global mute activation/unactivation stream.str(std::string()); stream << "GMute" << 2*i+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO14_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_MUTE_SHIFT+2*i), SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); stream.str(std::string()); stream << "GMute" << 2*i+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO14_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_MUTE_SHIFT+2*i+1), SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); // per Line/Out global dim activation/unactivation stream.str(std::string()); stream << "GDim" << 2*i+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO14_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_DIM_SHIFT+2*i), SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); stream.str(std::string()); stream << "GDim" << 2*i+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO14_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_DIM_SHIFT+2*i+1), SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); // per Line/Out volume control stream.str(std::string()); stream << "Volume" << i*2+1; vol = new FocusriteEAP::VolumeControl(m_eap, stream.str(), SAFFIRE_PRO14_REGISTER_APP_LINEOUT_MONITOR_VOLUME +i*sizeof(quadlet_t), FOCUSRITE_EAP_LINEOUT_VOLUME_SET_1, SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(vol); stream.str(std::string()); stream << "Volume" << i*2+2; vol = new FocusriteEAP::VolumeControl(m_eap, stream.str(), SAFFIRE_PRO14_REGISTER_APP_LINEOUT_MONITOR_VOLUME +i*sizeof(quadlet_t), FOCUSRITE_EAP_LINEOUT_VOLUME_SET_2, SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(vol); } // Line/Inst and Hi/Lo Gain switches Control::Container* grp_LineInstGain = new Control::Container(m_eap, "LineInstGain"); addElement(grp_LineInstGain); FocusriteEAP::Switch* lineinst = new FocusriteEAP::Switch(m_eap, "LineInst1", SAFFIRE_PRO14_REGISTER_APP_LINEOUT_INST_SWITCH, SAFFIRE_PRO14_LINEOUT_SWITCH_INST_VALUE, SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_INSTLINE); grp_LineInstGain->addElement(lineinst); lineinst = new FocusriteEAP::Switch(m_eap, "LineInst2", SAFFIRE_PRO14_REGISTER_APP_LINEOUT_INST_SWITCH, SAFFIRE_PRO14_LINEOUT_SWITCH_INST_VALUE <addElement(lineinst); lineinst = new FocusriteEAP::Switch(m_eap, "LineGain3", SAFFIRE_PRO14_REGISTER_APP_LINEOUT_GAIN_SWITCH, SAFFIRE_PRO14_LINEOUT_SWITCH_GAIN_VALUE, SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO14_MESSAGE_SET_INSTLINE); grp_LineInstGain->addElement(lineinst); lineinst = new FocusriteEAP::Switch(m_eap, "LineGain4", SAFFIRE_PRO14_REGISTER_APP_LINEOUT_GAIN_SWITCH, SAFFIRE_PRO14_LINEOUT_SWITCH_GAIN_VALUE <addElement(lineinst); } /** Device */ SaffirePro14::SaffirePro14( DeviceManager& d, ffado_smartptr( configRom )) : Dice::Device( d , configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Dice::Focusrite::SaffirePro14 (NodeID %d)\n", getConfigRom().getNodeId() ); } SaffirePro14::~SaffirePro14() { getEAP()->storeFlashConfig(); } bool SaffirePro14::discover() { if (Dice::Device::discover()) { FocusriteEAP* eap = dynamic_cast(getEAP()); SaffirePro14EAP::MonitorSection* monitor = new SaffirePro14EAP::MonitorSection(eap, "Monitoring"); eap->addElement(monitor); return true; } return false; } void SaffirePro14::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a Dice::Focusrite::SaffirePro14\n"); Dice::Device::showDevice(); } Dice::EAP* SaffirePro14::createEAP() { return new SaffirePro14EAP(*this); } bool SaffirePro14::setNickname(std::string name) { char nickname[SAFFIRE_PRO14_APP_NICK_NAME_SIZE+1]; // The device has room for SAFFIRE_PRO14_APP_NICK_NAME_SIZE characters. // Erase supplementary characters or fill-in with NULL character if necessary strncpy(nickname, name.c_str(), SAFFIRE_PRO14_APP_NICK_NAME_SIZE); // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)nickname, SAFFIRE_PRO14_APP_NICK_NAME_SIZE/4); #endif if (!getEAP()->writeRegBlock(Dice::EAP::eRT_Application, SAFFIRE_PRO14_REGISTER_APP_NICK_NAME, (quadlet_t*)nickname, SAFFIRE_PRO14_APP_NICK_NAME_SIZE)) { debugError("Could not write nickname string \n"); return false; } return true; } std::string SaffirePro14::getNickname() { char nickname[SAFFIRE_PRO14_APP_NICK_NAME_SIZE+1]; if (!getEAP()->readRegBlock(Dice::EAP::eRT_Application, SAFFIRE_PRO14_REGISTER_APP_NICK_NAME, (quadlet_t*)nickname, SAFFIRE_PRO14_APP_NICK_NAME_SIZE)){ debugError("Could not read nickname string \n"); return std::string("(unknown)"); } // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)nickname, SAFFIRE_PRO14_APP_NICK_NAME_SIZE/4); #endif // The device supplies at most SAFFIRE_PRO14_APP_NICK_NAME_SIZE characters. Ensure the string is // NULL terminated. nickname[SAFFIRE_PRO14_APP_NICK_NAME_SIZE] = 0; return std::string(nickname); } } } // vim: et libffado-2.4.5/src/dice/focusrite/saffire_pro14.h0000644000175000001440000001060014206145246021174 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2012 by Philippe Carriere * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DICE_FOCUSRITE_SAFFIRE_PRO14_H #define DICE_FOCUSRITE_SAFFIRE_PRO14_H #include "dice/dice_avdevice.h" #include "libieee1394/configrom.h" #include "focusrite_eap.h" /** * Saffire Pro14 application space * Informations kindly provided by Focusrite support */ // Versioning registers #define SAFFIRE_PRO14_REGISTER_APP_VERSION 0x00 #define SAFFIRE_PRO14_REGISTER_APP_RELEASE 0x04 #define SAFFIRE_PRO14_REGISTER_APP_BUILDNR 0x08 // Nickname register #define SAFFIRE_PRO14_REGISTER_APP_NICK_NAME 0x44 // NOTE: in bytes #define SAFFIRE_PRO14_APP_NICK_NAME_SIZE 16 // Global monitor registers (application space) #define SAFFIRE_PRO14_REGISTER_APP_GLOBAL_MUTE_SWITCH 0x10 #define SAFFIRE_PRO14_REGISTER_APP_GLOBAL_DIM_SWITCH 0x14 #define SAFFIRE_PRO14_REGISTER_APP_GLOBAL_DIM_VOLUME 0x58 #define SAFFIRE_PRO14_REGISTER_APP_GLOBAL_MONITOR_VOLUME 0x54 // Per line/out monitor volume and switches: registers are expected to be one after the other // each register controlling two output lines // This is indeed the number of analog output controlled by the monitor #define SAFFIRE_PRO14_APP_STEREO_LINEOUT_SIZE 2 // Volume and switch monitor register #define SAFFIRE_PRO14_REGISTER_APP_LINEOUT_MONITOR_VOLUME 0x18 #define SAFFIRE_PRO14_REGISTER_APP_LINEOUT_MONITOR_SWITCH 0x2C // Switch control (per line/out mute, dim and mono) #define SAFFIRE_PRO14_REGISTER_APP_LINEOUT_SWITCH_CONTROL 0x40 // Message set // The location of the message register and the values for each setting #define SAFFIRE_PRO14_REGISTER_APP_MESSAGE_SET 0x0C #define SAFFIRE_PRO14_MESSAGE_SET_NO_MESSAGE 0 #define SAFFIRE_PRO14_MESSAGE_SET_LINEOUT_MONITOR_VOLUME 1 #define SAFFIRE_PRO14_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH 2 #define SAFFIRE_PRO14_MESSAGE_SET_LINEOUT_SWITCH_CONTROL 3 #define SAFFIRE_PRO14_MESSAGE_SET_INSTLINE 4 #define SAFFIRE_PRO14_MESSAGE_SET_MESSAGE_END 5 // Standalone #define SAFFIRE_PRO14_REGISTER_STANDALONE_SWITCH 0x64 #define SAFFIRE_PRO14_REGISTER_STANDALONE_SRC_SMPL 0x68 // Automatically stored // Inst/Line and Hi/Lo gain control // each register controls two (mono) output lines #define SAFFIRE_PRO14_REGISTER_APP_LINEOUT_INST_SWITCH 0x5C #define SAFFIRE_PRO14_LINEOUT_SWITCH_INST_NUMBER 1 #define SAFFIRE_PRO14_LINEOUT_SWITCH_INST_VALUE 2 #define SAFFIRE_PRO14_REGISTER_APP_LINEOUT_GAIN_SWITCH 0x60 #define SAFFIRE_PRO14_LINEOUT_SWITCH_GAIN_NUMBER 1 #define SAFFIRE_PRO14_LINEOUT_SWITCH_GAIN_VALUE 1 namespace Dice { namespace Focusrite { class SaffirePro14 : public Dice::Device { public: SaffirePro14( DeviceManager& d, ffado_smartptr( configRom )); virtual ~SaffirePro14(); bool discover(); virtual void showDevice(); bool canChangeNickname() { return true; } bool setNickname(std::string); std::string getNickname(); private: class SaffirePro14EAP : public FocusriteEAP { public: SaffirePro14EAP(Dice::Device& dev) : FocusriteEAP(dev) { } void setupSources_low(); void setupDestinations_low(); void setupSources_mid(); void setupDestinations_mid(); void setupSources_high(); void setupDestinations_high(); void setupDefaultRouterConfig_low(); void setupDefaultRouterConfig_mid(); void setupDefaultRouterConfig_high(); class MonitorSection : public Control::Container { public: MonitorSection(Dice::Focusrite::FocusriteEAP*, std::string); private: Dice::Focusrite::FocusriteEAP* m_eap; }; }; Dice::EAP* createEAP(); }; } } #endif // vim: et libffado-2.4.5/src/dice/focusrite/saffire_pro24.cpp0000644000175000001440000005141014206145246021534 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2009 by Arnold Krille * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "saffire_pro24.h" #include "focusrite_eap.h" #include "libutil/ByteSwap.h" #include #include namespace Dice { namespace Focusrite { void SaffirePro24::SaffirePro24EAP::setupSources_low() { addSource("SPDIF/In", 6, 2, eRS_AES, 1); addSource("ADAT/In", 0, 8, eRS_ADAT, 1); addSource("Anlg/In", 0, 4, eRS_InS0, 1); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 8, eRS_ARX0, 1); addSource("Mute", 0, 1, eRS_Muted); } void SaffirePro24::SaffirePro24EAP::setupDestinations_low() { addDestination("SPDIF/Out", 6, 2, eRD_AES, 1); addDestination("Line/Out", 0, 6, eRD_InS0, 1); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 16, eRD_ATX0, 1); // Is a Mute destination useful ? // addDestination("Mute", 0, 1, eRD_Muted); } void SaffirePro24::SaffirePro24EAP::setupSources_mid() { addSource("SPDIF/In", 6, 2, eRS_AES, 1); addSource("ADAT/In", 0, 4, eRS_ADAT, 1); addSource("Anlg/In", 0, 4, eRS_InS0, 1); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 8, eRS_ARX0, 1); addSource("Mute", 0, 1, eRS_Muted); } void SaffirePro24::SaffirePro24EAP::setupDestinations_mid() { addDestination("SPDIF/Out", 6, 2, eRD_AES, 1); addDestination("Line/Out", 0, 6, eRD_InS0, 1); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 12, eRD_ATX0, 1); // Is a Mute destination useful ? // addDestination("Mute", 0, 1, eRD_Muted); } void SaffirePro24::SaffirePro24EAP::setupSources_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 24\n"); } void SaffirePro24::SaffirePro24EAP::setupDestinations_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 24\n"); } /** * The default configurations for the Saffire Pro 24 router. * For coherence with hardware, destinations must follow a specific ordering * There must be 44 destinations at low samplerate * Front LEDs are connected to the first four router entries */ void SaffirePro24::SaffirePro24EAP::setupDefaultRouterConfig_low() { unsigned int i; // the 1394 stream receivers except the two "loops" one // Looks like analogic inputs are curiously ordered // (required for coherent front LEDs) for (i=0; i<2; i++) { addRoute(eRS_InS0, i+2, eRD_ATX0, i); } for (i=0; i<2; i++) { addRoute(eRS_InS0, i, eRD_ATX0, i+2); } for (i=0; i<2; i++) { addRoute(eRS_AES, i+6, eRD_ATX0, i+4); } for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_ATX0, i+6); } // The audio ports // Ensure that audio port are not muted for (i=0; i<6; i++) { addRoute(eRS_ARX0, i%2, eRD_InS0, i); } // the SPDIF receiver for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_AES, i+6); } // the "loops" 1394 stream receivers for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_ATX0, i+14); } // the Mixer inputs for (i=0; i<4; i++) { addRoute(eRS_InS0, i, eRD_Mixer0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i+6, eRD_Mixer0, i+4); } for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_Mixer0, i+6); } for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_Mixer0, i+14); } for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_Mixer1, i); } // The two mute destinations for (i=0; i<2; i++) { addRoute(eRS_Mixer, i, eRD_Muted, 0); } } /** * There must be 40 (?) destinations at mid samplerate * Front LEDs are connected to the first four router entries */ void SaffirePro24::SaffirePro24EAP::setupDefaultRouterConfig_mid() { unsigned int i; // the 1394 stream receivers except the two "loops" one // Looks like analogic inputs are curiously ordered // (required for coherent front LEDs) for (i=0; i<2; i++) { addRoute(eRS_InS0, i+2, eRD_ATX0, i); } for (i=0; i<2; i++) { addRoute(eRS_InS0, i, eRD_ATX0, i+2); } for (i=0; i<2; i++) { addRoute(eRS_AES, i+6, eRD_ATX0, i+4); } for (i=0; i<4; i++) { addRoute(eRS_ADAT, i, eRD_ATX0, i+6); } // The audio ports // Ensure that audio port are not muted for (i=0; i<6; i++) { addRoute(eRS_ARX0, i%2, eRD_InS0, i); } // the SPDIF receiver for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_AES, i+6); } // the "loops" 1394 stream receivers for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_ATX0, i+10); } // the Mixer inputs for (i=0; i<4; i++) { addRoute(eRS_InS0, i, eRD_Mixer0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i+6, eRD_Mixer0, i+4); } for (i=0; i<4; i++) { addRoute(eRS_ADAT, i, eRD_Mixer0, i+6); } for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_Mixer0, i+10); } for (i=0; i<4; i++) { addRoute(eRS_Muted, 0, eRD_Mixer0, i+12); } for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_Mixer1, i); } // The two mute destinations for (i=0; i<2; i++) { addRoute(eRS_Mixer, i, eRD_Muted, 0); } } /** * High rate not supported */ void SaffirePro24::SaffirePro24EAP::setupDefaultRouterConfig_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 24\n"); } /** * Pro 24 Monitor section */ SaffirePro24::SaffirePro24EAP::MonitorSection::MonitorSection(Dice::Focusrite::FocusriteEAP* eap, std::string name) : Control::Container(eap, name) , m_eap(eap) { // Global Mute control Control::Container* grp_globalmute = new Control::Container(m_eap, "GlobalMute"); addElement(grp_globalmute); FocusriteEAP::Switch* mute = new FocusriteEAP::Switch(m_eap, "State", SAFFIRE_PRO24_REGISTER_APP_GLOBAL_MUTE_SWITCH, FOCUSRITE_EAP_GLOBAL_MUTE_SWITCH_VALUE, SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH); grp_globalmute->addElement(mute); // Global Dim control Control::Container* grp_globaldim = new Control::Container(m_eap, "GlobalDim"); addElement(grp_globaldim); FocusriteEAP::Switch* dim = new FocusriteEAP::Switch(m_eap, "State", SAFFIRE_PRO24_REGISTER_APP_GLOBAL_DIM_SWITCH, FOCUSRITE_EAP_GLOBAL_DIM_SWITCH_VALUE, SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH); grp_globaldim->addElement(dim); FocusriteEAP::Poti* dimlevel = new FocusriteEAP::Poti(m_eap, "Level", SAFFIRE_PRO24_REGISTER_APP_GLOBAL_DIM_VOLUME, SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_globaldim->addElement(dimlevel); FocusriteEAP::Switch* s; // Mono/stereo switch Control::Container* grp_mono = new Control::Container(m_eap, "Mono"); addElement(grp_mono); for (unsigned int i=0; iaddElement(s); } // Independent control of each line/out Control::Container* grp_perchannel = new Control::Container(m_eap, "LineOut"); addElement(grp_perchannel); FocusriteEAP::VolumeControl* vol; // per Line/Out monitoring for (unsigned int i=0; iaddElement(s); stream.str(std::string()); stream << "UnActivate" << i*2+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO24_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_2, SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); // per Line/Out mute/unmute stream.str(std::string()); stream << "Mute" << i*2+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO24_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_3, SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); stream.str(std::string()); stream << "Mute" << i*2+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO24_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_4, SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); // per Line/Out global mute activation/unactivation stream.str(std::string()); stream << "GMute" << 2*i+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO24_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_MUTE_SHIFT+2*i), SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); stream.str(std::string()); stream << "GMute" << 2*i+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO24_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_MUTE_SHIFT+2*i+1), SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); // per Line/Out global dim activation/unactivation stream.str(std::string()); stream << "GDim" << 2*i+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO24_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_DIM_SHIFT+2*i), SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); stream.str(std::string()); stream << "GDim" << 2*i+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO24_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_DIM_SHIFT+2*i+1), SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); // per Line/Out volume control stream.str(std::string()); stream << "Volume" << i*2+1; vol = new FocusriteEAP::VolumeControl(m_eap, stream.str(), SAFFIRE_PRO24_REGISTER_APP_LINEOUT_MONITOR_VOLUME +i*sizeof(quadlet_t), FOCUSRITE_EAP_LINEOUT_VOLUME_SET_1, SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(vol); stream.str(std::string()); stream << "Volume" << i*2+2; vol = new FocusriteEAP::VolumeControl(m_eap, stream.str(), SAFFIRE_PRO24_REGISTER_APP_LINEOUT_MONITOR_VOLUME +i*sizeof(quadlet_t), FOCUSRITE_EAP_LINEOUT_VOLUME_SET_2, SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(vol); } Control::Container* grp_LineInstGain = new Control::Container(m_eap, "LineInstGain"); addElement(grp_LineInstGain); FocusriteEAP::Switch* lineinst = new FocusriteEAP::Switch(m_eap, "LineInst1", SAFFIRE_PRO24_REGISTER_APP_LINEOUT_INST_SWITCH, SAFFIRE_PRO24_LINEOUT_SWITCH_INST_VALUE, SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_INSTLINE); grp_LineInstGain->addElement(lineinst); lineinst = new FocusriteEAP::Switch(m_eap, "LineInst2", SAFFIRE_PRO24_REGISTER_APP_LINEOUT_INST_SWITCH, SAFFIRE_PRO24_LINEOUT_SWITCH_INST_VALUE <addElement(lineinst); lineinst = new FocusriteEAP::Switch(m_eap, "LineGain3", SAFFIRE_PRO24_REGISTER_APP_LINEOUT_GAIN_SWITCH, SAFFIRE_PRO24_LINEOUT_SWITCH_GAIN_VALUE, SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO24_MESSAGE_SET_INSTLINE); grp_LineInstGain->addElement(lineinst); lineinst = new FocusriteEAP::Switch(m_eap, "LineGain4", SAFFIRE_PRO24_REGISTER_APP_LINEOUT_GAIN_SWITCH, SAFFIRE_PRO24_LINEOUT_SWITCH_GAIN_VALUE <addElement(lineinst); } SaffirePro24::SaffirePro24( DeviceManager& d, ffado_smartptr( configRom )) : Dice::Device(d , configRom) { debugOutput(DEBUG_LEVEL_VERBOSE, "Created Dice::Focusrite::SaffirePro24 (NodeID %d)\n", getConfigRom().getNodeId()); } SaffirePro24::~SaffirePro24() { //debugOutput(DEBUG_LEVEL_VERBOSE, "Deleting the saffirePro24\n"); /// I wonder whether we should really save only on clean exits or also each time a setting is // changed. Or should we provide a function (and thus gui-button) to save the state of the // device? getEAP()->storeFlashConfig(); } bool SaffirePro24::discover() { if (Dice::Device::discover()) { fb_quadlet_t* version = (fb_quadlet_t *)calloc(2, sizeof(fb_quadlet_t)); getEAP()->readRegBlock(Dice::EAP::eRT_Application, SAFFIRE_PRO24_REGISTER_APP_VERSION, version, 1*sizeof(fb_quadlet_t)); // On August 2013, Focusrite released a new firmware. // Version numbering 2.0 (0x00020000) seems common to all Saffire // Dice EAP devices. // Note: 0x00010004 and 0x00010008 stands for a firmware version // not for a device identity. 0x00010004 is a pro24, 0x00010008 // is the pro24dsp. if (version[0] != 0x00010004 && version[0] != 0x00010008 && version[0] != 0x00020000) { fprintf(stderr, "This is a Focusrite Saffire Pro24 but not the right firmware. Better stop here before something goes wrong.\n"); fprintf(stderr, "This device has firmware 0x%x while we only know about versions 0x%x, 0x%x and 0x%x.\n", version[0], 0x10004, 0x10008, 0x00020000); return false; } // FIXME: What is the purpose of the following commented lines at this point ? //getEAP()->readRegBlock(Dice::EAP::eRT_Command, 0x00, tmp, 2*sizeof(fb_quadlet_t)); // DEBUG //hexDumpQuadlets(tmp, 2); // DEBUG FocusriteEAP* eap = dynamic_cast(getEAP()); SaffirePro24EAP::MonitorSection* monitor = new SaffirePro24EAP::MonitorSection(eap, "Monitoring"); getEAP()->addElement(monitor); return true; } return false; } void SaffirePro24::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a Dice::Focusrite::SaffirePro24\n"); Dice::Device::showDevice(); } Dice::EAP* SaffirePro24::createEAP() { return new SaffirePro24EAP(*this); } bool SaffirePro24::setNickname( std::string name ) { char nickname[SAFFIRE_PRO24_APP_NICK_NAME_SIZE+1]; // The device has room for SAFFIRE_PRO24_APP_NICK_NAME_SIZE characters. // Erase supplementary characters or fill-in with NULL character if necessary strncpy(nickname, name.c_str(), SAFFIRE_PRO24_APP_NICK_NAME_SIZE); // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)nickname, SAFFIRE_PRO24_APP_NICK_NAME_SIZE/4); #endif if (!getEAP()->writeRegBlock(Dice::EAP::eRT_Application, SAFFIRE_PRO24_REGISTER_APP_NICK_NAME, (quadlet_t*)nickname, SAFFIRE_PRO24_APP_NICK_NAME_SIZE)) { debugError("Could not write nickname string \n"); return false; } return true; } std::string SaffirePro24::getNickname() { char nickname[SAFFIRE_PRO24_APP_NICK_NAME_SIZE+1]; if (!getEAP()->readRegBlock(Dice::EAP::eRT_Application, SAFFIRE_PRO24_REGISTER_APP_NICK_NAME, (quadlet_t*)nickname, SAFFIRE_PRO24_APP_NICK_NAME_SIZE)){ debugError("Could not read nickname string \n"); return std::string("(unknown)"); } // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)nickname, SAFFIRE_PRO24_APP_NICK_NAME_SIZE/4); #endif // The device supplies at most SAFFIRE_PRO24_APP_NICK_NAME_SIZE characters. Ensure the string is // NULL terminated. nickname[SAFFIRE_PRO24_APP_NICK_NAME_SIZE] = 0; return std::string(nickname); } } } // vim: et libffado-2.4.5/src/dice/focusrite/saffire_pro24.h0000644000175000001440000001052114206145246021177 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2009 by Arnold Krille * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DICE_FOCUSRITE_SAFFIRE_PRO24_H #define DICE_FOCUSRITE_SAFFIRE_PRO24_H #include "dice/dice_avdevice.h" #include "libieee1394/configrom.h" #include "focusrite_eap.h" /** * Saffire Pro24 application space */ // Versioning registers #define SAFFIRE_PRO24_REGISTER_APP_VERSION 0x00 #define SAFFIRE_PRO24_REGISTER_APP_RELEASE 0x04 #define SAFFIRE_PRO24_REGISTER_APP_BUILDNR 0x08 // Nickname register #define SAFFIRE_PRO24_REGISTER_APP_NICK_NAME 0x40 // NOTE: in bytes #define SAFFIRE_PRO24_APP_NICK_NAME_SIZE 16 // Global monitor registers (application space) #define SAFFIRE_PRO24_REGISTER_APP_GLOBAL_MUTE_SWITCH 0x0C #define SAFFIRE_PRO24_REGISTER_APP_GLOBAL_DIM_SWITCH 0x10 #define SAFFIRE_PRO24_REGISTER_APP_GLOBAL_DIM_VOLUME 0x54 #define SAFFIRE_PRO24_REGISTER_APP_GLOBAL_MONITOR_VOLUME 0x50 // Per line/out monitor volume and switches: registers are expected to be one after the other // each register controlling two output lines // The whole number of physical analog output is thus 2*SAFFIRE_PRO24_APP_STEREO_LINEOUT_SIZE #define SAFFIRE_PRO24_APP_STEREO_LINEOUT_SIZE 3 // Volume and switch monitor register #define SAFFIRE_PRO24_REGISTER_APP_LINEOUT_MONITOR_VOLUME 0x14 #define SAFFIRE_PRO24_REGISTER_APP_LINEOUT_MONITOR_SWITCH 0x28 // Switch control (per line/out mute, dim and mono) #define SAFFIRE_PRO24_REGISTER_APP_LINEOUT_SWITCH_CONTROL 0x3C // Message set // The location of the message register and the values for each setting #define SAFFIRE_PRO24_REGISTER_APP_MESSAGE_SET 0x68 #define SAFFIRE_PRO24_MESSAGE_SET_NO_MESSAGE 0 #define SAFFIRE_PRO24_MESSAGE_SET_LINEOUT_MONITOR_VOLUME 1 #define SAFFIRE_PRO24_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH 2 #define SAFFIRE_PRO24_MESSAGE_SET_LINEOUT_SWITCH_CONTROL 3 #define SAFFIRE_PRO24_MESSAGE_SET_INSTLINE 4 #define SAFFIRE_PRO24_MESSAGE_SET_MESSAGE_END 5 // Standalone #define SAFFIRE_PRO24_REGISTER_STANDALONE_SWITCH 0x60 #define SAFFIRE_PRO24_REGISTER_STANDALONE_SRC_SMPL 0x64 // Automatically stored // Inst/Line and Hi/Lo gain control // each register controls two (mono) output lines #define SAFFIRE_PRO24_REGISTER_APP_LINEOUT_INST_SWITCH 0x58 #define SAFFIRE_PRO24_LINEOUT_SWITCH_INST_NUMBER 1 #define SAFFIRE_PRO24_LINEOUT_SWITCH_INST_VALUE 2 #define SAFFIRE_PRO24_REGISTER_APP_LINEOUT_GAIN_SWITCH 0x5C #define SAFFIRE_PRO24_LINEOUT_SWITCH_GAIN_NUMBER 1 #define SAFFIRE_PRO24_LINEOUT_SWITCH_GAIN_VALUE 1 namespace Dice { namespace Focusrite { class SaffirePro24 : public Dice::Device { public: SaffirePro24( DeviceManager& d, ffado_smartptr( configRom )); ~SaffirePro24(); bool discover(); void showDevice(); bool canChangeNickname() { return true; } bool setNickname( std::string name ); std::string getNickname(); private: class SaffirePro24EAP : public FocusriteEAP { public: SaffirePro24EAP(Dice::Device& dev) : FocusriteEAP(dev) { } void setupSources_low(); void setupDestinations_low(); void setupSources_mid(); void setupDestinations_mid(); void setupSources_high(); void setupDestinations_high(); void setupDefaultRouterConfig_low(); void setupDefaultRouterConfig_mid(); void setupDefaultRouterConfig_high(); class MonitorSection : public Control::Container { public: MonitorSection(Dice::Focusrite::FocusriteEAP*, std::string); private: Dice::Focusrite::FocusriteEAP* m_eap; }; }; Dice::EAP* createEAP(); }; } } #endif // vim: et libffado-2.4.5/src/dice/focusrite/saffire_pro26.cpp0000644000175000001440000004360214206145246021542 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2009 by Arnold Krille * Copyright (C) 2015 by Hector Martin * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "saffire_pro26.h" #include "focusrite_eap.h" #include "libutil/ByteSwap.h" #include #include namespace Dice { namespace Focusrite { void SaffirePro26::SaffirePro26EAP::setupSources_low() { addSource("SPDIF/In", 4, 4, eRS_AES, 1); addSource("ADAT/In", 8, 8, eRS_ADAT, 1); addSource("Anlg/In", 0, 6, eRS_InS0, 1); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 8, eRS_ARX0, 1); addSource("Mute", 0, 1, eRS_Muted); } void SaffirePro26::SaffirePro26EAP::setupDestinations_low() { addDestination("SPDIF/Out", 6, 2, eRD_AES, 1); addDestination("Line/Out", 0, 6, eRD_InS0, 1); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 10, eRD_ATX0, 1); addDestination("1394/Out", 0, 8, eRD_ATX1, 11); // Is a Mute destination useful ? // addDestination("Mute", 0, 1, eRD_Muted); } void SaffirePro26::SaffirePro26EAP::setupSources_mid() { addSource("SPDIF/In", 4, 4, eRS_AES, 1); addSource("ADAT/In", 0, 4, eRS_ADAT, 1); addSource("Anlg/In", 0, 6, eRS_InS0, 1); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 8, eRS_ARX0, 1); addSource("Mute", 0, 1, eRS_Muted); } void SaffirePro26::SaffirePro26EAP::setupDestinations_mid() { addDestination("SPDIF/Out", 6, 2, eRD_AES, 1); addDestination("Line/Out", 0, 6, eRD_InS0, 1); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 10, eRD_ATX0, 1); addDestination("1394/Out", 0, 4, eRD_ATX1, 11); // Is a Mute destination useful ? // addDestination("Mute", 0, 1, eRD_Muted); } void SaffirePro26::SaffirePro26EAP::setupSources_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 26\n"); } void SaffirePro26::SaffirePro26EAP::setupDestinations_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 26\n"); } /** * The default configurations for the Saffire Pro 26 router. * For coherence with hardware, destinations must follow a specific ordering * Front LEDs are connected to the first six eRD_ATX0 entries */ void SaffirePro26::SaffirePro26EAP::setupDefaultRouterConfig_low() { unsigned int i; // the 1394 stream receivers except the two "loops" one for (i=0; i<6; i++) { addRoute(eRS_InS0, i, eRD_ATX0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i+4, eRD_ATX0, i+6); } for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_ATX1, i); } // The audio ports // Ensure that audio port are not muted for (i=0; i<6; i++) { addRoute(eRS_ARX0, i%2, eRD_InS0, i); } // the SPDIF receiver for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_AES, i+6); } // the "loops" 1394 stream receivers for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_ATX0, i+8); } // the Mixer inputs for (i=0; i<6; i++) { addRoute(eRS_InS0, i, eRD_Mixer0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i+4, eRD_Mixer0, i+6); } for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_Mixer0, i+8); } for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_Mixer1, i); } // The two mute destinations // FIXME: does this do anything useful? for (i=0; i<2; i++) { addRoute(eRS_Mixer, i, eRD_Muted, 0); } } /** * There must be 40 (?) destinations at mid samplerate * Front LEDs are connected to the first six eRD_ATX0 entries */ void SaffirePro26::SaffirePro26EAP::setupDefaultRouterConfig_mid() { unsigned int i; // the 1394 stream receivers except the two "loops" one for (i=0; i<6; i++) { addRoute(eRS_InS0, i, eRD_ATX0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i+4, eRD_ATX0, i+6); } for (i=0; i<4; i++) { addRoute(eRS_ADAT, i, eRD_ATX1, i); } // The audio ports // Ensure that audio port are not muted for (i=0; i<6; i++) { addRoute(eRS_ARX0, i%2, eRD_InS0, i); } // the SPDIF receiver for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_AES, i+6); } // the "loops" 1394 stream receivers for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_ATX0, i+8); } // the Mixer inputs for (i=0; i<6; i++) { addRoute(eRS_InS0, i, eRD_Mixer0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i+4, eRD_Mixer0, i+6); } for (i=0; i<4; i++) { addRoute(eRS_ADAT, i, eRD_Mixer0, i+8); } for (i=0; i<4; i++) { addRoute(eRS_Muted, i, eRD_Mixer0, i+12); } for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_Mixer1, i); } // The two mute destinations // FIXME: does this do anything useful? for (i=0; i<2; i++) { addRoute(eRS_Mixer, i, eRD_Muted, 0); } } /** * High rate not supported */ void SaffirePro26::SaffirePro26EAP::setupDefaultRouterConfig_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 26\n"); } /** * Pro 24 Monitor section */ SaffirePro26::SaffirePro26EAP::MonitorSection::MonitorSection(Dice::Focusrite::FocusriteEAP* eap, std::string name) : Control::Container(eap, name) , m_eap(eap) { // Global Mute control Control::Container* grp_globalmute = new Control::Container(m_eap, "GlobalMute"); addElement(grp_globalmute); FocusriteEAP::Switch* mute = new FocusriteEAP::Switch(m_eap, "State", SAFFIRE_PRO26_REGISTER_APP_GLOBAL_MUTE_SWITCH, FOCUSRITE_EAP_GLOBAL_MUTE_SWITCH_VALUE, SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO26_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH); grp_globalmute->addElement(mute); // Global Dim control Control::Container* grp_globaldim = new Control::Container(m_eap, "GlobalDim"); addElement(grp_globaldim); FocusriteEAP::Switch* dim = new FocusriteEAP::Switch(m_eap, "State", SAFFIRE_PRO26_REGISTER_APP_GLOBAL_DIM_SWITCH, FOCUSRITE_EAP_GLOBAL_DIM_SWITCH_VALUE, SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO26_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH); grp_globaldim->addElement(dim); FocusriteEAP::Poti* dimlevel = new FocusriteEAP::Poti(m_eap, "Level", SAFFIRE_PRO26_REGISTER_APP_GLOBAL_DIM_VOLUME, SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO26_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_globaldim->addElement(dimlevel); // The mono switches don't seem to do anything on this device (and are not // implemented in MixControl) // Independent control of each line/out Control::Container* grp_perchannel = new Control::Container(m_eap, "LineOut"); addElement(grp_perchannel); FocusriteEAP::VolumeControl* vol; // per Line/Out monitoring FocusriteEAP::Switch* s; for (unsigned int i=0; iaddElement(s); stream.str(std::string()); stream << "UnActivate" << i*2+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO26_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_2, SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO26_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); // per Line/Out mute/unmute stream.str(std::string()); stream << "Mute" << i*2+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO26_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_3, SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO26_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); stream.str(std::string()); stream << "Mute" << i*2+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO26_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_4, SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO26_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); // per Line/Out global mute activation/unactivation stream.str(std::string()); stream << "GMute" << 2*i+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO26_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_MUTE_SHIFT+2*i), SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO26_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); stream.str(std::string()); stream << "GMute" << 2*i+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO26_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_MUTE_SHIFT+2*i+1), SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO26_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); // per Line/Out global dim activation/unactivation stream.str(std::string()); stream << "GDim" << 2*i+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO26_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_DIM_SHIFT+2*i), SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO26_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); stream.str(std::string()); stream << "GDim" << 2*i+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO26_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_DIM_SHIFT+2*i+1), SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO26_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); // per Line/Out volume control stream.str(std::string()); stream << "Volume" << i*2+1; vol = new FocusriteEAP::VolumeControl(m_eap, stream.str(), SAFFIRE_PRO26_REGISTER_APP_LINEOUT_MONITOR_VOLUME +i*sizeof(quadlet_t), FOCUSRITE_EAP_LINEOUT_VOLUME_SET_1, SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO26_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(vol); stream.str(std::string()); stream << "Volume" << i*2+2; vol = new FocusriteEAP::VolumeControl(m_eap, stream.str(), SAFFIRE_PRO26_REGISTER_APP_LINEOUT_MONITOR_VOLUME +i*sizeof(quadlet_t), FOCUSRITE_EAP_LINEOUT_VOLUME_SET_2, SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO26_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(vol); } // The Saffire PRO 26 does not have line/inst/gain controls. Instead it has // dedicated hardware switches for inputs 1/2, auto selection for inputs 3/4, // and no controls for inputs 5/6. // There is also support for ADAT-as-SPDIF, but no control is necessary. // SPDIF inputs appear on their own dedicated routing source when present // (labeled SPDIF 3/4). } SaffirePro26::SaffirePro26( DeviceManager& d, ffado_smartptr( configRom )) : Dice::Device(d , configRom) { debugOutput(DEBUG_LEVEL_VERBOSE, "Created Dice::Focusrite::SaffirePro26 (NodeID %d)\n", getConfigRom().getNodeId()); } SaffirePro26::~SaffirePro26() { //debugOutput(DEBUG_LEVEL_VERBOSE, "Deleting the saffirePro26\n"); /// I wonder whether we should really save only on clean exits or also each time a setting is // changed. Or should we provide a function (and thus gui-button) to save the state of the // device? getEAP()->storeFlashConfig(); } bool SaffirePro26::discover() { if (Dice::Device::discover()) { fb_quadlet_t* version = (fb_quadlet_t *)calloc(2, sizeof(fb_quadlet_t)); getEAP()->readRegBlock(Dice::EAP::eRT_Application, SAFFIRE_PRO26_REGISTER_APP_VERSION, version, 1*sizeof(fb_quadlet_t)); // Only known firmware for Saffire Pro 26 devices so far is 0x10000. if (version[0] != 0x10000) { fprintf(stderr, "This is a Focusrite Saffire Pro26 but not the right firmware. Better stop here before something goes wrong.\n"); fprintf(stderr, "This device has firmware 0x%x while we only know about version 0x%x.\n", version[0], 0x10000); return false; } // FIXME: What is the purpose of the following commented lines at this point ? //getEAP()->readRegBlock(Dice::EAP::eRT_Command, 0x00, tmp, 2*sizeof(fb_quadlet_t)); // DEBUG //hexDumpQuadlets(tmp, 2); // DEBUG FocusriteEAP* eap = dynamic_cast(getEAP()); SaffirePro26EAP::MonitorSection* monitor = new SaffirePro26EAP::MonitorSection(eap, "Monitoring"); getEAP()->addElement(monitor); return true; } return false; } void SaffirePro26::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a Dice::Focusrite::SaffirePro26\n"); Dice::Device::showDevice(); } Dice::EAP* SaffirePro26::createEAP() { return new SaffirePro26EAP(*this); } bool SaffirePro26::setNickname( std::string name ) { char nickname[SAFFIRE_PRO26_APP_NICK_NAME_SIZE+1]; // The device has room for SAFFIRE_PRO26_APP_NICK_NAME_SIZE characters. // Erase supplementary characters or fill-in with NULL character if necessary strncpy(nickname, name.c_str(), SAFFIRE_PRO26_APP_NICK_NAME_SIZE); // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)nickname, SAFFIRE_PRO26_APP_NICK_NAME_SIZE/4); #endif if (!getEAP()->writeRegBlock(Dice::EAP::eRT_Application, SAFFIRE_PRO26_REGISTER_APP_NICK_NAME, (quadlet_t*)nickname, SAFFIRE_PRO26_APP_NICK_NAME_SIZE)) { debugError("Could not write nickname string \n"); return false; } return true; } std::string SaffirePro26::getNickname() { char nickname[SAFFIRE_PRO26_APP_NICK_NAME_SIZE+1]; if (!getEAP()->readRegBlock(Dice::EAP::eRT_Application, SAFFIRE_PRO26_REGISTER_APP_NICK_NAME, (quadlet_t*)nickname, SAFFIRE_PRO26_APP_NICK_NAME_SIZE)){ debugError("Could not read nickname string \n"); return std::string("(unknown)"); } // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)nickname, SAFFIRE_PRO26_APP_NICK_NAME_SIZE/4); #endif // The device supplies at most SAFFIRE_PRO26_APP_NICK_NAME_SIZE characters. Ensure the string is // NULL terminated. nickname[SAFFIRE_PRO26_APP_NICK_NAME_SIZE] = 0; return std::string(nickname); } } } // vim: et libffado-2.4.5/src/dice/focusrite/saffire_pro26.h0000644000175000001440000000751114206145246021206 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2009 by Arnold Krille * Copyright (C) 2015 by Hector Martin * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DICE_FOCUSRITE_SAFFIRE_PRO26_H #define DICE_FOCUSRITE_SAFFIRE_PRO26_H #include "dice/dice_avdevice.h" #include "libieee1394/configrom.h" #include "focusrite_eap.h" /** * Saffire Pro26 application space */ // Versioning registers #define SAFFIRE_PRO26_REGISTER_APP_VERSION 0x00 #define SAFFIRE_PRO26_REGISTER_APP_RELEASE 0x04 #define SAFFIRE_PRO26_REGISTER_APP_BUILDNR 0x08 // Nickname register #define SAFFIRE_PRO26_REGISTER_APP_NICK_NAME 0x44 // NOTE: in bytes #define SAFFIRE_PRO26_APP_NICK_NAME_SIZE 16 // Global monitor registers (application space) #define SAFFIRE_PRO26_REGISTER_APP_GLOBAL_MUTE_SWITCH 0x10 #define SAFFIRE_PRO26_REGISTER_APP_GLOBAL_DIM_SWITCH 0x14 #define SAFFIRE_PRO26_REGISTER_APP_GLOBAL_DIM_VOLUME 0x58 #define SAFFIRE_PRO26_REGISTER_APP_GLOBAL_MONITOR_VOLUME 0x28 // Per line/out monitor volume and switches: registers are expected to be one after the other // each register controlling two output lines // The whole number of physical analog output is thus 2*SAFFIRE_PRO26_APP_STEREO_LINEOUT_SIZE #define SAFFIRE_PRO26_APP_STEREO_LINEOUT_SIZE 3 // Volume and switch monitor register #define SAFFIRE_PRO26_REGISTER_APP_LINEOUT_MONITOR_VOLUME 0x18 #define SAFFIRE_PRO26_REGISTER_APP_LINEOUT_MONITOR_SWITCH 0x2C // Switch control (per line/out mute, dim and mono) #define SAFFIRE_PRO26_REGISTER_APP_LINEOUT_SWITCH_CONTROL 0x40 // Message set // The location of the message register and the values for each setting #define SAFFIRE_PRO26_REGISTER_APP_MESSAGE_SET 0x0c #define SAFFIRE_PRO26_MESSAGE_SET_NO_MESSAGE 0 #define SAFFIRE_PRO26_MESSAGE_SET_LINEOUT_MONITOR_VOLUME 1 #define SAFFIRE_PRO26_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH 2 #define SAFFIRE_PRO26_MESSAGE_SET_LINEOUT_SWITCH_CONTROL 3 #define SAFFIRE_PRO26_MESSAGE_SET_INSTLINE 4 #define SAFFIRE_PRO26_MESSAGE_SET_MESSAGE_END 5 namespace Dice { namespace Focusrite { class SaffirePro26 : public Dice::Device { public: SaffirePro26( DeviceManager& d, ffado_smartptr( configRom )); ~SaffirePro26(); bool discover(); void showDevice(); bool canChangeNickname() { return true; } bool setNickname( std::string name ); std::string getNickname(); private: class SaffirePro26EAP : public FocusriteEAP { public: SaffirePro26EAP(Dice::Device& dev) : FocusriteEAP(dev) { } void setupSources_low(); void setupDestinations_low(); void setupSources_mid(); void setupDestinations_mid(); void setupSources_high(); void setupDestinations_high(); void setupDefaultRouterConfig_low(); void setupDefaultRouterConfig_mid(); void setupDefaultRouterConfig_high(); class MonitorSection : public Control::Container { public: MonitorSection(Dice::Focusrite::FocusriteEAP*, std::string); private: Dice::Focusrite::FocusriteEAP* m_eap; }; }; Dice::EAP* createEAP(); }; } } #endif // vim: et libffado-2.4.5/src/dice/focusrite/saffire_pro40.cpp0000644000175000001440000005064214206145246021540 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "saffire_pro40.h" #include "focusrite_eap.h" #include "libutil/ByteSwap.h" #include namespace Dice { namespace Focusrite { // ADAT as SPDIF state bool SaffirePro40::SaffirePro40EAP::getADATSPDIF_state() { quadlet_t state_tmp; bool adatspdif = false; if (!readReg(Dice::EAP::eRT_Application, SAFFIRE_PRO40_REGISTER_APP_ADATSPDIF_SWITCH_CONTROL, &state_tmp)) { debugWarning("Could not read ADAT/SPDIF switch register: assume ADAT \n"); } else { adatspdif = (state_tmp&FOCUSRITE_EAP_ADATSPDIF_SWITCH_VALUE)?true:false; } return adatspdif; } // // Under 48kHz Saffire pro 40 has // - 8 analogic inputs (mic/line) // - 8 ADAT inputs or 2 optical SPDIF inputs // - 2 SPDIF inputs // - 20 ieee1394 inputs // - 18 mixer inputs // // - 10 analogic outputs // - 8 ADAT outputs or 0 optical SPDIF outputs // - 2 SPDIF outputs // - 20 ieee1394 outputs // - 16 mixer outputs // void SaffirePro40::SaffirePro40EAP::setupSources_low() { bool adatspdif = getADATSPDIF_state(); addSource("SPDIF/In", 0, 2, eRS_AES, 1); if (!adatspdif) { addSource("ADAT/In", 0, 8, eRS_ADAT, 1); } else { addSource("SPDIF/In", 4, 2, eRS_AES, 3); } addSource("Mic/Lin/Inst", 0, 2, eRS_InS1, 1); addSource("Mic/Lin/In", 2, 6, eRS_InS1, 3); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 12, eRS_ARX0, 1); addSource("1394/In", 0, 8, eRS_ARX1, 13); addSource("Mute", 0, 1, eRS_Muted); } void SaffirePro40::SaffirePro40EAP::setupDestinations_low() { bool adatspdif = getADATSPDIF_state(); addDestination("SPDIF/Out", 0, 2, eRD_AES, 1); if (!adatspdif) { addDestination("ADAT/Out", 0, 8, eRD_ADAT, 1); } addDestination("Line/Out", 0, 2, eRD_InS0, 1); addDestination("Line/Out", 0, 8, eRD_InS1, 3); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 10, eRD_ATX0, 1); addDestination("1394/Out", 0, 8, eRD_ATX1, 11); addDestination("Loop", 8, 2, eRD_ATX1, 1); // Is a Mute destination useful ? // addDestination("Mute", 0, 1, eRD_Muted); } // // Under 96kHz Saffire pro 40 has // - 8 analogic inputs (mic/line) // - 4 ADAT inputs // - 2 SPDIF inputs // - 16 ieee1394 inputs // - 18 mixer inputs // // - 10 analogic outputs // - 4 ADAT outputs // - 2 SPDIF outputs // - 16 ieee1394 outputs // - 16 mixer outputs // void SaffirePro40::SaffirePro40EAP::setupSources_mid() { bool adatspdif = getADATSPDIF_state(); addSource("SPDIF/In", 0, 2, eRS_AES, 1); if (!adatspdif) { addSource("ADAT/In", 0, 4, eRS_ADAT, 1); } else { addSource("SPDIF/In", 4, 2, eRS_AES, 3); } addSource("Mic/Lin/Inst", 0, 2, eRS_InS1, 1); addSource("Mic/Lin/In", 2, 6, eRS_InS1, 3); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 16, eRS_ARX0, 1); addSource("Mute", 0, 1, eRS_Muted); } void SaffirePro40::SaffirePro40EAP::setupDestinations_mid() { bool adatspdif = getADATSPDIF_state(); addDestination("SPDIF/Out", 0, 2, eRD_AES, 1); if (!adatspdif) { addDestination("ADAT/Out", 0, 4, eRD_ADAT, 1); } addDestination("Line/Out", 0, 2, eRD_InS0, 1); addDestination("Line/Out", 0, 8, eRD_InS1, 3); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 14, eRD_ATX0, 1); addDestination("Loop", 14, 2, eRD_ATX0, 1); // Is a Mute destination useful ? // addDestination("Mute", 0, 1, eRD_Muted); } // // 192 kHz is not supported // void SaffirePro40::SaffirePro40EAP::setupSources_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 40\n"); } void SaffirePro40::SaffirePro40EAP::setupDestinations_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 40\n"); } /** * The default configurations for the Saffire Pro 40 router. * For coherence with hardware, destinations must follow a specific ordering * There must be 60 destinations at low samplerate * Front LEDs are connected to the first eight router entries */ void SaffirePro40::SaffirePro40EAP::setupDefaultRouterConfig_low() { unsigned int i; // the 1394 stream receivers except the two "loops" one for (i=0; i<8; i++) { addRoute(eRS_InS1, i, eRD_ATX0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i, eRD_ATX0, i+8); } for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_ATX1, i); } // The audio ports // Ensure that audio port are not muted for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_InS0, i); } for (i=0; i<8; i++) { addRoute(eRS_ARX0, i%2, eRD_InS1, i); } // the SPDIF receiver for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_AES, i); } // the ADAT receiver for (i=0; i<8; i++) { addRoute(eRS_Muted, 0, eRD_ADAT, i); } // the "loops" 1394 stream receivers for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_ATX1, i+8); } // the Mixer inputs for (i=0; i<8; i++) { addRoute(eRS_InS1, i, eRD_Mixer0, i); } for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_Mixer0, i+8); } for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_Mixer1, i); } // The two mute destinations for (i=0; i<2; i++) { addRoute(eRS_Mixer, i, eRD_Muted, 0); } } /** * There must be 52 destinations at mid samplerate * Front LEDs are connected to the first eight router entries */ void SaffirePro40::SaffirePro40EAP::setupDefaultRouterConfig_mid() { unsigned int i; // the 1394 stream receivers except the two "loops" one for (i=0; i<8; i++) { addRoute(eRS_InS1, i, eRD_ATX0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i, eRD_ATX0, i+8); } for (i=0; i<4; i++) { addRoute(eRS_ADAT, i, eRD_ATX0, i+10); } // The audio ports // Ensure that audio port are not muted for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_InS0, i); } for (i=0; i<8; i++) { addRoute(eRS_ARX0, i%2, eRD_InS1, i); } // the SPDIF receiver for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_AES, i); } // the ADAT receiver for (i=0; i<4; i++) { addRoute(eRS_Muted, 0, eRD_ADAT, i); } // the "loops" 1394 stream receivers for (i=0; i<2; i++) { addRoute(eRS_Muted, 0, eRD_ATX0, i+14); } // the Mixer inputs for (i=0; i<8; i++) { addRoute(eRS_InS1, i, eRD_Mixer0, i); } for (i=0; i<4; i++) { addRoute(eRS_ADAT, i, eRD_Mixer0, i+8); } for (i=0; i<4; i++) { addRoute(eRS_Muted, 0, eRD_Mixer0, i+12); } for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_Mixer1, i); } // The two mute destinations for (i=0; i<2; i++) { addRoute(eRS_Mixer, i, eRD_Muted, 0); } } /** * High rate not supported */ void SaffirePro40::SaffirePro40EAP::setupDefaultRouterConfig_high() { printMessage("High (192 kHz) sample rate not supported by Saffire Pro 40\n"); } /** * Pro 40 Monitor section */ // Subclassed switch SaffirePro40::SaffirePro40EAP::Switch::Switch(Dice::Focusrite::FocusriteEAP* eap, std::string name, size_t offset, int activevalue, size_t msgset_offset, int msgset_value) : FocusriteEAP::Switch(eap, name, offset, activevalue, msgset_offset, msgset_value) , m_eap(eap) , m_name(name) , m_offset(offset) , m_activevalue(activevalue) , m_msgset_offset(msgset_offset) , m_msgset_value(msgset_value) { debugOutput( DEBUG_LEVEL_VERBOSE, "Create Pro 40 Switch %s)\n", m_name.c_str()); } bool SaffirePro40::SaffirePro40EAP::Switch::select(bool n) { bool is_selected = FocusriteEAP::Switch::select(n); m_eap->update(); return is_selected; } SaffirePro40::SaffirePro40EAP::MonitorSection::MonitorSection(Dice::Focusrite::FocusriteEAP* eap, std::string name) : Control::Container(eap, name) , m_eap(eap) { // Global Mute control Control::Container* grp_globalmute = new Control::Container(m_eap, "GlobalMute"); addElement(grp_globalmute); FocusriteEAP::Switch* mute = new FocusriteEAP::Switch(m_eap, "State", SAFFIRE_PRO40_REGISTER_APP_GLOBAL_MUTE_SWITCH, FOCUSRITE_EAP_GLOBAL_MUTE_SWITCH_VALUE, SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH); grp_globalmute->addElement(mute); // ADAT as optical SPDIF switch control Control::Container* grp_adatspdif = new Control::Container(m_eap, "AdatSpdif"); addElement(grp_adatspdif); SaffirePro40::SaffirePro40EAP::Switch* adatspdif = new SaffirePro40::SaffirePro40EAP::Switch( m_eap, "State", SAFFIRE_PRO40_REGISTER_APP_ADATSPDIF_SWITCH_CONTROL, FOCUSRITE_EAP_ADATSPDIF_SWITCH_VALUE, SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_INSTLINE); grp_adatspdif->addElement(adatspdif); // Global Dim control Control::Container* grp_globaldim = new Control::Container(m_eap, "GlobalDim"); addElement(grp_globaldim); FocusriteEAP::Switch* dim = new FocusriteEAP::Switch(m_eap, "State", SAFFIRE_PRO40_REGISTER_APP_GLOBAL_DIM_SWITCH, FOCUSRITE_EAP_GLOBAL_DIM_SWITCH_VALUE, SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH); grp_globaldim->addElement(dim); FocusriteEAP::Poti* dimlevel = new FocusriteEAP::Poti(m_eap, "Level", SAFFIRE_PRO40_REGISTER_APP_GLOBAL_DIM_VOLUME, SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_globaldim->addElement(dimlevel); FocusriteEAP::Switch* s; // Mono/stereo switch Control::Container* grp_mono = new Control::Container(m_eap, "Mono"); addElement(grp_mono); for (unsigned int i=0; iaddElement(s); } // Independent control of each line/out Control::Container* grp_perchannel = new Control::Container(m_eap, "LineOut"); addElement(grp_perchannel); FocusriteEAP::VolumeControl* vol; // per Line/Out monitoring for (unsigned int i=0; iaddElement(s); stream.str(std::string()); stream << "UnActivate" << i*2+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO40_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_2, SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); // per Line/Out mute/unmute stream.str(std::string()); stream << "Mute" << i*2+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO40_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_3, SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); stream.str(std::string()); stream << "Mute" << i*2+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO40_REGISTER_APP_LINEOUT_MONITOR_SWITCH+i*sizeof(quadlet_t), FOCUSRITE_EAP_SWITCH_BIT_4, SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(s); // per Line/Out global mute activation/unactivation stream.str(std::string()); stream << "GMute" << 2*i+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO40_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_MUTE_SHIFT+2*i), SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); stream.str(std::string()); stream << "GMute" << 2*i+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO40_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_MUTE_SHIFT+2*i+1), SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); // per Line/Out global dim activation/unactivation stream.str(std::string()); stream << "GDim" << 2*i+1; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO40_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_DIM_SHIFT+2*i), SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); stream.str(std::string()); stream << "GDim" << 2*i+2; s = new FocusriteEAP::Switch(m_eap, stream.str(), SAFFIRE_PRO40_REGISTER_APP_LINEOUT_SWITCH_CONTROL, FOCUSRITE_EAP_SWITCH_CONTROL_VALUE <<(FOCUSRITE_EAP_SWITCH_CONTROL_DIM_SHIFT+2*i+1), SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_LINEOUT_SWITCH_CONTROL); grp_perchannel->addElement(s); // per Line/Out volume control stream.str(std::string()); stream << "Volume" << i*2+1; vol = new FocusriteEAP::VolumeControl(m_eap, stream.str(), SAFFIRE_PRO40_REGISTER_APP_LINEOUT_MONITOR_VOLUME +i*sizeof(quadlet_t), FOCUSRITE_EAP_LINEOUT_VOLUME_SET_1, SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(vol); stream.str(std::string()); stream << "Volume" << i*2+2; vol = new FocusriteEAP::VolumeControl(m_eap, stream.str(), SAFFIRE_PRO40_REGISTER_APP_LINEOUT_MONITOR_VOLUME +i*sizeof(quadlet_t), FOCUSRITE_EAP_LINEOUT_VOLUME_SET_2, SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET, SAFFIRE_PRO40_MESSAGE_SET_LINEOUT_MONITOR_VOLUME); grp_perchannel->addElement(vol); } } /** Device */ SaffirePro40::SaffirePro40( DeviceManager& d, ffado_smartptr( configRom )) : Dice::Device( d , configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Dice::Focusrite::SaffirePro40 (NodeID %d)\n", getConfigRom().getNodeId() ); } SaffirePro40::~SaffirePro40() { getEAP()->storeFlashConfig(); } bool SaffirePro40::discover() { if (Dice::Device::discover()) { FocusriteEAP* eap = dynamic_cast(getEAP()); SaffirePro40EAP::MonitorSection* monitor = new SaffirePro40EAP::MonitorSection(eap, "Monitoring"); eap->addElement(monitor); return true; } return false; } void SaffirePro40::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a Dice::Focusrite::SaffirePro40\n"); Dice::Device::showDevice(); } Dice::EAP* SaffirePro40::createEAP() { return new SaffirePro40EAP(*this); } /** * Nickname */ bool SaffirePro40::setNickname(std::string name) { char nickname[SAFFIRE_PRO40_APP_NICK_NAME_SIZE+1]; // The device has room for SAFFIRE_PRO40_APP_NICK_NAME_SIZE characters. // Erase supplementary characters or fill-in with NULL character if necessary strncpy(nickname, name.c_str(), SAFFIRE_PRO40_APP_NICK_NAME_SIZE); // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)nickname, SAFFIRE_PRO40_APP_NICK_NAME_SIZE/4); #endif if (!getEAP()->writeRegBlock(Dice::EAP::eRT_Application, SAFFIRE_PRO40_REGISTER_APP_NICK_NAME, (quadlet_t*)nickname, SAFFIRE_PRO40_APP_NICK_NAME_SIZE)) { debugError("Could not write nickname string \n"); return false; } return true; } std::string SaffirePro40::getNickname() { char nickname[SAFFIRE_PRO40_APP_NICK_NAME_SIZE+1]; if (!getEAP()->readRegBlock(Dice::EAP::eRT_Application, SAFFIRE_PRO40_REGISTER_APP_NICK_NAME, (quadlet_t*)nickname, SAFFIRE_PRO40_APP_NICK_NAME_SIZE)){ debugError("Could not read nickname string \n"); return std::string("(unknown)"); } // Strings from the device are always little-endian, // so byteswap for big-endian machines #if __BYTE_ORDER == __BIG_ENDIAN byteSwapBlock((quadlet_t *)nickname, SAFFIRE_PRO40_APP_NICK_NAME_SIZE/4); #endif // The device supplies at most SAFFIRE_PRO40_APP_NICK_NAME_SIZE characters. Ensure the string is // NULL terminated. nickname[SAFFIRE_PRO40_APP_NICK_NAME_SIZE] = 0; return std::string(nickname); } } } // vim: et libffado-2.4.5/src/dice/focusrite/saffire_pro40.h0000644000175000001440000001122414206145246021176 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DICE_FOCUSRITE_SAFFIRE_PRO40_H #define DICE_FOCUSRITE_SAFFIRE_PRO40_H #include "dice/dice_avdevice.h" #include "libieee1394/configrom.h" #include "focusrite_eap.h" /** * Saffire Pro40 application space */ // Versioning registers #define SAFFIRE_PRO40_REGISTER_APP_VERSION 0x00 #define SAFFIRE_PRO40_REGISTER_APP_RELEASE 0x04 #define SAFFIRE_PRO40_REGISTER_APP_BUILDNR 0x08 // Nickname register #define SAFFIRE_PRO40_REGISTER_APP_NICK_NAME 0x44 // NOTE: in bytes #define SAFFIRE_PRO40_APP_NICK_NAME_SIZE 16 // Global monitor registers (application space) #define SAFFIRE_PRO40_REGISTER_APP_GLOBAL_MUTE_SWITCH 0x0C #define SAFFIRE_PRO40_REGISTER_APP_GLOBAL_DIM_SWITCH 0x10 #define SAFFIRE_PRO40_REGISTER_APP_GLOBAL_DIM_VOLUME 0x58 #define SAFFIRE_PRO40_REGISTER_APP_GLOBAL_MONITOR_VOLUME 0x54 // Per line/out monitor volume and switches: registers are expected to be one after the other // each register controlling two output lines // The whole number of physical analog output is thus 2*SAFFIRE_PRO40_APP_STEREO_LINEOUT_SIZE #define SAFFIRE_PRO40_APP_STEREO_LINEOUT_SIZE 5 // Volume and switch monitor register #define SAFFIRE_PRO40_REGISTER_APP_LINEOUT_MONITOR_VOLUME 0x14 #define SAFFIRE_PRO40_REGISTER_APP_LINEOUT_MONITOR_SWITCH 0x28 // Switch controls // per line/out mute, dim and mono #define SAFFIRE_PRO40_REGISTER_APP_LINEOUT_SWITCH_CONTROL 0x3C // ADAT as SPDIF #define SAFFIRE_PRO40_REGISTER_APP_ADATSPDIF_SWITCH_CONTROL 0x5C // Message set // The location of the message register and the values for each setting #define SAFFIRE_PRO40_REGISTER_APP_MESSAGE_SET 0x68 #define SAFFIRE_PRO40_MESSAGE_SET_NO_MESSAGE 0 #define SAFFIRE_PRO40_MESSAGE_SET_LINEOUT_MONITOR_VOLUME 1 #define SAFFIRE_PRO40_MESSAGE_SET_GLOBAL_DIM_MUTE_SWITCH 2 #define SAFFIRE_PRO40_MESSAGE_SET_LINEOUT_SWITCH_CONTROL 3 #define SAFFIRE_PRO40_MESSAGE_SET_INSTLINE 4 #define SAFFIRE_PRO40_MESSAGE_SET_MESSAGE_END 5 // Standalone #define SAFFIRE_PRO40_REGISTER_STANDALONE_SWITCH 0x60 #define SAFFIRE_PRO40_REGISTER_STANDALONE_SRC_SMPL 0x64 // Automatically stored namespace Dice { namespace Focusrite { class SaffirePro40 : public Dice::Device { public: SaffirePro40( DeviceManager& d, ffado_smartptr( configRom )); virtual ~SaffirePro40(); bool discover(); virtual void showDevice(); bool canChangeNickname() { return true; } bool setNickname(std::string); std::string getNickname(); private: class SaffirePro40EAP : public FocusriteEAP { private: // Adat as Spdif register state required to adapt the router settings bool getADATSPDIF_state(); public: SaffirePro40EAP(Dice::Device& dev) : FocusriteEAP(dev) { } void setupSources_low(); void setupDestinations_low(); void setupSources_mid(); void setupDestinations_mid(); void setupSources_high(); void setupDestinations_high(); void setupDefaultRouterConfig_low(); void setupDefaultRouterConfig_mid(); void setupDefaultRouterConfig_high(); // Pro 40 requires a specific switch which updates the view of the routing // (essentially the ADAT/SPDIF switch) class Switch : public FocusriteEAP::Switch { public: Switch(Dice::Focusrite::FocusriteEAP*, std::string, size_t, int, size_t, int); bool select(bool); private: Dice::Focusrite::FocusriteEAP* m_eap; std::string m_name; size_t m_offset; int m_activevalue; size_t m_msgset_offset; int m_msgset_value; }; class MonitorSection : public Control::Container { public: MonitorSection(Dice::Focusrite::FocusriteEAP*, std::string); private: Dice::Focusrite::FocusriteEAP* m_eap; }; }; Dice::EAP* createEAP(); }; } } #endif // vim: et libffado-2.4.5/src/dice/maudio/0000755000175000001440000000000014206145612015632 5ustar jwoitheuserslibffado-2.4.5/src/dice/maudio/profire_2626.cpp0000644000175000001440000003243514206145246020475 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2012 by Philippe Carriere * Copyright (C) 2012 by Jano Svitok * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "profire_2626.h" #include namespace Dice { namespace Maudio { Profire2626::Profire2626EAP::Profire2626EAP(Dice::Device & dev) : Dice::EAP(dev) { } // // Profire 2626 has // - 8 mic/line inputs // - 2x8 ADAT inputs // - 2 coaxial SPDIF inputs // - 32 ieee1394 inputs // - 18 mixer inputs // - 1 MIDI input // // - 8 line outputs (first two pairs are routed to 2 headphone jacks // - 2x8 ADAT outputs // - 2 coaxial SPDIF outputs // - 32 ieee1394 outputs // - 16 mixer outputs // - 1 MIDI output // void Profire2626::Profire2626EAP::setupSources_low() { addSource("Mic/Line/In", 0, 8, eRS_InS1, 1); addSource("ADAT A/In", 0, 8, eRS_ADAT, 1); addSource("ADAT B/In", 9, 8, eRS_ADAT, 1); addSource("SPDIF/In", 14, 2, eRS_AES, 1); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 10, eRS_ARX0, 1); addSource("1394/In", 0, 16, eRS_ARX1, 11); addSource("Mute", 0, 1, eRS_Muted); } void Profire2626::Profire2626EAP::setupDestinations_low() { addDestination("Line/Out", 0, 8, eRD_InS1, 1); addDestination("ADAT A/Out", 0, 8, eRD_ADAT, 1); addDestination("ADAT B/Out", 8, 8, eRD_ADAT, 1); addDestination("SPDIF/Out", 0, 2, eRD_AES, 1); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 10, eRD_ATX0, 1); addDestination("1394/Out", 0, 16, eRD_ATX1, 11); addDestination("Mute", 0, 1, eRD_Muted); } void Profire2626::Profire2626EAP::setupSources_mid() { addSource("Mic/Line/In", 0, 8, eRS_InS1, 1); addSource("ADAT A/In", 0, 4, eRS_ADAT, 1); addSource("ADAT B/In", 4, 4, eRS_ADAT, 1); addSource("SPDIF/In", 14, 2, eRS_AES, 1); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 10, eRS_ARX0, 1); addSource("1394/In", 0, 8, eRS_ARX1, 11); addSource("Mute", 0, 1, eRS_Muted); } void Profire2626::Profire2626EAP::setupDestinations_mid() { addDestination("Line/Out", 0, 8, eRD_InS1, 1); addDestination("ADAT A/Out", 0, 4, eRD_ADAT, 1); addDestination("ADAT B/Out", 4, 4, eRD_ADAT, 1); addDestination("SPDIF/Out", 0, 2, eRD_AES, 1); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 10, eRD_ATX0, 1); addDestination("1394/Out", 0, 8, eRD_ATX1, 11); addDestination("Mute", 0, 1, eRD_Muted); } void Profire2626::Profire2626EAP::setupSources_high() { addSource("Mic/Line/In", 0, 8, eRS_InS1, 1); addSource("ADAT A/In", 0, 2, eRS_ADAT, 1); addSource("ADAT B/In", 2, 2, eRS_ADAT, 1); addSource("SPDIF/In", 14, 2, eRS_AES, 1); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 8, eRS_ARX0, 1); addSource("1394/In", 0, 6, eRS_ARX1, 9); addSource("Mute", 0, 1, eRS_Muted); } void Profire2626::Profire2626EAP::setupDestinations_high() { addDestination("Line/Out", 0, 8, eRD_InS1, 1); addDestination("ADAT A/Out", 0, 2, eRD_ADAT, 1); addDestination("ADAT B/Out", 2, 2, eRD_ADAT, 1); addDestination("SPDIF/Out", 0, 2, eRD_AES, 1); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 8, eRD_ATX0, 1); addDestination("1394/Out", 0, 6, eRD_ATX1, 9); addDestination("Mute", 0, 1, eRD_Muted); } /** * The default configurations for the Profire 2626. * 91 destinations * This tries to mimic default config from Windows control panel */ void Profire2626::Profire2626EAP::setupDefaultRouterConfig_low() { unsigned int i; // ======== the 1394 stream receivers // LINE IN for (i=0; i<8; i++) { addRoute(eRS_InS1, i, eRD_ATX0, i); } // ADAT A for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_ATX0, i+8); } // ADAT B for (i=0; i<8; i++) { addRoute(eRS_ADAT, i+8, eRD_ATX1, i); } // SPDIF for (i=0; i<2; i++) { addRoute(eRS_AES, i, eRD_ATX1, i+8); } // ======== Mixer inputs // LINE IN for (i=0; i<8; i++) { addRoute(eRS_InS1, i, eRD_Mixer0, i); } // ADAT A for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_Mixer0, i+8); } // SPDIF for (i=0; i<2; i++) { addRoute(eRS_AES, i, eRD_Mixer1, i); } // ======== Outputs // LINE OUT for (i=0; i<8; i++) { addRoute(eRS_ARX0, i, eRD_InS1, i+2); } // ADAT A for (i=0; i<8; i++) { addRoute(eRS_ARX0, i+8, eRD_ADAT, i); } // ADAT B for (i=0; i<8; i++) { addRoute(eRS_ARX1, i, eRD_ADAT, i+8); } // SPDIF for (i=0; i<2; i++) { addRoute(eRS_ARX1, i+8, eRD_AES, i); } // Mixer is muted for (i=0; i<16; i++) { addRoute(eRS_Mixer, i, eRD_Muted, 0); } } void Profire2626::Profire2626EAP::setupDefaultRouterConfig_mid() { unsigned int i; // ======== the 1394 stream receivers // LINE IN for (i=0; i<8; i++) { addRoute(eRS_InS1, i, eRD_ATX0, i); } // ADAT A for (i=0; i<4; i++) { addRoute(eRS_ADAT, i, eRD_ATX0, i+8); } // ADAT B for (i=0; i<4; i++) { addRoute(eRS_ADAT, i+4, eRD_ATX1, i); } // SPDIF for (i=0; i<2; i++) { addRoute(eRS_AES, i, eRD_ATX1, i+8); } // ======== Mixer inputs // LINE IN for (i=0; i<8; i++) { addRoute(eRS_InS1, i, eRD_Mixer0, i); } // ADAT for (i=0; i<8; i++) { addRoute(eRS_ADAT, i, eRD_Mixer0, i+8); } // SPDIF for (i=0; i<2; i++) { addRoute(eRS_AES, i, eRD_Mixer1, i); } // ======== Outputs // LINE OUT for (i=0; i<8; i++) { addRoute(eRS_ARX0, i, eRD_InS1, i+2); } // ADAT A for (i=0; i<8; i++) { addRoute(eRS_ARX0, i+8, eRD_ADAT, i); } // ADAT B for (i=0; i<8; i++) { addRoute(eRS_ARX1, i, eRD_ADAT, i+8); } // SPDIF for (i=0; i<2; i++) { addRoute(eRS_ARX1, i+8, eRD_AES, i); } // Mixer is muted for (i=0; i<16; i++) { addRoute(eRS_Mixer, i, eRD_Muted, 0); } } void Profire2626::Profire2626EAP::setupDefaultRouterConfig_high() { printMessage("Don't know how to handle High (192 kHz) sample rate for Profire2626\n"); } /** * Application space register read/write */ bool Profire2626::Profire2626EAP::readApplicationReg(unsigned offset, quadlet_t* quadlet) { bool ret = readReg(Dice::EAP::eRT_Application, offset, quadlet); return ret; } bool Profire2626::Profire2626EAP::writeApplicationReg(unsigned offset, quadlet_t quadlet) { bool ret = writeReg(Dice::EAP::eRT_Application, offset, quadlet); if (!ret) { debugWarning("Couldn't write %i to register %x!\n", quadlet, offset); return false; } return ret; } /** * Switch class */ Profire2626::Profire2626EAP::Switch::Switch( Profire2626::Profire2626EAP* eap, std::string name, size_t offset, int activevalue) : Control::Boolean(eap, name) , m_eap(eap) , m_name(name) , m_offset(offset) , m_activevalue(activevalue) { debugOutput( DEBUG_LEVEL_VERBOSE, "Create Switch %s)\n", m_name.c_str()); } bool Profire2626::Profire2626EAP::Switch::selected() { quadlet_t state_tmp; m_eap->readApplicationReg(m_offset, &state_tmp); bool is_selected = (state_tmp&m_activevalue)?true:false; return is_selected; } bool Profire2626::Profire2626EAP::Switch::select(bool n) { quadlet_t state_tmp; m_eap->readApplicationReg(m_offset, &state_tmp); bool is_selected = (state_tmp&m_activevalue)?true:false; // Switch the corresponding bit(s) if ( n != is_selected ) { m_eap->writeApplicationReg(m_offset, state_tmp^m_activevalue); is_selected = n; } return is_selected; } /** * Profire2626 Settings section */ Profire2626::Profire2626EAP::SettingsSection::SettingsSection( Profire2626::Profire2626EAP* eap, std::string name) : Control::Container(eap, name) , m_eap(eap) { // Volume Knob Control::Container* grp_volumeknob = new Control::Container(m_eap, "VolumeKnob"); addElement(grp_volumeknob); for (unsigned i=0; iaddElement(outputPair); } } Profire2626::Profire610EAP::Profire610EAP(Dice::Device & dev) : Profire2626EAP(dev) { } // // Profire 610 has // - 4 mic/line inputs // - 2 coaxial SPDIF inputs // - 32 ieee1394 inputs // - 18 mixer inputs // - 1 MIDI input // // - 8 line outputs (first two pairs are also routed to 2 headphone jacks) // - 2 coaxial SPDIF outputs // - 32 ieee1394 outputs // - 16 mixer outputs // - 1 MIDI output // void Profire2626::Profire610EAP::setupSources_low() { addSource("Mic/Line/In", 0, 4, eRS_InS0, 1); addSource("SPDIF/In", 0, 2, eRS_AES, 1); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 16, eRS_ARX0, 1); addSource("1394/In", 0, 16, eRS_ARX1, 17); addSource("Mute", 0, 1, eRS_Muted); } void Profire2626::Profire610EAP::setupDestinations_low() { addDestination("Line/Out", 0, 8, eRD_InS0, 1); addDestination("SPDIF/Out", 0, 2, eRD_AES, 1); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 16, eRD_ATX0, 1); addDestination("1394/Out", 0, 16, eRD_ATX1, 17); addDestination("Mute", 0, 1, eRD_Muted); } void Profire2626::Profire610EAP::setupSources_mid() { setupSources_low(); } void Profire2626::Profire610EAP::setupDestinations_mid() { setupDestinations_low(); } void Profire2626::Profire610EAP::setupSources_high() { setupSources_low(); } void Profire2626::Profire610EAP::setupDestinations_high() { setupDestinations_low(); } void Profire2626::Profire610EAP::setupDefaultRouterConfig_low() { unsigned int i; // ======== the 1394 stream receivers // LINE IN for (i=0; i<4; i++) { addRoute(eRS_InS0, i, eRD_ATX0, i); } // SPDIF IN for (i=0; i<2; i++) { addRoute(eRS_AES, i, eRD_ATX0, i+4); } // ======== Mixer inputs // LINE IN for (i=0; i<4; i++) { addRoute(eRS_InS0, i, eRD_Mixer0, i); } // SPDIF IN for (i=0; i<2; i++) { addRoute(eRS_AES, i, eRD_Mixer0, i+4); } // IEEE1394 channels for (i=0; i<10; i++) { addRoute(eRS_ARX0, i, eRD_Mixer0, i+6); } for (i=0; i<2; i++) { addRoute(eRS_ARX0, i+10, eRD_Mixer1, i); } // ======== Outputs // LINE OUT for (i=0; i<8; i++) { addRoute(eRS_ARX0, i, eRD_InS0, i); } // SPDIF OUT for (i=0; i<2; i++) { addRoute(eRS_ARX0, i+8, eRD_AES, i); } // Mixer is muted for (i=0; i<16; i++) { addRoute(eRS_Mixer, i, eRD_Muted, 0); } } void Profire2626::Profire610EAP::setupDefaultRouterConfig_mid() { setupDefaultRouterConfig_low(); } void Profire2626::Profire610EAP::setupDefaultRouterConfig_high() { setupDefaultRouterConfig_low(); } /** Device */ Profire2626::Profire2626( DeviceManager& d, ffado_smartptr(configRom)) : Dice::Device( d, configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Dice::Maudio::Profire2626 (NodeID %d)\n", getConfigRom().getNodeId() ); } Profire2626::~Profire2626() { getEAP()->storeFlashConfig(); } bool Profire2626::discover() { if (Dice::Device::discover()) { debugOutput(DEBUG_LEVEL_VERBOSE, "Discovering Dice::Maudio::Profire2626\n"); Profire2626EAP* eap = dynamic_cast(getEAP()); Profire2626EAP::SettingsSection *settings = new Profire2626EAP::SettingsSection(eap, "Settings"); eap->addElement(settings); return true; } return false; } void Profire2626::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a Dice::Maudio::Profire2626\n"); Dice::Device::showDevice(); } Dice::EAP* Profire2626::createEAP() { if (getConfigRom().getModelId() == 0x00000011) return new Profire610EAP(*this); else return new Profire2626EAP(*this); } } } // vim: et libffado-2.4.5/src/dice/maudio/profire_2626.h0000644000175000001440000000710314206145246020134 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2012 by Philippe Carriere * Copyright (C) 2012 by Jano Svitok * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DICE_MAUDIO_PROFIRE_2626_H #define DICE_MAUDIO_PROFIRE_2626_H #include "dice/dice_avdevice.h" #include "dice/dice_eap.h" #include "libieee1394/configrom.h" // Global monitor registers (application space) #define MAUDIO_PROFIRE2626_REGISTER_APP_VOLUME_KNOB_OFFSET 0x00 #define MAUDIO_PROFIRE2626_REGISTER_APP_VOLUME_KNOB_SIZE 4 #define MAUDIO_PROFIRE2626_REGISTER_APP_VOLUME_KNOB_VALUE 1 #define MAUDIO_PROFIRE2626_REGISTER_APP_VOLUME_KNOB_SHIFT 0 namespace Dice { namespace Maudio { class Profire2626 : public Dice::Device { public: Profire2626( DeviceManager& d, ffado_smartptr( configRom )); virtual ~Profire2626(); bool discover(); virtual void showDevice(); bool canChangeNickname() { return false; } class Profire2626EAP : public Dice::EAP { public: Profire2626EAP(Dice::Device& dev); void setupSources_low(); void setupDestinations_low(); void setupSources_mid(); void setupDestinations_mid(); void setupSources_high(); void setupDestinations_high(); void setupDefaultRouterConfig_low(); void setupDefaultRouterConfig_mid(); void setupDefaultRouterConfig_high(); bool readApplicationReg(unsigned, quadlet_t*); bool writeApplicationReg(unsigned, quadlet_t); /** * @brief A standard-switch for boolean. * * If you don't like True and False for the labels, subclass and return your own. * \internal copy&paste from focusrite_eap.h */ class Switch : public Control::Boolean { public: Switch(Profire2626EAP*, std::string, size_t, int); bool selected(); bool select(bool); private: Profire2626EAP* m_eap; std::string m_name; size_t m_offset; int m_activevalue; }; class SettingsSection : public Control::Container { public: SettingsSection(Profire2626EAP*, std::string); private: Profire2626EAP * m_eap; }; }; class Profire610EAP : public Profire2626EAP { public: Profire610EAP(Dice::Device& dev); void setupSources_low() override; void setupDestinations_low() override; void setupSources_mid() override; void setupDestinations_mid() override; void setupSources_high() override; void setupDestinations_high() override; void setupDefaultRouterConfig_low() override; void setupDefaultRouterConfig_mid() override; void setupDefaultRouterConfig_high() override; }; private: Dice::EAP* createEAP(); }; } } #endif // vim: et libffado-2.4.5/src/dice/presonus/0000755000175000001440000000000014206145612016232 5ustar jwoitheuserslibffado-2.4.5/src/dice/presonus/firestudio_mobile.cpp0000644000175000001440000001221714206145246022450 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2012 by Philippe Carriere * Copyright (C) 2014 by Philippe Carriere * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "firestudio_mobile.h" namespace Dice { namespace Presonus { // // Firestudio Mobile has // - 2 mic/inst inputs // - 6 line inputs // - 2 SPDIF inputs // - 6 ieee1394 inputs // - 18 mixer inputs // // - 4 analogic line outputs (stereo main, stereo phone) // - 2 SPDIF outputs // - 10 ieee1394 outputs // - 16 mixer outputs // void FirestudioMobile::FirestudioMobileEAP::setupSources_low() { addSource("SPDIF/In", 2, 2, eRS_AES, 1); addSource("Mic/Inst/In", 0, 2, eRS_InS0, 1); addSource("Lin/In", 2, 6, eRS_InS0, 3); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 6, eRS_ARX0, 1); addSource("Mute", 0, 1, eRS_Muted); } void FirestudioMobile::FirestudioMobileEAP::setupDestinations_low() { addDestination("SPDIF/Out", 2, 2, eRD_AES, 1); addDestination("Line/Out", 0, 4, eRD_InS0, 1); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 10, eRD_ATX0, 1); // Is a Mute destination useful ? // addDestination("Mute", 0, 1, eRD_Muted); } // // Independent of samplerate void FirestudioMobile::FirestudioMobileEAP::setupSources_mid() { setupSources_low(); } void FirestudioMobile::FirestudioMobileEAP::setupDestinations_mid() { setupDestinations_low(); } // // 192 kHz is not supported // void FirestudioMobile::FirestudioMobileEAP::setupSources_high() { printMessage("High (192 kHz) sample rate not supported by Firestudio Tube\n"); } void FirestudioMobile::FirestudioMobileEAP::setupDestinations_high() { printMessage("High (192 kHz) sample rate not supported by Firestudio Tube\n"); } /** * The default configuration for the Firestudio Mobile. * 82 destinations; each "group" every 32 registers **FIXME What follows is extracted from a listing of an existing router configuration. * However, the origin of such a router configuration was unknown. */ void FirestudioMobile::FirestudioMobileEAP::setupDefaultRouterConfig_low() { unsigned int i; // the 1394 stream receivers for (i=0; i<8; i++) { addRoute(eRS_InS0, i, eRD_ATX0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i+2, eRD_ATX0, i+8); } // Then 22 muted destinations for (i=0; i<22; i++) { addRoute(eRS_Muted, 0, eRD_Muted, 0); } // the Mixer inputs for (i=0; i<8; i++) { addRoute(eRS_InS0, i, eRD_Mixer0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i+2, eRD_Mixer0, i+8); } for (i=0; i<6; i++) { addRoute(eRS_ARX0, i, eRD_Mixer0, i+10); } // Then 16 muted destinations for (i=0; i<16; i++) { addRoute(eRS_Muted, 0, eRD_Muted, 0); } // The audio ports // Ensure that audio port are not muted for (i=0; i<4; i++) { addRoute(eRS_Mixer, i, eRD_InS0, i); } // The SPDIF ports for (i=0; i<2; i++) { addRoute(eRS_Mixer, i+4, eRD_AES, i+2); } // Then 12 muted destinations for (i=0; i<12; i++) { addRoute(eRS_Muted, 0, eRD_Muted, 0); } } /** * Identical to mid-rate */ void FirestudioMobile::FirestudioMobileEAP::setupDefaultRouterConfig_mid() { setupDefaultRouterConfig_low(); } /** * High rate not supported */ void FirestudioMobile::FirestudioMobileEAP::setupDefaultRouterConfig_high() { printMessage("High (192 kHz) sample rate not supported by Firestudio Mobile\n"); } /** Device */ FirestudioMobile::FirestudioMobile( DeviceManager& d, ffado_smartptr( configRom )) : Dice::Device( d , configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Dice::Presonus::FirestudioMobile (NodeID %d)\n", getConfigRom().getNodeId() ); } FirestudioMobile::~FirestudioMobile() { getEAP()->storeFlashConfig(); } bool FirestudioMobile::discover() { if (Dice::Device::discover()) { debugOutput(DEBUG_LEVEL_VERBOSE, "Discovering Dice::Presonus::FirestudioMobile\n"); return true; } return false; } void FirestudioMobile::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a Dice::Presonus::FirestudioMobile\n"); Dice::Device::showDevice(); } Dice::EAP* FirestudioMobile::createEAP() { return new FirestudioMobileEAP(*this); } } } // vim: et libffado-2.4.5/src/dice/presonus/firestudio_mobile.h0000644000175000001440000000371114206145246022114 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2012 by Philippe Carriere * Copyright (C) 2014 by Philippe Carriere * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DICE_PRESONUS_FIRESTUDIO_MOBILE_H #define DICE_PRESONUS_FIRESTUDIO_MOBILE_H #include "dice/dice_avdevice.h" #include "dice/dice_eap.h" #include "libieee1394/configrom.h" namespace Dice { namespace Presonus { class FirestudioMobile : public Dice::Device { public: FirestudioMobile( DeviceManager& d, ffado_smartptr( configRom )); virtual ~FirestudioMobile(); bool discover(); virtual void showDevice(); bool canChangeNickname() { return false; } private: class FirestudioMobileEAP : public Dice::EAP { public: FirestudioMobileEAP(Dice::Device& dev) : Dice::EAP(dev) { } void setupSources_low(); void setupDestinations_low(); void setupSources_mid(); void setupDestinations_mid(); void setupSources_high(); void setupDestinations_high(); void setupDefaultRouterConfig_low(); void setupDefaultRouterConfig_mid(); void setupDefaultRouterConfig_high(); }; Dice::EAP* createEAP(); }; } } #endif // vim: et libffado-2.4.5/src/dice/presonus/firestudio_project.cpp0000644000175000001440000001224714206145246022652 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2012 by Philippe Carriere * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "firestudio_project.h" namespace Dice { namespace Presonus { // // Firestudio Project has // - 8 mic/line inputs // - 2 SPDIF inputs // - 10 ieee1394 inputs // - 18 mixer inputs // // - 8 analogic line outputs // - 2 SPDIF outputs // - 10 ieee1394 outputs // - 16 mixer outputs // void FirestudioProject::FirestudioProjectEAP::setupSources_low() { addSource("SPDIF/In", 2, 2, eRS_AES, 1); addSource("Mic/Inst/In", 0, 2, eRS_InS0, 1); addSource("Mic/Lin/In", 2, 6, eRS_InS0, 3); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 10, eRS_ARX0, 1); addSource("Mute", 0, 1, eRS_Muted); } void FirestudioProject::FirestudioProjectEAP::setupDestinations_low() { addDestination("SPDIF/Out", 2, 2, eRD_AES, 1); addDestination("Line/Out", 0, 8, eRD_InS0, 1); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 10, eRD_ATX0, 1); // Is a Mute destination useful ? // addDestination("Mute", 0, 1, eRD_Muted); } // // Independent of samplerate void FirestudioProject::FirestudioProjectEAP::setupSources_mid() { setupSources_low(); } void FirestudioProject::FirestudioProjectEAP::setupDestinations_mid() { setupDestinations_low(); } // // 192 kHz is not supported // void FirestudioProject::FirestudioProjectEAP::setupSources_high() { printMessage("High (192 kHz) sample rate not supported by Firestudio Tube\n"); } void FirestudioProject::FirestudioProjectEAP::setupDestinations_high() { printMessage("High (192 kHz) sample rate not supported by Firestudio Tube\n"); } /** * The default configuration for the Firestudio Project. * 82 destinations; each "group" every 32 registers **FIXME What follows is extracted from a listing of an existing router configuration. * However, the origin of such a router configuration was unknown. */ void FirestudioProject::FirestudioProjectEAP::setupDefaultRouterConfig_low() { unsigned int i; // the 1394 stream receivers for (i=0; i<8; i++) { addRoute(eRS_InS0, i, eRD_ATX0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i+2, eRD_ATX0, i+8); } // Then 22 muted destinations for (i=0; i<22; i++) { addRoute(eRS_Muted, 0, eRD_Muted, 0); } // the Mixer inputs for (i=0; i<8; i++) { addRoute(eRS_InS0, i, eRD_Mixer0, i); } for (i=0; i<2; i++) { addRoute(eRS_AES, i+2, eRD_Mixer0, i+8); } for (i=0; i<6; i++) { addRoute(eRS_ARX0, i, eRD_Mixer0, i+10); } for (i=0; i<2; i++) { addRoute(eRS_ARX0, i+6, eRD_Mixer1, i); } // Then 14 muted destinations for (i=0; i<14; i++) { addRoute(eRS_Muted, 0, eRD_Muted, 0); } // The audio ports // Ensure that audio port are not muted for (i=0; i<8; i++) { addRoute(eRS_ARX0, i, eRD_InS0, i); } // The SPDIF ports for (i=0; i<2; i++) { addRoute(eRS_ARX0, i+8, eRD_AES, i+2); } // Then 8 muted destinations for (i=0; i<8; i++) { addRoute(eRS_Muted, 0, eRD_Muted, 0); } } /** * Identical to mid-rate */ void FirestudioProject::FirestudioProjectEAP::setupDefaultRouterConfig_mid() { setupDefaultRouterConfig_low(); } /** * High rate not supported */ void FirestudioProject::FirestudioProjectEAP::setupDefaultRouterConfig_high() { printMessage("High (192 kHz) sample rate not supported by Firestudio Project\n"); } /** Device */ FirestudioProject::FirestudioProject( DeviceManager& d, ffado_smartptr( configRom )) : Dice::Device( d , configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Dice::Presonus::FirestudioProject (NodeID %d)\n", getConfigRom().getNodeId() ); } FirestudioProject::~FirestudioProject() { getEAP()->storeFlashConfig(); } bool FirestudioProject::discover() { if (Dice::Device::discover()) { debugOutput(DEBUG_LEVEL_VERBOSE, "Discovering Dice::Presonus::FirestudioProject\n"); return true; } return false; } void FirestudioProject::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a Dice::Presonus::FirestudioProject\n"); Dice::Device::showDevice(); } Dice::EAP* FirestudioProject::createEAP() { return new FirestudioProjectEAP(*this); } } } // vim: et libffado-2.4.5/src/dice/presonus/firestudio_project.h0000644000175000001440000000363514206145246022320 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2012 by Philippe Carriere * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DICE_PRESONUS_FIRESTUDIO_PROJECT_H #define DICE_PRESONUS_FIRESTUDIO_PROJECT_H #include "dice/dice_avdevice.h" #include "dice/dice_eap.h" #include "libieee1394/configrom.h" namespace Dice { namespace Presonus { class FirestudioProject : public Dice::Device { public: FirestudioProject( DeviceManager& d, ffado_smartptr( configRom )); virtual ~FirestudioProject(); bool discover(); virtual void showDevice(); bool canChangeNickname() { return false; } private: class FirestudioProjectEAP : public Dice::EAP { public: FirestudioProjectEAP(Dice::Device& dev) : Dice::EAP(dev) { } void setupSources_low(); void setupDestinations_low(); void setupSources_mid(); void setupDestinations_mid(); void setupSources_high(); void setupDestinations_high(); void setupDefaultRouterConfig_low(); void setupDefaultRouterConfig_mid(); void setupDefaultRouterConfig_high(); }; Dice::EAP* createEAP(); }; } } #endif // vim: et libffado-2.4.5/src/dice/presonus/firestudio_tube.cpp0000644000175000001440000001144014206145246022135 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2012 by Philippe Carriere * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "firestudio_tube.h" namespace Dice { namespace Presonus { // // Firestudio Tube has // - 8 mic/line inputs // - 6 line inputs // - 2 "Tube" analogic inputs // - 8 ieee1394 inputs **FIXME // - 18 mixer inputs // // - 6 analogic line outputs // - 2 analogic headphone outputs // - 16 ieee1394 outputs // - 16 mixer outputs // void FirestudioTube::FirestudioTubeEAP::setupSources_low() { addSource("Mic/Lin/In", 0, 8, eRS_InS0, 1); addSource("Line/In", 8, 6, eRS_InS0, 9); addSource("Tube/In", 14, 2, eRS_InS0, 15); addSource("Mixer/Out", 0, 16, eRS_Mixer, 1); addSource("1394/In", 0, 8, eRS_ARX0, 1); addSource("Mute", 0, 1, eRS_Muted); } void FirestudioTube::FirestudioTubeEAP::setupDestinations_low() { addDestination("Line/Out", 0, 6, eRD_InS0, 1); addDestination("Head/Out", 6, 2, eRD_InS0, 7); addDestination("Mixer/In", 0, 16, eRD_Mixer0, 1); addDestination("Mixer/In", 0, 2, eRD_Mixer1, 17); addDestination("1394/Out", 0, 16, eRD_ATX0, 1); // Is a Mute destination useful ? // addDestination("Mute", 0, 1, eRD_Muted); } // // Independent of samplerate void FirestudioTube::FirestudioTubeEAP::setupSources_mid() { setupSources_low(); } void FirestudioTube::FirestudioTubeEAP::setupDestinations_mid() { setupDestinations_low(); } // // 192 kHz is not supported // void FirestudioTube::FirestudioTubeEAP::setupSources_high() { printMessage("High (192 kHz) sample rate not supported by Firestudio Tube\n"); } void FirestudioTube::FirestudioTubeEAP::setupDestinations_high() { printMessage("High (192 kHz) sample rate not supported by Firestudio Tube\n"); } /** * The default configurations for the Firestudio Tube. * 82 destinations; each "group" every 32 registers **FIXME What follows is extracted from a listing of an existing router configuration. * However, the origin of such a router configuration was unknown. */ void FirestudioTube::FirestudioTubeEAP::setupDefaultRouterConfig_low() { unsigned int i; // the 1394 stream receivers for (i=0; i<16; i++) { addRoute(eRS_InS0, i, eRD_ATX0, i); } // Then 16 muted destinations for (i=0; i<16; i++) { addRoute(eRS_Muted, 0, eRD_Muted, 0); } // the Mixer inputs for (i=0; i<16; i++) { addRoute(eRS_InS0, i, eRD_Mixer0, i); } for (i=0; i<2; i++) { addRoute(eRS_ARX0, i, eRD_Mixer1, i); } // Then 14 muted destinations for (i=0; i<14; i++) { addRoute(eRS_Muted, 0, eRD_Muted, 0); } // The audio ports // Ensure that audio port are not muted for (i=0; i<8; i++) { addRoute(eRS_ARX0, i, eRD_InS0, i); } // Then 10 muted destinations for (i=0; i<10; i++) { addRoute(eRS_Muted, 0, eRD_Muted, 0); } } /** * Identical to mid-rate */ void FirestudioTube::FirestudioTubeEAP::setupDefaultRouterConfig_mid() { setupDefaultRouterConfig_low(); } /** * High rate not supported */ void FirestudioTube::FirestudioTubeEAP::setupDefaultRouterConfig_high() { printMessage("High (192 kHz) sample rate not supported by Firestudio Tube\n"); } /** Device */ FirestudioTube::FirestudioTube( DeviceManager& d, ffado_smartptr( configRom )) : Dice::Device( d , configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Dice::Presonus::FirestudioTube (NodeID %d)\n", getConfigRom().getNodeId() ); } FirestudioTube::~FirestudioTube() { getEAP()->storeFlashConfig(); } bool FirestudioTube::discover() { if (Dice::Device::discover()) { debugOutput(DEBUG_LEVEL_VERBOSE, "Discovering Dice::Presonus::FirestudioTube\n"); return true; } return false; } void FirestudioTube::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a Dice::Presonus::FirestudioTube\n"); Dice::Device::showDevice(); } Dice::EAP* FirestudioTube::createEAP() { return new FirestudioTubeEAP(*this); } } } // vim: et libffado-2.4.5/src/dice/presonus/firestudio_tube.h0000644000175000001440000000361014206145246021602 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * Copyright (C) 2012 by Philippe Carriere * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef DICE_PRESONUS_FIRESTUDIO_TUBE_H #define DICE_PRESONUS_FIRESTUDIO_TUBE_H #include "dice/dice_avdevice.h" #include "dice/dice_eap.h" #include "libieee1394/configrom.h" namespace Dice { namespace Presonus { class FirestudioTube : public Dice::Device { public: FirestudioTube( DeviceManager& d, ffado_smartptr( configRom )); virtual ~FirestudioTube(); bool discover(); virtual void showDevice(); bool canChangeNickname() { return false; } private: class FirestudioTubeEAP : public Dice::EAP { public: FirestudioTubeEAP(Dice::Device& dev) : Dice::EAP(dev) { } void setupSources_low(); void setupDestinations_low(); void setupSources_mid(); void setupDestinations_mid(); void setupSources_high(); void setupDestinations_high(); void setupDefaultRouterConfig_low(); void setupDefaultRouterConfig_mid(); void setupDefaultRouterConfig_high(); }; Dice::EAP* createEAP(); }; } } #endif // vim: et libffado-2.4.5/src/digidesign/0000755000175000001440000000000014206145612015556 5ustar jwoitheuserslibffado-2.4.5/src/digidesign/digidesign_avdevice.cpp0000644000175000001440000004426414206145246022253 0ustar jwoitheusers/* * Copyright (C) 2005-2011 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #warning Digidesign support is at an early development stage and is not functional #include "config.h" #include "digidesign/digidesign_avdevice.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/IsoHandlerManager.h" #include "debugmodule/debugmodule.h" #include "libstreaming/digidesign/DigidesignPort.h" #include "devicemanager.h" #include #include #include #include "libutil/ByteSwap.h" #include #include #include namespace Digidesign { Device::Device( DeviceManager& d, ffado_smartptr( configRom )) : FFADODevice( d, configRom ) , m_digidesign_model( DIGIDESIGN_MODEL_NONE ) , num_channels( 0 ) , frames_per_packet( 0 ) , iso_tx_channel( -1 ) , iso_rx_channel( -1 ) , m_receiveProcessor( NULL ) , m_transmitProcessor( NULL ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Digidesign::Device (NodeID %d)\n", getConfigRom().getNodeId() ); } Device::~Device() { delete m_receiveProcessor; delete m_transmitProcessor; if (iso_tx_channel>=0 && !get1394Service().freeIsoChannel(iso_tx_channel)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Could not free tx iso channel %d\n", iso_tx_channel); } if (iso_rx_channel>=0 && !get1394Service().freeIsoChannel(iso_rx_channel)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Could not free rx iso channel %d\n", iso_rx_channel); } destroyMixer(); } bool Device::buildMixer() { destroyMixer(); debugOutput(DEBUG_LEVEL_VERBOSE, "Building a Digidesign mixer...\n"); // From the sounds of it there are no software-controllable device // settings on the Digidesign interfaces beyond those things directly // related to streaming. Therefore this method probably doesn't need // to do anything. If this situation changes, check out the RME // Device::buildMixer() method as an example of how it's done. return true; } bool Device::destroyMixer() { debugOutput(DEBUG_LEVEL_VERBOSE, "destroy mixer...\n"); // If buildMixer() doesn't do anything then there's nothing for // this function to do either. return false; } bool Device::probe( Util::Configuration& c, ConfigRom& configRom, bool generic ) { if (generic) { return false; } else { // check if device is in supported devices list. This is where // you insert code which allows you to identify the Digidesign // devices on the bus. For most devices the entries in the vendor // model table (which are consulted here) are sufficient. unsigned int vendorId = configRom.getNodeVendorId(); unsigned int modelId = configRom.getModelId(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); return c.isValid(vme) && vme.driver == Util::Configuration::eD_Digidesign; } } FFADODevice * Device::createDevice(DeviceManager& d, ffado_smartptr( configRom )) { return new Device(d, configRom ); } bool Device::discover() { unsigned int vendorId = getConfigRom().getNodeVendorId(); unsigned int modelId = getConfigRom().getModelId(); Util::Configuration &c = getDeviceManager().getConfiguration(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); if (c.isValid(vme) && vme.driver == Util::Configuration::eD_Digidesign) { debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", vme.vendor_name.c_str(), vme.model_name.c_str()); } else { debugWarning("Device '%s %s' unsupported by Digidesign driver (no generic Digidesign support)\n", getConfigRom().getVendorName().c_str(), getConfigRom().getModelName().c_str()); } // Here you work out what m_digidesign_model should be set to and // set it accordingly. This can then be used by the rest of the driver // whenever one needs to do different things for different interfaces. // // m_digidesign_model = DIGIDESIGN_MODEL_... // // This function should return true if the device is a supported device // or false if not. Presently only the 003 Rack is supported. if (m_digidesign_model != DIGIDESIGN_MODEL_003_RACK) { debugError("Unsupported model\n"); return false; } if (!buildMixer()) { debugWarning("Could not build mixer\n"); } return true; } int Device::getSamplingFrequency( ) { // This function should return the current sampling rate of the device. return 0; } int Device::getConfigurationId() { return 0; } bool Device::setSamplingFrequency( int samplingFrequency ) { // Do whatever it takes to set the device's sampling rate. Return false // if the requested rate cannot be done, true if it can. return false; } std::vector Device::getSupportedSamplingFrequencies() { std::vector frequencies; // Generate a list of sampling rates supported by the device, in Hz. // This may be a relatively simple process. For example: // frequencies.push_back(44100); // frequencies.push_back(48000); return frequencies; } FFADODevice::ClockSourceVector Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; ClockSource s; // Similar to getSupportedSamplingFrequencies(), the available clock // sources for the device are configured here. For example: // s.valid = true; // s.locked = true; // s.active = true; // s.slipping = false; // s.type = eCT_Internal; // s.description = "Internal sync"; // r.push_back(s); // // See src/ffadodevice.h for the full list of possible eCT_* types. return r; } bool Device::setActiveClockSource(ClockSource s) { // Do whatever is necessary to configure the device to use the given // clock source. Return true on success, false on error. return false; } FFADODevice::ClockSource Device::getActiveClockSource() { ClockSource s; // Return the active clock source return s; } bool Device::lock() { // Use this method (and the companion unlock() method) if a device lock // is required for some reason. return true; } bool Device::unlock() { return true; } void Device::showDevice() { unsigned int vendorId = getConfigRom().getNodeVendorId(); unsigned int modelId = getConfigRom().getModelId(); // This method is really just to aid debugging and can be used to to // print useful identification information to the debug output. Util::Configuration &c = getDeviceManager().getConfiguration(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); debugOutput(DEBUG_LEVEL_VERBOSE, "%s %s at node %d\n", vme.vendor_name.c_str(), vme.model_name.c_str(), getNodeId()); } bool Device::prepare() { signed int bandwidth; signed int err = 0; debugOutput(DEBUG_LEVEL_NORMAL, "Preparing Device...\n" ); // This method should prepare the device for streaming without actually // turning streaming on. It is mostly used to configure ports which // will ultimately show up as JACK ports - one per audio channel. // Store the number of frames per FireWire packet. Depending on the // device protocol this may not need to be stored in a data field of // the object, in which case frames_per_packet could be come a local // variable. frames_per_packet = getFramesPerPacket(); // Similarly, the number of channels might not be needed beyond this // method. In any case, it must be set correctly to permit calculation // of required bus bandwidth. num_channels = 0; // Bus bandwidth is calculated here. We assume the device is an S400 // device, so 1 allocation unit is 1 transmitted byte. There is 25 // allocation units of protocol overhead per packet. The following // expression is correct if each channel of audio data is sent/received // as a 32 bit integer. bandwidth = 25 + num_channels*4*frames_per_packet; // Depending on the device, the onus on reserving bus bandwidth rests // either with the device or this driver. The following code shows // how iso channels can be requested from the IRM and then reserved // with the required amount of bandwidth. if (iso_tx_channel < 0) { iso_tx_channel = get1394Service().allocateIsoChannelGeneric(bandwidth); } if (iso_tx_channel < 0) { debugFatal("Could not allocate iso tx channel\n"); return false; } if (iso_rx_channel < 0) { iso_rx_channel = get1394Service().allocateIsoChannelGeneric(bandwidth); } if (iso_rx_channel < 0) { debugFatal("Could not allocate iso rx channel\n"); err = 1; } if (err) { if (iso_tx_channel >= 0) get1394Service().freeIsoChannel(iso_tx_channel); if (iso_rx_channel >= 0) get1394Service().freeIsoChannel(iso_rx_channel); return false; } // get the device specific and/or global SP configuration Util::Configuration &config = getDeviceManager().getConfiguration(); // base value is the config.h value float recv_sp_dll_bw = STREAMPROCESSOR_DLL_BW_HZ; float xmit_sp_dll_bw = STREAMPROCESSOR_DLL_BW_HZ; // we can override that globally config.getValueForSetting("streaming.spm.recv_sp_dll_bw", recv_sp_dll_bw); config.getValueForSetting("streaming.spm.xmit_sp_dll_bw", xmit_sp_dll_bw); // or override in the device section config.getValueForDeviceSetting(getConfigRom().getNodeVendorId(), getConfigRom().getModelId(), "recv_sp_dll_bw", recv_sp_dll_bw); config.getValueForDeviceSetting(getConfigRom().getNodeVendorId(), getConfigRom().getModelId(), "xmit_sp_dll_bw", xmit_sp_dll_bw); // Calculate the event size. Each audio channel is allocated 4 bytes in // the data stream by the statement which follows. This will need to be // changed to suit the Digidesign interfaces. signed int event_size = num_channels * 4; // Set up receive stream processor, initialise it and set DLL bw m_receiveProcessor = new Streaming::DigidesignReceiveStreamProcessor(*this, event_size); m_receiveProcessor->setVerboseLevel(getDebugLevel()); if (!m_receiveProcessor->init()) { debugFatal("Could not initialize receive processor!\n"); return false; } if (!m_receiveProcessor->setDllBandwidth(recv_sp_dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete m_receiveProcessor; m_receiveProcessor = NULL; return false; } // Add ports to the receive stream processor - one port per audio channel // expected from the device. std::string id=std::string("dev?"); // Ports can be added directly in this method. I (Jonathan Woithe) // prefer them in a separate method to keep the prepare() method as // clear as possible. addDirPorts(Streaming::Port::E_Capture); /* Now set up the transmit stream processor */ m_transmitProcessor = new Streaming::DigidesignTransmitStreamProcessor(*this, event_size); m_transmitProcessor->setVerboseLevel(getDebugLevel()); if (!m_transmitProcessor->init()) { debugFatal("Could not initialise receive processor!\n"); return false; } if (!m_transmitProcessor->setDllBandwidth(xmit_sp_dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete m_transmitProcessor; m_transmitProcessor = NULL; return false; } // Add ports to the transmit stream processor. This is one port // per audio channel to be sent to the device. addDirPorts(Streaming::Port::E_Playback); return true; } int Device::getStreamCount() { return 2; // Normally this is 2: one receive, one transmit } Streaming::StreamProcessor * Device::getStreamProcessorByIndex(int i) { // A housekeeping method. This should not need to be changed. switch (i) { case 0: return m_receiveProcessor; case 1: return m_transmitProcessor; default: debugWarning("Invalid stream index %d\n", i); } return NULL; } bool Device::startStreamByIndex(int i) { // Do whatever is needed to get the device to start the i'th stream. // Some devices don't permit the separate enabling of the different // streams, in which case you can do everything when stream 0 is // requested, and nothing for the others. Below is an example of this. if (i == 0) { m_receiveProcessor->setChannel(iso_rx_channel); m_transmitProcessor->setChannel(iso_tx_channel); // Send commands to device to start the streaming } // Return true on success, false on failure. return true; } bool Device::stopStreamByIndex(int i) { // This method should stop the streaming for index i, returning true // on success or false on error. Again, if streams can't be stopped // separately one can react only when i is 0. return true; } signed int Device::getFramesPerPacket(void) { // Return the number of frames transmitted in a single FireWire packet. // For some devices this is fixed, while for others it depends on the // current sampling rate. // // Getting the current sample rate is as simple as: // signed int freq = getSamplingFrequency(); // This needs to be set as required. return 0; } bool Device::addPort(Streaming::StreamProcessor *s_processor, char *name, enum Streaming::Port::E_Direction direction, int position, int size) { // This is a helper method for addDirPorts() to minimise the amount of // boilerplate code needed in the latter. It creates the Port object // as required, which is automatically inserted into the supplied stream // processor object. Streaming::Port *p; p = new Streaming::DigidesignAudioPort(*s_processor, name, direction, position, size); if (p == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n",name); } return true; } bool Device::addDirPorts(enum Streaming::Port::E_Direction direction) { const char *mode_str = direction==Streaming::Port::E_Capture?"cap":"pbk"; Streaming::StreamProcessor *s_processor; std::string id; char name[128]; // This method (in conjunction with addPort()) illustrates the port // creation process. if (direction == Streaming::Port::E_Capture) { s_processor = m_receiveProcessor; } else { s_processor = m_transmitProcessor; } id = std::string("dev?"); if (!getOption("id", id)) { debugWarning("Could not retrieve id parameter, defaulting to 'dev?'\n"); } // Add two ports for an example snprintf(name, sizeof(name), "%s_%s_%s", id.c_str(), mode_str, "port_0"); addPort(s_processor, name, direction, 0, 0); snprintf(name, sizeof(name), "%s_%s_%s", id.c_str(), mode_str, "port_1"); addPort(s_processor, name, direction, 4, 0); return true; } unsigned int Device::readRegister(fb_nodeaddr_t reg) { quadlet_t quadlet = 0; // This function is a convenience wrapper around the read() method // from ieee1394service. It takes care of merging the node ID with // the register address and any other address components which are // required to construct a complete bus address. It also deals with // byte ordering. if (get1394Service().read(0xffc0 | getNodeId(), reg, 1, &quadlet) <= 0) { debugError("Error doing Digidesign read from register 0x%06llx\n",reg); } // This return value assumes that the device uses bus byte order (big // endian) for async packets. Most do. return CondSwapFromBus32(quadlet); } signed int Device::readBlock(fb_nodeaddr_t reg, quadlet_t *buf, unsigned int n_quads) { unsigned int i; // As for readRegister() except that an arbitary block of data is // transferred to the device. if (get1394Service().read(0xffc0 | getNodeId(), reg, n_quads, buf) <= 0) { debugError("Error doing Digidesign block read of %d quadlets from register 0x%06llx\n", n_quads, reg); return -1; } for (i=0; i. * */ #ifndef DIGIDESIGNDEVICE_H #define DIGIDESIGNDEVICE_H #include "ffadodevice.h" #include "debugmodule/debugmodule.h" #include "libavc/avc_definitions.h" #include "libutil/Configuration.h" #include "libstreaming/digidesign/DigidesignReceiveStreamProcessor.h" #include "libstreaming/digidesign/DigidesignTransmitStreamProcessor.h" class ConfigRom; class Ieee1394Service; namespace Digidesign { // The actual values within this enum are arbitary enum EDigidesignModel { DIGIDESIGN_MODEL_NONE = 0x0000, DIGIDESIGN_MODEL_003_RACK, }; class Device : public FFADODevice { public: Device( DeviceManager& d, ffado_smartptr( configRom )); virtual ~Device(); virtual bool buildMixer(); virtual bool destroyMixer(); static bool probe( Util::Configuration& c, ConfigRom& configRom, bool generic = false ); static FFADODevice * createDevice( DeviceManager& d, ffado_smartptr( configRom )); static int getConfigurationId( ); virtual bool discover(); virtual void showDevice(); virtual bool setSamplingFrequency( int samplingFrequency ); virtual int getSamplingFrequency( ); virtual std::vector getSupportedSamplingFrequencies(); 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 getNumChannels(void) { return num_channels; }; signed int getFramesPerPacket(void); 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 readRegister(fb_nodeaddr_t reg); signed int readBlock(fb_nodeaddr_t reg, quadlet_t *buf, unsigned int n_quads); signed int writeRegister(fb_nodeaddr_t reg, quadlet_t data); signed int writeBlock(fb_nodeaddr_t reg, quadlet_t *data, unsigned int n_quads); /* General information functions */ signed int getDigidesignModel(void) { return m_digidesign_model; } protected: enum EDigidesignModel m_digidesign_model; // The following two members may not be required for Digidesign hardware. // It depends on whether the information is required in multiple places. // If it turns out that this isn't needed they can be removed. signed int num_channels; signed int frames_per_packet; // 1 frame includes 1 sample from each channel signed int iso_tx_channel, iso_rx_channel; Streaming::DigidesignReceiveStreamProcessor *m_receiveProcessor; Streaming::DigidesignTransmitStreamProcessor *m_transmitProcessor; }; } #endif libffado-2.4.5/src/fbtypes.h0000644000175000001440000000171714206145246015306 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FBTYPES_H #define FBTYPES_H #include //#warning "This file is deprecated. Please use ffadotypes.h instead." #endif libffado-2.4.5/src/ffado.cpp0000644000175000001440000003462014206145246015243 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Implementation of the FFADO external C API */ #include "version.h" #include "../libffado/ffado.h" #include "libstreaming/generic/StreamProcessor.h" #include "libstreaming/generic/Port.h" #include "debugmodule/debugmodule.h" #include "fbtypes.h" #include "devicemanager.h" #include "ffadodevice.h" #include #include #include #include #include DECLARE_GLOBAL_DEBUG_MODULE; IMPL_GLOBAL_DEBUG_MODULE( FFADO, DEBUG_LEVEL_VERBOSE ); #ifdef __cplusplus extern "C" { #endif // this is very much nescessary, as otherwise the // message buffer thread doesn't get killed when the // library is dlclose()'d static void exitfunc(void) __attribute__((destructor)); static void exitfunc(void) { delete DebugModuleManager::instance(); } #ifdef __cplusplus } #endif const char* ffado_get_version() { return PACKAGE_STRING; } int ffado_get_api_version() { return FFADO_API_VERSION; } struct _ffado_device { DeviceManager * m_deviceManager; ffado_options_t options; ffado_device_info_t device_info; }; ffado_device_t *ffado_streaming_init (ffado_device_info_t device_info, ffado_options_t options) { unsigned int i=0; setDebugLevel(options.verbose); struct _ffado_device *dev = new struct _ffado_device; printMessage("%s built %s %s\n", ffado_get_version(), __DATE__, __TIME__); #if DEBUG_USE_MESSAGE_BUFFER // ok #else printMessage("FFADO built without realtime-safe message buffer support. This can cause xruns and is not recommended.\n"); #endif if(!dev) { debugFatal( "Could not allocate streaming device\n" ); return 0; } memcpy((void *)&dev->options, (void *)&options, sizeof(dev->options)); dev->m_deviceManager = new DeviceManager(); if ( !dev->m_deviceManager ) { debugFatal( "Could not allocate device manager\n" ); delete dev; return 0; } dev->m_deviceManager->setVerboseLevel(dev->options.verbose); if(dev->options.realtime) { debugOutput(DEBUG_LEVEL_VERBOSE, "Starting with realtime scheduling, base priority %d\n", dev->options.packetizer_priority); } else { debugWarning("Realtime scheduling is not enabled. This will cause significant reliability issues.\n"); } dev->m_deviceManager->setThreadParameters(dev->options.realtime, dev->options.packetizer_priority); for (i = 0; i < device_info.nb_device_spec_strings; i++) { char *s = device_info.device_spec_strings[i]; if ( !dev->m_deviceManager->addSpecString(s) ) { debugFatal( "Could not add spec string %s to device manager\n", s ); delete dev->m_deviceManager; delete dev; return 0; } } // create a processor manager to manage the actual stream // processors if ( !dev->m_deviceManager->setStreamingParams(dev->options.period_size, dev->options.sample_rate, dev->options.nb_buffers)) { debugFatal( "Could not set streaming parameters of device manager\n" ); delete dev->m_deviceManager; delete dev; return 0; } // set slave mode option bool slaveMode=(dev->options.slave_mode != 0); debugOutput(DEBUG_LEVEL_VERBOSE, "setting slave mode to %d\n", slaveMode); if(!dev->m_deviceManager->setOption("slaveMode", slaveMode)) { debugWarning("Failed to set slave mode option\n"); } // set snoop mode option bool snoopMode=(dev->options.snoop_mode != 0); debugOutput(DEBUG_LEVEL_VERBOSE, "setting snoop mode to %d\n", snoopMode); if(!dev->m_deviceManager->setOption("snoopMode", snoopMode)) { debugWarning("Failed to set snoop mode option\n"); } if ( !dev->m_deviceManager->initialize() ) { debugFatal( "Could not initialize device manager\n" ); delete dev->m_deviceManager; delete dev; return 0; } // discover the devices on the bus if(!dev->m_deviceManager->discover()) { debugFatal("Could not discover devices\n"); delete dev->m_deviceManager; delete dev; return 0; } // are there devices on the bus? if(dev->m_deviceManager->getAvDeviceCount() == 0) { debugFatal("There are no devices on the bus\n"); delete dev->m_deviceManager; delete dev; return 0; } // prepare here or there are no ports for jack if(!dev->m_deviceManager->initStreaming()) { debugFatal("Could not init the streaming system\n"); return 0; } // we are ready! return dev; } int ffado_streaming_set_period_size(ffado_device_t *dev, unsigned int period) { if (!dev->m_deviceManager->setPeriodSize(period)) { debugFatal( "Could not set period size of device manager\n" ); return -1; } return 0; } int ffado_streaming_prepare(ffado_device_t *dev) { debugOutput(DEBUG_LEVEL_VERBOSE, "Preparing...\n"); // prepare here or there are no ports for jack if(!dev->m_deviceManager->prepareStreaming()) { debugFatal("Could not prepare the streaming system\n"); return -1; } return 0; } void ffado_streaming_finish(ffado_device_t *dev) { assert(dev); if(!dev->m_deviceManager->finishStreaming()) { debugError("Could not finish the streaming\n"); } delete dev->m_deviceManager; delete dev; return; } int ffado_streaming_start(ffado_device_t *dev) { debugOutput(DEBUG_LEVEL_VERBOSE,"------------- Start -------------\n"); if(!dev->m_deviceManager->startStreaming()) { debugFatal("Could not start the streaming system\n"); return -1; } return 0; } int ffado_streaming_stop(ffado_device_t *dev) { debugOutput(DEBUG_LEVEL_VERBOSE,"------------- Stop -------------\n"); if(!dev->m_deviceManager->stopStreaming()) { debugFatal("Could not stop the streaming system\n"); return -1; } return 0; } int ffado_streaming_reset(ffado_device_t *dev) { debugOutput(DEBUG_LEVEL_VERBOSE,"------------- Reset -------------\n"); if(!dev->m_deviceManager->resetStreaming()) { debugFatal("Could not reset the streaming system\n"); return -1; } return 0; } ffado_wait_response ffado_streaming_wait(ffado_device_t *dev) { static int periods=0; static int periods_print=0; static int xruns=0; periods++; if(periods>periods_print) { debugOutputShort(DEBUG_LEVEL_NORMAL, "\nffado_streaming_wait\n"); debugOutputShort(DEBUG_LEVEL_NORMAL, "============================================\n"); debugOutputShort(DEBUG_LEVEL_NORMAL, "Xruns: %d\n", xruns); debugOutputShort(DEBUG_LEVEL_NORMAL, "============================================\n"); dev->m_deviceManager->showStreamingInfo(); debugOutputShort(DEBUG_LEVEL_NORMAL, "\n"); periods_print+=100; } enum DeviceManager::eWaitResult result; result = dev->m_deviceManager->waitForPeriod(); if(result == DeviceManager::eWR_OK) { return ffado_wait_ok; } else if (result == DeviceManager::eWR_Xrun) { debugOutput(DEBUG_LEVEL_NORMAL, "Handled XRUN\n"); xruns++; return ffado_wait_xrun; } else if (result == DeviceManager::eWR_Shutdown) { debugWarning("Streaming system requests shutdown.\n"); return ffado_wait_shutdown; } else { debugError("Error condition while waiting (Unhandled XRUN)\n"); xruns++; return ffado_wait_error; } } int ffado_streaming_transfer_capture_buffers(ffado_device_t *dev) { return dev->m_deviceManager->getStreamProcessorManager().transfer(Streaming::StreamProcessor::ePT_Receive); } int ffado_streaming_transfer_playback_buffers(ffado_device_t *dev) { return dev->m_deviceManager->getStreamProcessorManager().transfer(Streaming::StreamProcessor::ePT_Transmit); } int ffado_streaming_transfer_buffers(ffado_device_t *dev) { return dev->m_deviceManager->getStreamProcessorManager().transfer(); } int ffado_streaming_get_nb_capture_streams(ffado_device_t *dev) { return dev->m_deviceManager->getStreamProcessorManager().getPortCount(Streaming::Port::E_Capture); } int ffado_streaming_get_nb_playback_streams(ffado_device_t *dev) { return dev->m_deviceManager->getStreamProcessorManager().getPortCount(Streaming::Port::E_Playback); } int ffado_streaming_get_capture_stream_name(ffado_device_t *dev, int i, char* buffer, size_t buffersize) { Streaming::Port *p = dev->m_deviceManager->getStreamProcessorManager().getPortByIndex(i, Streaming::Port::E_Capture); if(!p) { debugWarning("Could not get capture port at index %d\n",i); return -1; } std::string name=p->getName(); if (!strncpy(buffer, name.c_str(), buffersize)) { debugWarning("Could not copy name\n"); return -1; } else return 0; } int ffado_streaming_get_playback_stream_name(ffado_device_t *dev, int i, char* buffer, size_t buffersize) { Streaming::Port *p = dev->m_deviceManager->getStreamProcessorManager().getPortByIndex(i, Streaming::Port::E_Playback); if(!p) { debugWarning("Could not get playback port at index %d\n",i); return -1; } std::string name=p->getName(); if (!strncpy(buffer, name.c_str(), buffersize)) { debugWarning("Could not copy name\n"); return -1; } else return 0; } ffado_streaming_stream_type ffado_streaming_get_capture_stream_type(ffado_device_t *dev, int i) { Streaming::Port *p = dev->m_deviceManager->getStreamProcessorManager().getPortByIndex(i, Streaming::Port::E_Capture); if(!p) { debugWarning("Could not get capture port at index %d\n",i); return ffado_stream_type_invalid; } switch(p->getPortType()) { case Streaming::Port::E_Audio: return ffado_stream_type_audio; case Streaming::Port::E_Midi: return ffado_stream_type_midi; case Streaming::Port::E_Control: return ffado_stream_type_control; default: return ffado_stream_type_unknown; } } ffado_streaming_stream_type ffado_streaming_get_playback_stream_type(ffado_device_t *dev, int i) { Streaming::Port *p = dev->m_deviceManager->getStreamProcessorManager().getPortByIndex(i, Streaming::Port::E_Playback); if(!p) { debugWarning("Could not get playback port at index %d\n",i); return ffado_stream_type_invalid; } switch(p->getPortType()) { case Streaming::Port::E_Audio: return ffado_stream_type_audio; case Streaming::Port::E_Midi: return ffado_stream_type_midi; case Streaming::Port::E_Control: return ffado_stream_type_control; default: return ffado_stream_type_unknown; } } int ffado_streaming_set_audio_datatype(ffado_device_t *dev, ffado_streaming_audio_datatype t) { switch(t) { case ffado_audio_datatype_int24: if(!dev->m_deviceManager->getStreamProcessorManager().setAudioDataType( Streaming::StreamProcessorManager::eADT_Int24)) { debugError("Could not set datatype\n"); return -1; } break; case ffado_audio_datatype_float: if(!dev->m_deviceManager->getStreamProcessorManager().setAudioDataType( Streaming::StreamProcessorManager::eADT_Float)) { debugError("Could not set datatype\n"); return -1; } break; default: debugError("Invalid audio datatype\n"); return -1; } return 0; } ffado_streaming_audio_datatype ffado_streaming_get_audio_datatype(ffado_device_t *dev) { switch(dev->m_deviceManager->getStreamProcessorManager().getAudioDataType()) { case Streaming::StreamProcessorManager::eADT_Int24: return ffado_audio_datatype_int24; case Streaming::StreamProcessorManager::eADT_Float: return ffado_audio_datatype_float; default: debugError("Invalid audio datatype\n"); return ffado_audio_datatype_error; } } int ffado_streaming_stream_onoff(ffado_device_t *dev, int i, int on, enum Streaming::Port::E_Direction direction) { Streaming::Port *p = dev->m_deviceManager->getStreamProcessorManager().getPortByIndex(i, direction); if(!p) { debugWarning("Could not get %s port at index %d\n", (direction==Streaming::Port::E_Playback?"Playback":"Capture"),i); return -1; } if(on) { p->enable(); } else { p->disable(); } return 0; } int ffado_streaming_playback_stream_onoff(ffado_device_t *dev, int number, int on) { return ffado_streaming_stream_onoff(dev, number, on, Streaming::Port::E_Playback); } int ffado_streaming_capture_stream_onoff(ffado_device_t *dev, int number, int on) { return ffado_streaming_stream_onoff(dev, number, on, Streaming::Port::E_Capture); } int ffado_streaming_set_capture_stream_buffer(ffado_device_t *dev, int i, char *buff) { Streaming::Port *p = dev->m_deviceManager->getStreamProcessorManager().getPortByIndex(i, Streaming::Port::E_Capture); // use an assert here performancewise, // it should already have failed before, if not correct assert(p); p->setBufferAddress((void *)buff); return 0; } int ffado_streaming_set_playback_stream_buffer(ffado_device_t *dev, int i, char *buff) { Streaming::Port *p = dev->m_deviceManager->getStreamProcessorManager().getPortByIndex(i, Streaming::Port::E_Playback); // use an assert here performancewise, // it should already have failed before, if not correct assert(p); p->setBufferAddress((void *)buff); return 0; } libffado-2.4.5/src/ffadodevice.cpp0000644000175000001440000001564214206145246016426 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ffadodevice.h" #include "devicemanager.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libcontrol/Element.h" #include "libcontrol/ClockSelect.h" #include "libcontrol/Nickname.h" #include #include #include #include IMPL_DEBUG_MODULE( FFADODevice, FFADODevice, DEBUG_LEVEL_NORMAL ); FFADODevice::FFADODevice( DeviceManager& d, ffado_smartptr( configRom ) ) : Control::Container(&d) , m_pConfigRom( configRom ) , m_pDeviceManager( d ) { addOption(Util::OptionContainer::Option("id",m_pConfigRom->getGuidString())); std::ostringstream nodestr; nodestr << "node" << getConfigRom().getNodeId(); if (!addElement(&getConfigRom())) { debugWarning("failed to add ConfigRom to Control::Container\n"); } m_genericContainer = new Control::Container(this, "Generic"); if(m_genericContainer == NULL) { debugError("Could not create Control::Container for generic controls\n"); } else { if (!addElement(m_genericContainer)) { debugWarning("failed to add generic container to Control::Container\n"); } // add a generic control for the clock source selection if(!m_genericContainer->addElement(new Control::ClockSelect(*this))) { debugWarning("failed to add clock source control to container\n"); } // add a generic control for the sample rate selection if(!m_genericContainer->addElement(new Control::SamplerateSelect(*this))) { debugWarning("failed to add sample rate control to container\n"); } // add a generic control for the nickname if(!m_genericContainer->addElement(new Control::Nickname(*this))) { debugWarning("failed to add Nickname control to container\n"); } // add a generic control for the streaming status if(!m_genericContainer->addElement(new Control::StreamingStatus(*this))) { debugWarning("failed to add StreamingStatus control to container\n"); } } } FFADODevice::~FFADODevice() { if (!deleteElement(&getConfigRom())) { debugWarning("failed to remove ConfigRom from Control::Container\n"); } // remove generic controls if present if(m_genericContainer) { if (!deleteElement(m_genericContainer)) { debugError("Generic controls present but not registered to the avdevice\n"); } // remove and delete (as in free) child control elements m_genericContainer->clearElements(true); delete m_genericContainer; } } FFADODevice * FFADODevice::createDevice(ffado_smartptr( 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; } bool FFADODevice::needsRediscovery() { // require rediscovery by default return true; } enum FFADODevice::eSyncState FFADODevice::getSyncState( ) { return eSS_Unknown; } enum FFADODevice::eStreamingState FFADODevice::getStreamingState() { return eSS_Idle; } bool FFADODevice::setNickname( std::string name) { return false; } std::string FFADODevice::getNickname() { return "Unsupported"; } bool FFADODevice::canChangeNickname() { return false; } void FFADODevice::handleBusReset() { debugOutput( DEBUG_LEVEL_VERBOSE, "Handle bus reset...\n"); // update the config rom node id sleep(1); Util::MutexLockHelper lock(m_DeviceMutex); getConfigRom().setVerboseLevel(getDebugLevel()); getConfigRom().updatedNodeId(); } void FFADODevice::setVerboseLevel(int l) { debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); setDebugLevel(l); m_DeviceMutex.setVerboseLevel(l); getConfigRom().setVerboseLevel(l); } void FFADODevice::showDevice() { #ifdef DEBUG Ieee1394Service& s = getConfigRom().get1394Service(); debugOutput(DEBUG_LEVEL_NORMAL, "Attached to port.......: %d (%s)\n", s.getPort(), s.getPortName().c_str()); debugOutput(DEBUG_LEVEL_NORMAL, "Node...................: %d\n", getNodeId()); debugOutput(DEBUG_LEVEL_NORMAL, "Vendor name............: %s\n", getConfigRom().getVendorName().c_str()); debugOutput(DEBUG_LEVEL_NORMAL, "Model name.............: %s\n", getConfigRom().getModelName().c_str()); debugOutput(DEBUG_LEVEL_NORMAL, "GUID...................: %s\n", getConfigRom().getGuidString().c_str()); std::string id=std::string("dev? [none]"); getOption("id", id); debugOutput(DEBUG_LEVEL_NORMAL, "Assigned ID....: %s\n", id.c_str()); flushDebugOutput(); #endif } bool FFADODevice::enableStreaming() { return true; } bool FFADODevice::disableStreaming() { return true; } const char * FFADODevice::ClockSourceTypeToString(enum eClockSourceType t) { switch(t) { default: return "Erratic type "; case eCT_Invalid: return "Invalid "; case eCT_Internal: return "Internal "; case eCT_1394Bus: return "1394 Bus "; case eCT_SytMatch: return "Compound Syt Match"; case eCT_SytStream: return "Sync Syt Match "; case eCT_WordClock: return "WordClock "; case eCT_SPDIF: return "SPDIF "; case eCT_ADAT: return "ADAT "; case eCT_TDIF: return "TDIF "; case eCT_AES: return "AES "; } } libffado-2.4.5/src/ffadodevice.h0000644000175000001440000003706114206145246016072 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FFADODEVICE_H #define FFADODEVICE_H #include "libutil/OptionContainer.h" #include "libutil/PosixMutex.h" #include "libcontrol/BasicElements.h" #include "libieee1394/vendor_model_ids.h" #include #include #include // Prefer shared_ptr over auto_ptr if it is available #if __cplusplus >= 201103L #define ffado_smartptr std::shared_ptr #else #define ffado_smartptr std::auto_ptr #endif class DeviceManager; class ConfigRom; class Ieee1394Service; namespace Streaming { class StreamProcessor; } namespace Control { class Container; } /*! @brief Base class for device support This class should be subclassed to implement ffado support for a specific device. */ class FFADODevice : public Util::OptionContainer, public Control::Container { public: FFADODevice( DeviceManager&, ffado_smartptr< 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 a is larger than the GUID of 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 a is larger than the GUID of 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 Called by DeviceManager to check whether a device requires rediscovery * * This function is called to figure out if the device has to be rediscovered * e.g. after a bus reset where the device internal structure changed. * * @returns true if device requires rediscovery */ virtual bool needsRediscovery(); /** * @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( ffado_smartptr( x )); /** * @brief This is called by the DeviceManager to discover & configure the device * * @return true if the device was discovered successfully */ 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 Some devices require to update part of their internal configuration on samplerate change * Most do not, so set default to false * @param oldSamplingFrequency : to be compared to the actual sampling frequency of the device * note: next, it is thus assumed that the device was actually set to some new sampling frequency * (while the client did not yet update its view of the device). * In some future, it might very well appear that restricting to this single argument is * unsufficient for some devices, so feel free to adapt accordingly. * * @return true if changes have been performed */ virtual bool onSamplerateChange( int oldSamplingFrequency ) { return false; } /** * @brief get the supported sampling frequencies * @return a vector containing the supported sampling frequencies */ virtual std::vector getSupportedSamplingFrequencies( ) = 0; /** * @brief sync state enum */ enum eSyncState { eSS_Unknown=0, eSS_Locked=1, eSS_Unlocked=2, }; /** * @brief gets the devices current synchronization state * @return the device's sync state */ virtual enum eSyncState getSyncState( ); /** * @brief clock source types */ enum eClockSourceType { eCT_Invalid, ///> invalid entry (e.g. on error) eCT_Auto, ///> automatically select clock source eCT_Internal, ///> internal sync (unspecified) eCT_1394Bus, ///> Sync on the 1394 bus clock (e.g. CSP) eCT_SytMatch, ///> SYT match on incoming audio stream eCT_SytStream, ///> SYT match on incoming sync stream eCT_WordClock, ///> SYT on WordClock input eCT_SPDIF, ///> SYT on SPDIF input eCT_ADAT, ///> SYT on ADAT input eCT_TDIF, ///> SYT on TDIF input eCT_AES, ///> SYT on AES input eCT_SMPTE, ///> SMPTE clock }; /** * @brief convert the clock source type to a C string * @return a C string describing the clock source type */ static const char *ClockSourceTypeToString(enum eClockSourceType); /** * @brief Clock source identification struct */ class ClockSource { public: ClockSource() : type( eCT_Invalid ) , id( 0 ) , valid( false ) , active( false ) , locked( true ) , slipping( false ) , description( "" ) {}; /// indicates the type of the clock source (e.g. eCT_ADAT) enum eClockSourceType type; /// indicated the id of the clock source (e.g. id=1 => clocksource is ADAT_1) unsigned int id; /// is the clock source valid (i.e. can be selected) at this moment? bool valid; /// is the clock source active at this moment? bool active; /// is the clock source locked? bool locked; /// is the clock source slipping? bool slipping; /// description of the clock struct (optional) std::string description; bool operator==(const ClockSource& x) { return (type == x.type) && (id == x.id); } }; typedef std::vector< ClockSource > ClockSourceVector; typedef std::vector< ClockSource >::iterator ClockSourceVectorIterator; /** * @brief Get the clocksources supported by this device * * This function returns a vector of ClockSource structures that contains * one entry for every clock source supported by the device. * * @returns a vector of ClockSource structures */ virtual ClockSourceVector getSupportedClockSources() = 0; /** * @brief Sets the active clock source of this device * * This function sets the clock source of the device. * * @returns true upon success. false upon failure. */ virtual bool setActiveClockSource(ClockSource) = 0; /** * @brief Returns the active clock source of this device * * This function returns the active clock source of the device. * * @returns the active ClockSource */ virtual ClockSource getActiveClockSource() = 0; /** * @brief stream states */ enum eStreamingState { eSS_Idle = 0, ///> not streaming eSS_Sending = 1, ///> the device is sending a stream eSS_Receiving = 2, ///> the device is receiving a stream eSS_Both = 3, ///> the device is sending and receiving a stream }; /** * @brief gets the devices current synchronization state * @return the device's sync state */ virtual enum eStreamingState getStreamingState(); /** * @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 Performs operations needed to prepare for a stream start * * This is called by the streaming layer just before streaming is * started. It provides a place where reset activity can be done which * ensures the object is ready to restart streaming even if streaming * has been previously started and stopped. * * @return true if successful, false if not */ virtual bool resetForStreaming() { return true; } /** * @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 (i has to be 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 (i has to be smaller than getStreamCount()) * @return true if successful, false if not */ virtual bool startStreamByIndex(int i) = 0; /** * @brief stops the stream with index i * * @param i : Stream index (i has to be smaller than getStreamCount()) * @return true if successful, false if not */ virtual bool stopStreamByIndex(int i) = 0; /** * set verbosity level */ virtual void setVerboseLevel(int l); /** * @brief return the node id of this device * * @return the node id */ int getNodeId(); /** * @brief return the nick name of this device * * @return string containing the name */ virtual std::string getNickname(); /** * @brief set the nick name of this device * @param name new nickname * @return true if successful */ virtual bool setNickname(std::string name); /** * @brief return whether the nick name of this device can be changed * * @return true if the nick can be changed */ virtual bool canChangeNickname(); /** * @brief handle a bus reset * * Called whenever a bus reset is detected. Handle everything * that has to be done to cope with a bus reset. * */ // FIXME: not virtual? void handleBusReset(); // the Control::Container functions virtual std::string getName(); virtual bool setName( std::string n ) { return false; }; DeviceManager& getDeviceManager() {return m_pDeviceManager;}; private: ffado_smartptr( m_pConfigRom ); DeviceManager& m_pDeviceManager; Control::Container* m_genericContainer; protected: DECLARE_DEBUG_MODULE; Util::PosixMutex m_DeviceMutex; }; #endif libffado-2.4.5/src/ffadotypes.h0000644000175000001440000000452614206145246015777 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * (C) 2009-2009 by Arnold Krille * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FFADOTYPES_H #define FFADOTYPES_H #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include #include #define INVALID_NODE_ID 0xFF typedef quadlet_t fb_quadlet_t; typedef byte_t fb_byte_t; typedef octlet_t fb_octlet_t; typedef nodeid_t fb_nodeid_t; typedef nodeaddr_t fb_nodeaddr_t; #define FORMAT_FB_OCTLET_T "0x%016" PRIX64 #define FORMAT_FB_NODEID_T "0x%016" PRIX64 #define FORMAT_FB_NODEADDR_T "0x%016" PRIX64 class DeviceManager; struct ffado_handle { DeviceManager* m_deviceManager; }; #include #include #include #include class stringlist : public std::vector { public: static stringlist splitString(std::string in, std::string delimiter) { stringlist result; size_type start = 0, end = 0; while ( start < in.size() ) { end = std::min(in.size(), in.find(delimiter, start)); result.push_back(in.substr(start, end-start)); start = end+delimiter.size(); } return result; } std::string join(std::string joiner ="") { std::string result; for ( stringlist::iterator it=begin(); it!=end(); ) { result += *it; it++; if ( it!=end() ) { result += joiner; } } return result; } }; #endif libffado-2.4.5/src/fireworks/0000755000175000001440000000000014206145612015463 5ustar jwoitheuserslibffado-2.4.5/src/fireworks/audiofire/0000755000175000001440000000000014206145612017432 5ustar jwoitheuserslibffado-2.4.5/src/fireworks/audiofire/audiofire_device.cpp0000644000175000001440000000265114206145246023433 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "audiofire_device.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" namespace FireWorks { namespace ECHO { AudioFire::AudioFire( DeviceManager& d, ffado_smartptr( configRom )) : FireWorks::Device( d, configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created FireWorks::ECHO::AudioFire (NodeID %d)\n", getConfigRom().getNodeId() ); } AudioFire::~AudioFire() { } void AudioFire::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a FireWorks::ECHO::AudioFire\n"); FireWorks::Device::showDevice(); } } // ECHO } // FireWorks libffado-2.4.5/src/fireworks/audiofire/audiofire_device.h0000644000175000001440000000243214206145246023075 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIREWORKS_ECHO_DEVICE_H #define FIREWORKS_ECHO_DEVICE_H #include "debugmodule/debugmodule.h" #include "fireworks/fireworks_device.h" namespace FireWorks { namespace ECHO { class AudioFire : public FireWorks::Device { public: AudioFire( DeviceManager& d, ffado_smartptr( configRom )); virtual ~AudioFire(); virtual void showDevice(); }; } // namespace ECHO } // namespace FireWorks #endif libffado-2.4.5/src/fireworks/efc/0000755000175000001440000000000014206145612016220 5ustar jwoitheuserslibffado-2.4.5/src/fireworks/efc/efc_avc_cmd.cpp0000644000175000001440000000434214206145246021143 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "efc_avc_cmd.h" #include "libutil/ByteSwap.h" #include using namespace std; using namespace AVC; namespace FireWorks { EfcOverAVCCmd::EfcOverAVCCmd(Ieee1394Service& ieee1394service) : VendorDependentCmd( ieee1394service ) , m_dummy_1 ( 0 ) , m_dummy_2 ( 0 ) , m_cmd( NULL ) { m_companyId=0x0; } EfcOverAVCCmd::~EfcOverAVCCmd() { } bool EfcOverAVCCmd::serialize( Util::Cmd::IOSSerialize& se ) { if (m_cmd==NULL) { debugError("no child EFC command\n"); return false; } bool result=true; result &= VendorDependentCmd::serialize( se ); result &= se.write(m_dummy_1, "Dummy byte 1"); result &= se.write(m_dummy_2, "Dummy byte 1"); result &= m_cmd->serialize( se ); if(!result) { debugWarning("Serialization failed\n"); } return result; } bool EfcOverAVCCmd::deserialize( Util::Cmd::IISDeserialize& de ) { if (m_cmd==NULL) { debugError("no child EFC command\n"); return false; } bool result=true; result &= VendorDependentCmd::deserialize( de ); result &= de.read(&m_dummy_1); result &= de.read(&m_dummy_2); if(!result) { debugWarning("AV/C deserialization failed\n"); return false; } result &= m_cmd->deserialize( de ); if(!result) { debugWarning("Deserialization failed\n"); } return result; } } // namespace FireWorks libffado-2.4.5/src/fireworks/efc/efc_avc_cmd.h0000644000175000001440000000302014206145246020600 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIREWORKS_EFC_AVC_H #define FIREWORKS_EFC_AVC_H #include "libavc/general/avc_generic.h" #include "libutil/cmd_serialize.h" #include "libavc/general/avc_vendor_dependent_cmd.h" #include "efc_cmd.h" namespace FireWorks { class EfcOverAVCCmd: public AVC::VendorDependentCmd { public: EfcOverAVCCmd(Ieee1394Service& ieee1394service); virtual ~EfcOverAVCCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcOverAVCCmd"; } uint8_t m_dummy_1; uint8_t m_dummy_2; EfcCmd* m_cmd; }; } // namespace FireWorks #endif // FIREWORKS_EFC_AVC_H libffado-2.4.5/src/fireworks/efc/efc_cmd.cpp0000644000175000001440000001245314206145246020314 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "efc_cmd.h" #include "efc_cmds_hardware.h" #include "libutil/ByteSwap.h" #include #include using namespace std; namespace FireWorks { IMPL_DEBUG_MODULE( EfcCmd, EfcCmd, DEBUG_LEVEL_NORMAL ); // static int to keep track of the sequence index uint32_t EfcCmd::m_seqnum = 1; // some generic string generation functions const char *eMixerTargetToString(const enum eMixerTarget target) { switch (target) { case eMT_PhysicalOutputMix: return "PhysicalOutputMix"; case eMT_PhysicalInputMix: return "PhysicalInputMix"; case eMT_PlaybackMix: return "PlaybackMix"; case eMT_RecordMix: return "RecordMix"; default: return "invalid"; } } const char *eMixerCommandToString(const enum eMixerCommand command) { switch (command) { case eMC_Gain: return "Gain"; case eMC_Solo: return "Solo"; case eMC_Mute: return "Mute"; case eMC_Pan: return "Pan"; case eMC_Nominal: return "Nominal"; default: return "invalid"; } } const char *eIOConfigRegisterToString(const enum eIOConfigRegister reg) { switch (reg) { case eCR_Mirror: return "Mirror"; case eCR_DigitalInterface: return "DigitalInterface"; case eCR_Phantom: return "Phantom"; case eCR_IsocMap: return "IsocMap"; default: return "invalid"; } } // the real deal EfcCmd::EfcCmd(uint32_t cat, uint32_t cmd) : m_length ( 0 ) , m_category_id ( cat ) , m_command_id ( cmd ) { memset(&m_header,0,sizeof(m_header)); } EfcCmd::EfcCmd() : m_length ( 0 ) , m_category_id ( EFC_CAT_INVALID ) , m_command_id ( EFC_CMD_INVALID ) { memset(&m_header,0,sizeof(m_header)); } EfcCmd::~EfcCmd() { } bool EfcCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= se.write(CondSwapToBus32(m_length), "EFC length"); unsigned int i=0; // assign the category and the command m_header.category=m_category_id; m_header.command=m_command_id; // set the sequence number m_header.seqnum=m_seqnum++; // serialize the header quadlet_t *header_as_quadlets=(quadlet_t *)&m_header; result &= se.write(CondSwapToBus32(*(header_as_quadlets+i)), "EFC header version"); i++; result &= se.write(CondSwapToBus32(*(header_as_quadlets+i)), "EFC header seqnum"); i++; result &= se.write(CondSwapToBus32(*(header_as_quadlets+i)), "EFC header category"); i++; result &= se.write(CondSwapToBus32(*(header_as_quadlets+i)), "EFC header command"); i++; result &= se.write(CondSwapToBus32(*(header_as_quadlets+i)), "EFC header return value"); i++; return result; } bool EfcCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= de.read(&m_length); m_length=CondSwapFromBus32(m_length); // read the EFC header quadlet_t *header_as_quadlets=(quadlet_t *)&m_header; for (unsigned int i=0; i 1) { debugError("Unsupported EFC version: %d\n", m_header.version); return false; } // check whether the category and command of the response are valid if (m_header.category != m_category_id) { debugError("Invalid category response: %d != %d\n", m_header.category, m_category_id); return false; } if (m_header.command != m_command_id) { debugError("Invalid command response: %d != %d\n", m_header.command, m_command_id); return false; } return result; } void EfcCmd::showEfcCmd() { debugOutput(DEBUG_LEVEL_NORMAL, "EFC Length: %d\n", m_length); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Header:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Version : 0x%08X\n", m_header.version); debugOutput(DEBUG_LEVEL_NORMAL, " Sequence number : 0x%08X\n", m_header.seqnum); debugOutput(DEBUG_LEVEL_NORMAL, " Category : 0x%08X\n", m_header.category); debugOutput(DEBUG_LEVEL_NORMAL, " Command : 0x%08X\n", m_header.command); debugOutput(DEBUG_LEVEL_NORMAL, " Return Value : 0x%08X\n", m_header.retval); } void EfcCmd::setVerboseLevel(int l) { setDebugLevel(l); } } // namespace FireWorks libffado-2.4.5/src/fireworks/efc/efc_cmd.h0000644000175000001440000001667214206145246017770 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIREWORKS_EFC_CMD_H #define FIREWORKS_EFC_CMD_H #include "debugmodule/debugmodule.h" #include "libutil/cmd_serialize.h" #define EFC_CAT_INVALID 0xFFFFFFFF #define EFC_CMD_INVALID 0xFFFFFFFF // Categories #define EFC_CAT_HARDWARE_INFO 0 #define EFC_CAT_FLASH 1 #define EFC_CAT_TRANSPORT 2 #define EFC_CAT_HARDWARE_CONTROL 3 #define EFC_CAT_PHYSICAL_OUTPUT_MIX 4 #define EFC_CAT_PHYSICAL_INPUT_MIX 5 #define EFC_CAT_PLAYBACK_MIX 6 #define EFC_CAT_RECORD_MIX 7 #define EFC_CAT_MONITOR_MIX 8 #define EFC_CAT_IO_CONFIG 9 #define EFC_CAT_COUNT 10 // Commands for the EFC_CAT_HARDWARE_INFO category #define EFC_CMD_HW_HWINFO_GET_CAPS 0 #define EFC_CMD_HW_GET_POLLED 1 #define EFC_CMD_HW_SET_EFR_ADDRESS 2 #define EFC_CMD_HW_READ_SESSION_BLOCK 3 #define EFC_CMD_HW_GET_DEBUG_INFO 4 #define EFC_CMD_HW_SET_DEBUG_TRACKING 5 #define EFC_CMD_HW_COUNT 6 // Commands for the EFC_CAT_FLASH category #define EFC_CMD_FLASH_ERASE 0 #define EFC_CMD_FLASH_READ 1 #define EFC_CMD_FLASH_WRITE 2 #define EFC_CMD_FLASH_GET_STATUS 3 #define EFC_CMD_FLASH_GET_SESSION_BASE 4 #define EFC_CMD_FLASH_LOCK 5 // Commands for the EFC_CAT_HARDWARE_CONTROL category #define EFC_CMD_HWCTRL_SET_CLOCK 0 #define EFC_CMD_HWCTRL_GET_CLOCK 1 #define EFC_CMD_HWCTRL_BSX_HANDSHAKE 2 #define EFC_CMD_HWCTRL_CHANGE_FLAGS 3 #define EFC_CMD_HWCTRL_GET_FLAGS 4 #define EFC_CMD_HWCTRL_IDENTIFY 5 #define EFC_CMD_HWCTRL_RECONNECT_PHY 6 // Commands for the EFC_CAT_*_MIX categories #define EFC_CMD_MIXER_SET_GAIN 0 #define EFC_CMD_MIXER_GET_GAIN 1 #define EFC_CMD_MIXER_SET_MUTE 2 #define EFC_CMD_MIXER_GET_MUTE 3 #define EFC_CMD_MIXER_SET_SOLO 4 #define EFC_CMD_MIXER_GET_SOLO 5 #define EFC_CMD_MIXER_SET_PAN 6 #define EFC_CMD_MIXER_GET_PAN 7 #define EFC_CMD_MIXER_SET_NOMINAL 8 #define EFC_CMD_MIXER_GET_NOMINAL 9 // Commands for the EFC_CAT_IO_CONFIG category #define EFC_CMD_IO_CONFIG_SET_MIRROR 0 #define EFC_CMD_IO_CONFIG_GET_MIRROR 1 #define EFC_CMD_IO_CONFIG_SET_DIGITAL_MODE 2 #define EFC_CMD_IO_CONFIG_GET_DIGITAL_MODE 3 #define EFC_CMD_IO_CONFIG_SET_PHANTOM 4 #define EFC_CMD_IO_CONFIG_GET_PHANTOM 5 #define EFC_CMD_IO_CONFIG_SET_ISOC_MAP 6 #define EFC_CMD_IO_CONFIG_GET_ISOC_MAP 7 // size of the header #define EFC_HEADER_LENGTH_QUADLETS ((sizeof(uint32_t) + sizeof(struct EfcCmd::efc_header))/4) // util macro to do deserialization and byteswap #define EFC_DESERIALIZE_AND_SWAP(__de__, __value__, __result__) \ { __result__ &= __de__.read(__value__); \ *(__value__)=CondSwapFromBus32(*(__value__)); } \ // specifiers for the flags field #define EFC_CMD_HW_DYNADDR_SUPPORTED 0 #define EFC_CMD_HW_MIRRORING_SUPPORTED 1 #define EFC_CMD_HW_OPTICAL_INTERFACE_SUPPORTED 2 #define EFC_CMD_HW_SPDIF_AESEBUXLR_SUPPORTED 3 #define EFC_CMD_HW_HAS_DSP 4 #define EFC_CMD_HW_HAS_FPGA 5 #define EFC_CMD_HW_HAS_PHANTOM 6 #define EFC_CMD_HW_HAS_PLAYBACK_ROUTING 7 #define EFC_CMD_HW_CHECK_FLAG(__val__,__flag__) \ (((__val__) & (1<<(__flag__))) != 0) #define EFC_CMD_HW_TO_FLAG(__val__) \ (1<<(__val__)) // Clock sources #define EFC_CMD_HW_CLOCK_INTERNAL 0 #define EFC_CMD_HW_CLOCK_SYTMATCH 1 #define EFC_CMD_HW_CLOCK_WORDCLOCK 2 #define EFC_CMD_HW_CLOCK_SPDIF 3 #define EFC_CMD_HW_CLOCK_ADAT_1 4 #define EFC_CMD_HW_CLOCK_ADAT_2 5 #define EFC_CMD_HW_CLOCK_COUNT 6 #define EFC_CMD_HW_CLOCK_UNSPECIFIED 0xFFFFFFFF // MIDI flags #define EFC_CMD_HW_MIDI_IN_1 8 #define EFC_CMD_HW_MIDI_OUT_1 9 #define EFC_CMD_HW_MIDI_IN_2 10 #define EFC_CMD_HW_MIDI_OUT_2 11 // Channel types #define EFC_CMD_HW_CHANNEL_TYPE_ANALOG 0 #define EFC_CMD_HW_CHANNEL_TYPE_SPDIF 1 #define EFC_CMD_HW_CHANNEL_TYPE_ADAT 2 #define EFC_CMD_HW_CHANNEL_TYPE_SPDIF_OR_ADAT 3 #define EFC_CMD_HW_CHANNEL_TYPE_ANALOG_MIRRORING 4 #define EFC_CMD_HW_CHANNEL_TYPE_HEADPHONES 5 #define EFC_CMD_HW_CHANNEL_TYPE_I2S 6 namespace FireWorks { enum eMixerTarget { eMT_PhysicalOutputMix, eMT_PhysicalInputMix, eMT_PlaybackMix, eMT_RecordMix, }; enum eMixerCommand { eMC_Gain, eMC_Solo, eMC_Mute, eMC_Pan, eMC_Nominal, }; enum eCmdType { eCT_Get, eCT_Set, }; enum eIOConfigRegister { eCR_Mirror, eCR_DigitalInterface, eCR_Phantom, eCR_IsocMap }; const char *eMixerTargetToString(const enum eMixerTarget target); const char *eMixerCommandToString(const enum eMixerCommand command); const char *eIOConfigRegisterToString(const enum eIOConfigRegister reg); class EfcCmd { public: enum EfcReturnValue { eERV_Ok = 0, eERV_Bad = 1, eERV_BadCommand = 2, eERV_CommErr = 3, eERV_BadQuadCount = 4, eERV_Unsupported = 5, eERV_1394Timeout = 6, eERV_DspTimeout = 7, eERV_BadRate = 8, eERV_BadClock = 9, eERV_BadChannel = 10, eERV_BadPan = 11, eERV_FlashBusy = 12, eERV_BadMirror = 13, eERV_BadLed = 14, eERV_BadParameter = 15, eERV_Incomplete = 0x80000000L, eERV_Unspecified = 0xFFFFFFFFL, }; struct efc_header { uint32_t version; uint32_t seqnum; uint32_t category; uint32_t command; uint32_t retval; }; protected: // this HAS to be overloaded EfcCmd(uint32_t cat, uint32_t cmd); EfcCmd(); public: virtual ~EfcCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const = 0; virtual void showEfcCmd(); virtual void setVerboseLevel(int l); uint32_t m_length; // in quadlets, including length field and header. struct efc_header m_header; protected: uint32_t m_category_id; uint32_t m_command_id; public: static uint32_t m_seqnum; protected: DECLARE_DEBUG_MODULE; }; } // namespace FireWorks #endif // FIREWORKS_EFC_CMD_H libffado-2.4.5/src/fireworks/efc/efc_cmds_flash.cpp0000644000175000001440000001655714206145246021665 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "efc_cmd.h" #include "efc_cmds_flash.h" #include "libutil/ByteSwap.h" #include using namespace std; namespace FireWorks { EfcFlashEraseCmd::EfcFlashEraseCmd() : EfcCmd(EFC_CAT_FLASH, EFC_CMD_FLASH_ERASE) , m_address ( 0xFFFFFFFF ) { } bool EfcFlashEraseCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS + 1; result &= EfcCmd::serialize ( se ); result &= se.write(CondSwapToBus32(m_address), "Address" ); return result; } bool EfcFlashEraseCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); return result; } void EfcFlashEraseCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Flash Erase:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Address : %u\n", m_address); } // ---- EfcFlashReadCmd::EfcFlashReadCmd() : EfcCmd(EFC_CAT_FLASH, EFC_CMD_FLASH_READ) , m_address ( 0xFFFFFFFF ) , m_nb_quadlets ( 0 ) { } bool EfcFlashReadCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS+2; result &= EfcCmd::serialize ( se ); result &= se.write(CondSwapToBus32(m_address), "Address" ); result &= se.write(CondSwapToBus32(m_nb_quadlets), "Length (quadlets)" ); return result; } bool EfcFlashReadCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); EFC_DESERIALIZE_AND_SWAP(de, &m_address, result); EFC_DESERIALIZE_AND_SWAP(de, &m_nb_quadlets, result); if (m_nb_quadlets > EFC_FLASH_SIZE_QUADS) { debugError("Too much quadlets returned: %u\n", m_nb_quadlets); return false; } for (unsigned int i=0; i < m_nb_quadlets; i++) { EFC_DESERIALIZE_AND_SWAP(de, &m_data[i], result); } return result; } void EfcFlashReadCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Flash Read:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Address : %u\n", m_address); debugOutput(DEBUG_LEVEL_NORMAL, " Length (quadlets) : %u\n", m_nb_quadlets); debugOutput(DEBUG_LEVEL_NORMAL, " Data : \n"); for (unsigned int i=0; i < m_nb_quadlets; i++) { debugOutput(DEBUG_LEVEL_NORMAL, " %08X \n", m_data[i]); } } // ---- EfcFlashWriteCmd::EfcFlashWriteCmd() : EfcCmd(EFC_CAT_FLASH, EFC_CMD_FLASH_WRITE) , m_address ( 0xFFFFFFFF ) , m_nb_quadlets ( 0 ) { } bool EfcFlashWriteCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; if (m_nb_quadlets > EFC_FLASH_SIZE_QUADS) { debugError("Too much quadlets to write: %u\n", m_nb_quadlets); return false; } // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS+2+m_nb_quadlets; result &= EfcCmd::serialize ( se ); result &= se.write(CondSwapToBus32(m_address), "Address" ); result &= se.write(CondSwapToBus32(m_nb_quadlets), "Length (quadlets)" ); for (unsigned int i=0; i < m_nb_quadlets; i++) { result &= se.write(CondSwapToBus32(m_data[i]), "Data"); } return result; } bool EfcFlashWriteCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); return result; } void EfcFlashWriteCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Flash Write:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Address : %u\n", m_address); debugOutput(DEBUG_LEVEL_NORMAL, " Length (quadlets) : %u\n", m_nb_quadlets); debugOutput(DEBUG_LEVEL_NORMAL, " Data : \n"); for (unsigned int i=0; i < m_nb_quadlets; i++) { debugOutput(DEBUG_LEVEL_NORMAL, " %08X \n", m_data[i]); } } // ------------------ EfcFlashLockCmd::EfcFlashLockCmd() : EfcCmd(EFC_CAT_FLASH, EFC_CMD_FLASH_LOCK) , m_lock ( false ) { } bool EfcFlashLockCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS + 1; result &= EfcCmd::serialize ( se ); result &= se.write(CondSwapToBus32(m_lock), "Locked" ); return result; } bool EfcFlashLockCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); //EFC_DESERIALIZE_AND_SWAP(de, &m_lock, result); return result; } void EfcFlashLockCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Flash Lock:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Locked : %s\n", (m_lock?"Yes":"No")); } // ------------------ EfcFlashGetStatusCmd::EfcFlashGetStatusCmd() : EfcCmd(EFC_CAT_FLASH, EFC_CMD_FLASH_GET_STATUS) , m_ready ( false ) { } bool EfcFlashGetStatusCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS; result &= EfcCmd::serialize ( se ); return result; } bool EfcFlashGetStatusCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); m_ready = !(m_header.retval == eERV_FlashBusy); return result; } void EfcFlashGetStatusCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Flash Get Status:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Ready? : %s\n", (m_ready?"Yes":"No")); } // ------------------ EfcFlashGetSessionBaseCmd::EfcFlashGetSessionBaseCmd() : EfcCmd(EFC_CAT_FLASH, EFC_CMD_FLASH_GET_SESSION_BASE) , m_address ( false ) { } bool EfcFlashGetSessionBaseCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS; result &= EfcCmd::serialize ( se ); return result; } bool EfcFlashGetSessionBaseCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); EFC_DESERIALIZE_AND_SWAP(de, &m_address, result); return result; } void EfcFlashGetSessionBaseCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Flash Get Session Base:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Address : %u\n", m_address); } } // namespace FireWorks libffado-2.4.5/src/fireworks/efc/efc_cmds_flash.h0000644000175000001440000000736514206145246021327 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIREWORKS_EFC_CMD_FLASH_H #define FIREWORKS_EFC_CMD_FLASH_H #include "efc_cmd.h" // #define EFC_CMD_FLASH_ERASE 0 // #define EFC_CMD_FLASH_READ 1 // #define EFC_CMD_FLASH_WRITE 2 // #define EFC_CMD_FLASH_GET_STATUS 3 // #define EFC_CMD_FLASH_GET_SESSION_BASE 4 // #define EFC_CMD_FLASH_LOCK 5 #define EFC_FLASH_SIZE_BYTES 256 #define EFC_FLASH_SIZE_QUADS (EFC_FLASH_SIZE_BYTES / 4) namespace FireWorks { class EfcFlashEraseCmd : public EfcCmd { public: EfcFlashEraseCmd(); virtual ~EfcFlashEraseCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcFlashEraseCmd"; } virtual void showEfcCmd(); uint32_t m_address; }; class EfcFlashReadCmd : public EfcCmd { public: EfcFlashReadCmd(); virtual ~EfcFlashReadCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcFlashReadCmd"; } virtual void showEfcCmd(); uint32_t m_address; uint32_t m_nb_quadlets; uint32_t m_data[EFC_FLASH_SIZE_QUADS]; }; class EfcFlashWriteCmd : public EfcCmd { public: EfcFlashWriteCmd(); virtual ~EfcFlashWriteCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcFlashWriteCmd"; } virtual void showEfcCmd(); uint32_t m_address; uint32_t m_nb_quadlets; uint32_t m_data[EFC_FLASH_SIZE_QUADS]; }; class EfcFlashLockCmd : public EfcCmd { public: EfcFlashLockCmd(); virtual ~EfcFlashLockCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcFlashLockCmd"; } virtual void showEfcCmd(); bool m_lock; }; class EfcFlashGetStatusCmd : public EfcCmd { public: EfcFlashGetStatusCmd(); virtual ~EfcFlashGetStatusCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcFlashGetStatusCmd"; } virtual void showEfcCmd(); bool m_ready; }; class EfcFlashGetSessionBaseCmd : public EfcCmd { public: EfcFlashGetSessionBaseCmd(); virtual ~EfcFlashGetSessionBaseCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcFlashGetSessionBaseCmd"; } virtual void showEfcCmd(); uint32_t m_address; }; } // namespace FireWorks #endif // FIREWORKS_EFC_CMD_FLASH_H libffado-2.4.5/src/fireworks/efc/efc_cmds_hardware.cpp0000644000175000001440000002272414206145246022356 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "efc_cmds_hardware.h" #include "libutil/ByteSwap.h" #include using namespace std; namespace FireWorks { EfcHardwareInfoCmd::EfcHardwareInfoCmd() : EfcCmd(EFC_CAT_HARDWARE_INFO, EFC_CMD_HW_HWINFO_GET_CAPS) , m_nb_out_groups( 0 ) , m_nb_in_groups( 0 ) {} bool EfcHardwareInfoCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS; result &= EfcCmd::serialize ( se ); return result; } bool EfcHardwareInfoCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; uint32_t tmp; result &= EfcCmd::deserialize ( de ); // the serialization is different from the deserialization EFC_DESERIALIZE_AND_SWAP(de, &m_flags, result); EFC_DESERIALIZE_AND_SWAP(de, &tmp, result); m_guid=tmp; m_guid <<= 32; EFC_DESERIALIZE_AND_SWAP(de, &tmp, result); m_guid |= tmp; EFC_DESERIALIZE_AND_SWAP(de, &m_type, result); EFC_DESERIALIZE_AND_SWAP(de, &m_version, result); int i=0; byte_t *vname=(byte_t *)m_vendor_name; for (i=0; i= 1) { EFC_DESERIALIZE_AND_SWAP(de, &m_fpga_version, result); EFC_DESERIALIZE_AND_SWAP(de, &m_nb_1394_play_chan_2x, result); EFC_DESERIALIZE_AND_SWAP(de, &m_nb_1394_rec_chan_2x, result); EFC_DESERIALIZE_AND_SWAP(de, &m_nb_1394_play_chan_4x, result); EFC_DESERIALIZE_AND_SWAP(de, &m_nb_1394_rec_chan_4x, result); } return result; } void EfcHardwareInfoCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC HW CAPS info:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Flags : 0x%08X\n", m_flags); debugOutput(DEBUG_LEVEL_NORMAL, " GUID : %016" PRIX64 "\n", m_guid); debugOutput(DEBUG_LEVEL_NORMAL, " HwType : 0x%08X\n", m_type); debugOutput(DEBUG_LEVEL_NORMAL, " Version : %u\n", m_version); debugOutput(DEBUG_LEVEL_NORMAL, " Vendor : %s\n", m_vendor_name); debugOutput(DEBUG_LEVEL_NORMAL, " Model : %s\n", m_model_name); debugOutput(DEBUG_LEVEL_NORMAL, " Supported Clocks : 0x%08X\n", m_supported_clocks); debugOutput(DEBUG_LEVEL_NORMAL, " # 1394 Playback : %d\n", m_nb_1394_playback_channels); debugOutput(DEBUG_LEVEL_NORMAL, " # 1394 Record : %d\n", m_nb_1394_record_channels); debugOutput(DEBUG_LEVEL_NORMAL, " # Physical out : %d\n", m_nb_phys_audio_out); debugOutput(DEBUG_LEVEL_NORMAL, " # Physical in : %d\n", m_nb_phys_audio_in); unsigned int i=0; debugOutput(DEBUG_LEVEL_NORMAL, " # Output Groups : %d\n", m_nb_out_groups); for (i=0;i= 1) { debugOutput(DEBUG_LEVEL_NORMAL, " FPGA version : 0x%08X\n", m_fpga_version); debugOutput(DEBUG_LEVEL_NORMAL, " # 1394 Playback (x2) : %d\n", m_nb_1394_play_chan_2x); debugOutput(DEBUG_LEVEL_NORMAL, " # 1394 Record (x2) : %d\n", m_nb_1394_rec_chan_2x); debugOutput(DEBUG_LEVEL_NORMAL, " # 1394 Playback (x4) : %d\n", m_nb_1394_play_chan_4x); debugOutput(DEBUG_LEVEL_NORMAL, " # 1394 Record (x4) : %d\n", m_nb_1394_rec_chan_4x); } } // --- polled info command EfcPolledValuesCmd::EfcPolledValuesCmd() : EfcCmd(EFC_CAT_HARDWARE_INFO, EFC_CMD_HW_GET_POLLED) , m_nb_output_meters ( 0 ) , m_nb_input_meters ( 0 ) {} bool EfcPolledValuesCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS; result &= EfcCmd::serialize ( se ); return result; } bool EfcPolledValuesCmd::deserialize( Util::Cmd::IISDeserialize& de ) { int i, nb_meters; bool result; if (!EfcCmd::deserialize(de)) return false; // the serialization is different from the deserialization result = true; EFC_DESERIALIZE_AND_SWAP(de, &m_status, result); EFC_DESERIALIZE_AND_SWAP(de, &m_detect_spdif, result); EFC_DESERIALIZE_AND_SWAP(de, &m_detect_adat, result); EFC_DESERIALIZE_AND_SWAP(de, &m_reserved3, result); EFC_DESERIALIZE_AND_SWAP(de, &m_reserved4, result); EFC_DESERIALIZE_AND_SWAP(de, &m_nb_output_meters, result); EFC_DESERIALIZE_AND_SWAP(de, &m_nb_input_meters, result); EFC_DESERIALIZE_AND_SWAP(de, &m_reserved5, result); EFC_DESERIALIZE_AND_SWAP(de, &m_reserved6, result); if (!result) return result; /* * NOTE: * Firmware version 5.0 or later for AudioFire12 returns invalid values to * contents of response against this command. Currently apply a workaround * to compensate the value because this value is not re-used again. */ nb_meters = m_nb_output_meters + m_nb_input_meters; if (nb_meters > POLLED_MAX_NB_METERS) { m_nb_output_meters = 0; m_nb_input_meters = 0; nb_meters = 0; } for (i = 0; i < nb_meters; i++) EFC_DESERIALIZE_AND_SWAP(de, (uint32_t *)&m_meters[i], result); return result; } void EfcPolledValuesCmd::showEfcCmd() { unsigned int i; EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC POLLED info:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Status : 0x%08X\n", m_status); debugOutput(DEBUG_LEVEL_NORMAL, " Detect SPDIF : 0x%08X\n", m_detect_spdif); debugOutput(DEBUG_LEVEL_NORMAL, " Detect ADAT : 0x%08X\n", m_detect_adat); /* prevent buffer over run */ if (m_nb_output_meters + m_nb_input_meters > POLLED_MAX_NB_METERS) return; if (m_nb_output_meters > 0) { debugOutput(DEBUG_LEVEL_NORMAL, " # Output Meters : %d\n", m_nb_output_meters); for (i = 0; i < m_nb_output_meters; i++) debugOutput(DEBUG_LEVEL_NORMAL, " Meter %d: %d\n", i, m_meters[i]); } if (m_nb_input_meters > 0) { debugOutput(DEBUG_LEVEL_NORMAL, " # Input Meters : %d\n", m_nb_input_meters); for (; i < m_nb_output_meters + m_nb_input_meters; i++) debugOutput(DEBUG_LEVEL_NORMAL, " Meter %d: %d\n", i, m_meters[i]); } } } // namespace FireWorks libffado-2.4.5/src/fireworks/efc/efc_cmds_hardware.h0000644000175000001440000001110714206145246022014 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIREWORKS_EFC_CMDS_HARDWARE_H #define FIREWORKS_EFC_CMDS_HARDWARE_H #include "efc_cmd.h" namespace FireWorks { #define HWINFO_NAME_SIZE_BYTES 32 #define HWINFO_MAX_CAPS_GROUPS 8 #define POLLED_MAX_NB_METERS 100 class EfcHardwareInfoCmd : public EfcCmd { typedef struct { uint8_t type; uint8_t count; } caps_phys_group; public: EfcHardwareInfoCmd(); virtual ~EfcHardwareInfoCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcHardwareInfoCmd"; } virtual void showEfcCmd(); bool hasPlaybackRouting() const {return EFC_CMD_HW_CHECK_FLAG(m_flags, EFC_CMD_HW_HAS_PLAYBACK_ROUTING);}; bool hasSoftwarePhantom() const {return EFC_CMD_HW_CHECK_FLAG(m_flags, EFC_CMD_HW_HAS_PHANTOM);}; bool hasDSP() const {return EFC_CMD_HW_CHECK_FLAG(m_flags, EFC_CMD_HW_HAS_DSP);}; bool hasFPGA() const {return EFC_CMD_HW_CHECK_FLAG(m_flags, EFC_CMD_HW_HAS_FPGA);}; bool hasOpticalInterface() const {return EFC_CMD_HW_CHECK_FLAG(m_flags, EFC_CMD_HW_OPTICAL_INTERFACE_SUPPORTED);}; bool hasSpdifAESEBUXLR() const {return EFC_CMD_HW_CHECK_FLAG(m_flags, EFC_CMD_HW_SPDIF_AESEBUXLR_SUPPORTED);}; bool hasMirroring() const {return EFC_CMD_HW_CHECK_FLAG(m_flags, EFC_CMD_HW_MIRRORING_SUPPORTED);}; bool hasDynAddr() const {return EFC_CMD_HW_CHECK_FLAG(m_flags, EFC_CMD_HW_DYNADDR_SUPPORTED);}; uint32_t m_flags; uint64_t m_guid; uint32_t m_type; uint32_t m_version; char m_vendor_name[ HWINFO_NAME_SIZE_BYTES ]; char m_model_name[ HWINFO_NAME_SIZE_BYTES ]; uint32_t m_supported_clocks; uint32_t m_nb_1394_playback_channels; uint32_t m_nb_1394_record_channels; uint32_t m_nb_phys_audio_out; uint32_t m_nb_phys_audio_in; uint32_t m_nb_out_groups; caps_phys_group out_groups[ HWINFO_MAX_CAPS_GROUPS ]; uint32_t m_nb_in_groups; caps_phys_group in_groups[ HWINFO_MAX_CAPS_GROUPS ]; uint32_t m_nb_midi_out; uint32_t m_nb_midi_in; uint32_t m_max_sample_rate; uint32_t m_min_sample_rate; uint32_t m_dsp_version; uint32_t m_arm_version; uint32_t num_mix_play_chan; uint32_t num_mix_rec_chan; // Only present when efc_version == 1 (?or >= 1?) uint32_t m_fpga_version; // version # for FPGA uint32_t m_nb_1394_play_chan_2x; uint32_t m_nb_1394_rec_chan_2x; uint32_t m_nb_1394_play_chan_4x; uint32_t m_nb_1394_rec_chan_4x; uint32_t m_reserved[16]; }; class EfcPolledValuesCmd : public EfcCmd { public: EfcPolledValuesCmd(); virtual ~EfcPolledValuesCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcPolledValuesCmd"; } virtual void showEfcCmd(); uint32_t m_status; uint32_t m_detect_spdif; uint32_t m_detect_adat; uint32_t m_reserved3; uint32_t m_reserved4; uint32_t m_nb_output_meters; uint32_t m_nb_input_meters; uint32_t m_reserved5; uint32_t m_reserved6; int32_t m_meters[POLLED_MAX_NB_METERS]; }; } // namespace FireWorks #endif // FIREWORKS_EFC_CMDS_HARDWARE_H libffado-2.4.5/src/fireworks/efc/efc_cmds_hardware_ctrl.cpp0000644000175000001440000001543714206145246023405 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "efc_cmd.h" #include "efc_cmds_hardware_ctrl.h" #include "libutil/ByteSwap.h" #include #include using namespace std; namespace FireWorks { EfcGetClockCmd::EfcGetClockCmd() : EfcCmd(EFC_CAT_HARDWARE_CONTROL, EFC_CMD_HWCTRL_GET_CLOCK) , m_clock ( EFC_CMD_HW_CLOCK_UNSPECIFIED ) , m_samplerate ( EFC_CMD_HW_CLOCK_UNSPECIFIED ) , m_index ( 0 ) { } bool EfcGetClockCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS; result &= EfcCmd::serialize ( se ); return result; } bool EfcGetClockCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); EFC_DESERIALIZE_AND_SWAP(de, &m_clock, result); EFC_DESERIALIZE_AND_SWAP(de, &m_samplerate, result); EFC_DESERIALIZE_AND_SWAP(de, &m_index, result); return result; } void EfcGetClockCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Get Clock:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Clock : %u\n", m_clock); debugOutput(DEBUG_LEVEL_NORMAL, " Samplerate : %u\n", m_samplerate); debugOutput(DEBUG_LEVEL_NORMAL, " Index : %u\n", m_index); } // ---- EfcSetClockCmd::EfcSetClockCmd() : EfcCmd(EFC_CAT_HARDWARE_CONTROL, EFC_CMD_HWCTRL_SET_CLOCK) , m_clock ( EFC_CMD_HW_CLOCK_UNSPECIFIED ) , m_samplerate ( EFC_CMD_HW_CLOCK_UNSPECIFIED ) , m_index ( 0 ) { } bool EfcSetClockCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS+3; result &= EfcCmd::serialize ( se ); result &= se.write(CondSwapToBus32(m_clock), "Clock" ); result &= se.write(CondSwapToBus32(m_samplerate), "Samplerate" ); result &= se.write(CondSwapToBus32(m_index), "Index" ); return result; } bool EfcSetClockCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); EFC_DESERIALIZE_AND_SWAP(de, &m_clock, result); EFC_DESERIALIZE_AND_SWAP(de, &m_samplerate, result); EFC_DESERIALIZE_AND_SWAP(de, &m_index, result); /* * With firmware version 5.8, after setting, approximately 100ms needs to * get it effective. If getting command is executed during this period, * previous value is returned. This is a simple hack for this issue. */ usleep(150000); return result; } void EfcSetClockCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Set Clock:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Clock : %u\n", m_clock); debugOutput(DEBUG_LEVEL_NORMAL, " Samplerate : %u\n", m_samplerate); debugOutput(DEBUG_LEVEL_NORMAL, " Index : %u\n", m_index); } // ---- EfcPhyReconnectCmd::EfcPhyReconnectCmd() : EfcCmd(EFC_CAT_HARDWARE_CONTROL, EFC_CMD_HWCTRL_RECONNECT_PHY) { } bool EfcPhyReconnectCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS; result &= EfcCmd::serialize ( se ); return result; } bool EfcPhyReconnectCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); return result; } void EfcPhyReconnectCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Phy Reconnect\n"); } // -- get flags EfcGetFlagsCmd::EfcGetFlagsCmd() : EfcCmd(EFC_CAT_HARDWARE_CONTROL, EFC_CMD_HWCTRL_GET_FLAGS) , m_flags ( 0 ) { } bool EfcGetFlagsCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS; result &= EfcCmd::serialize ( se ); return result; } bool EfcGetFlagsCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); EFC_DESERIALIZE_AND_SWAP(de, &m_flags, result); return result; } void EfcGetFlagsCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Get Flags:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Flags : %08X\n", m_flags); } // ---- EfcChangeFlagsCmd::EfcChangeFlagsCmd() : EfcCmd(EFC_CAT_HARDWARE_CONTROL, EFC_CMD_HWCTRL_CHANGE_FLAGS) , m_setmask ( 0 ) , m_clearmask ( 0 ) { } bool EfcChangeFlagsCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS+2; result &= EfcCmd::serialize ( se ); result &= se.write(CondSwapToBus32(m_setmask), "SetMask" ); result &= se.write(CondSwapToBus32(m_clearmask), "ClearMask" ); return result; } bool EfcChangeFlagsCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); return result; } void EfcChangeFlagsCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Change flags:\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Set mask : %08X\n", m_setmask); debugOutput(DEBUG_LEVEL_NORMAL, " Clear mask : %08X\n", m_clearmask); } // ---- EfcIdentifyCmd::EfcIdentifyCmd() : EfcCmd(EFC_CAT_HARDWARE_CONTROL, EFC_CMD_HWCTRL_IDENTIFY) { } bool EfcIdentifyCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS; result &= EfcCmd::serialize ( se ); return result; } bool EfcIdentifyCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); return result; } void EfcIdentifyCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC Identify\n"); } } // namespace FireWorks libffado-2.4.5/src/fireworks/efc/efc_cmds_hardware_ctrl.h0000644000175000001440000000663214206145246023047 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIREWORKS_EFC_CMD_HARDWARE_CTRL_H #define FIREWORKS_EFC_CMD_HARDWARE_CTRL_H #include "efc_cmd.h" namespace FireWorks { #define FIREWORKS_EFC_FLAG_MIXER_ENABLED 1 #define FIREWORKS_EFC_FLAG_SPDIF_PRO 2 #define FIREWORKS_EFC_FLAG_SPDIF_RAW 4 class EfcGetClockCmd : public EfcCmd { public: EfcGetClockCmd(); virtual ~EfcGetClockCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcGetClockCmd"; } virtual void showEfcCmd(); uint32_t m_clock; uint32_t m_samplerate; uint32_t m_index; }; class EfcSetClockCmd : public EfcCmd { public: EfcSetClockCmd(); virtual ~EfcSetClockCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcSetClockCmd"; } virtual void showEfcCmd(); uint32_t m_clock; uint32_t m_samplerate; uint32_t m_index; }; class EfcPhyReconnectCmd : public EfcCmd { public: EfcPhyReconnectCmd(); virtual ~EfcPhyReconnectCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcPhyReconnectCmd"; } virtual void showEfcCmd(); }; class EfcGetFlagsCmd : public EfcCmd { public: EfcGetFlagsCmd(); virtual ~EfcGetFlagsCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcGetFlagsCmd"; } virtual void showEfcCmd(); uint32_t m_flags; }; class EfcChangeFlagsCmd : public EfcCmd { public: EfcChangeFlagsCmd(); virtual ~EfcChangeFlagsCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcChangeFlagsCmd"; } virtual void showEfcCmd(); uint32_t m_setmask; uint32_t m_clearmask; }; class EfcIdentifyCmd : public EfcCmd { public: EfcIdentifyCmd(); virtual ~EfcIdentifyCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "EfcIdentifyCmd"; } virtual void showEfcCmd(); }; } // namespace FireWorks #endif // FIREWORKS_EFC_CMD_HARDWARE_CTRL_H libffado-2.4.5/src/fireworks/efc/efc_cmds_ioconfig.cpp0000644000175000001440000002127314206145246022354 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "efc_cmd.h" #include "efc_cmds_ioconfig.h" #include "libutil/ByteSwap.h" #include #include using namespace std; namespace FireWorks { EfcGenericIOConfigCmd::EfcGenericIOConfigCmd(enum eIOConfigRegister r) : EfcCmd() , m_value ( 0 ) , m_reg ( r ) { m_category_id = EFC_CAT_IO_CONFIG; m_type=eCT_Get; setRegister(r); } bool EfcGenericIOConfigCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; if (m_type == eCT_Get) { // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS; result &= EfcCmd::serialize ( se ); } else { // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS+1; result &= EfcCmd::serialize ( se ); result &= se.write(CondSwapToBus32(m_value), "Value" ); } return result; } bool EfcGenericIOConfigCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); if (m_type == eCT_Get) { EFC_DESERIALIZE_AND_SWAP(de, &m_value, result); } return result; } bool EfcGenericIOConfigCmd::setType( enum eCmdType type ) { m_type=type; if (m_type == eCT_Get) { switch (m_reg) { case eCR_Mirror: m_command_id=EFC_CMD_IO_CONFIG_GET_MIRROR; break; case eCR_DigitalInterface: m_command_id=EFC_CMD_IO_CONFIG_GET_DIGITAL_MODE; break; case eCR_Phantom: m_command_id=EFC_CMD_IO_CONFIG_GET_PHANTOM; break; default: debugError("Invalid IOConfig get command: %d\n", m_reg); return false; } } else { switch (m_reg) { case eCR_Mirror: m_command_id=EFC_CMD_IO_CONFIG_SET_MIRROR; break; case eCR_DigitalInterface: m_command_id=EFC_CMD_IO_CONFIG_SET_DIGITAL_MODE; break; case eCR_Phantom: m_command_id=EFC_CMD_IO_CONFIG_SET_PHANTOM; break; default: debugError("Invalid IOConfig set command: %d\n", m_reg); return false; } } return true; } bool EfcGenericIOConfigCmd::setRegister( enum eIOConfigRegister r ) { m_reg=r; if (m_type == eCT_Get) { switch (m_reg) { case eCR_Mirror: m_command_id=EFC_CMD_IO_CONFIG_GET_MIRROR; break; case eCR_DigitalInterface: m_command_id=EFC_CMD_IO_CONFIG_GET_DIGITAL_MODE; break; case eCR_Phantom: m_command_id=EFC_CMD_IO_CONFIG_GET_PHANTOM; break; default: debugError("Invalid IOConfig get command: %d\n", m_reg); return false; } } else { switch (m_reg) { case eCR_Mirror: m_command_id=EFC_CMD_IO_CONFIG_SET_MIRROR; break; case eCR_DigitalInterface: m_command_id=EFC_CMD_IO_CONFIG_SET_DIGITAL_MODE; break; case eCR_Phantom: m_command_id=EFC_CMD_IO_CONFIG_SET_PHANTOM; break; default: debugError("Invalid IOConfig set command: %d\n", m_reg); return false; } } return true; } void EfcGenericIOConfigCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC IOCONFIG %s %s:\n", (m_type==eCT_Get?"GET":"SET"), eIOConfigRegisterToString(m_reg)); debugOutput(DEBUG_LEVEL_NORMAL, " Value : %u\n", m_value); } // --- The specific commands EfcIsocMapIOConfigCmd::EfcIsocMapIOConfigCmd(void) : EfcCmd() , m_samplerate (0) , m_flags (0) , m_num_playmap_entries (0) , m_num_phys_out (0) , m_num_recmap_entries (0) , m_num_phys_in (0) { m_category_id = EFC_CAT_IO_CONFIG; m_reg = eCR_IsocMap; m_type = eCT_Get; memset(m_playmap, 0, sizeof(m_playmap)); memset(m_recmap, 0, sizeof(m_recmap)); } bool EfcIsocMapIOConfigCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; unsigned int i; assert((m_num_playmap_entries <= EFC_MAX_ISOC_MAP_ENTRIES) || (m_num_recmap_entries <= EFC_MAX_ISOC_MAP_ENTRIES)); if (m_type == eCT_Get) { // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS; result &= EfcCmd::serialize ( se ); } else { // the length should be specified before // the header is serialized m_length = EFC_HEADER_LENGTH_QUADLETS + sizeof(IsoChannelMap); result &= EfcCmd::serialize ( se ); result &= se.write(CondSwapToBus32(m_samplerate), "Samplerate" ); result &= se.write(CondSwapToBus32(m_flags), "Flags" ); result &= se.write(CondSwapToBus32(m_num_playmap_entries), "Num. of Entries for Play Map" ); result &= se.write(CondSwapToBus32(m_num_phys_out), "Num. of Phys. Out" ); for (i = 0; i < EFC_MAX_ISOC_MAP_ENTRIES; i++) result &= se.write(CondSwapToBus32(m_playmap[i]), "Play Map Entry" ); result &= se.write(CondSwapToBus32(m_num_recmap_entries), "Num. of Entries for Rec Map" ); result &= se.write(CondSwapToBus32(m_num_phys_in), "Num. of Phys. In" ); for (i = 0; i < EFC_MAX_ISOC_MAP_ENTRIES; i++) result &= se.write(CondSwapToBus32(m_recmap[i]), "Rec Map Entry" ); } return result; } bool EfcIsocMapIOConfigCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; unsigned int i; result &= EfcCmd::deserialize ( de ); if (m_type == eCT_Get) { EFC_DESERIALIZE_AND_SWAP(de, &m_samplerate, result); EFC_DESERIALIZE_AND_SWAP(de, &m_flags, result); EFC_DESERIALIZE_AND_SWAP(de, &m_num_playmap_entries, result); EFC_DESERIALIZE_AND_SWAP(de, &m_num_phys_out, result); for (i = 0; i < EFC_MAX_ISOC_MAP_ENTRIES; i++) EFC_DESERIALIZE_AND_SWAP(de, &m_playmap[i], result); EFC_DESERIALIZE_AND_SWAP(de, &m_num_recmap_entries, result); EFC_DESERIALIZE_AND_SWAP(de, &m_num_phys_in, result); for (i = 0; i < EFC_MAX_ISOC_MAP_ENTRIES; i++) EFC_DESERIALIZE_AND_SWAP(de, &m_recmap[i], result); } return result; } bool EfcIsocMapIOConfigCmd::setType( enum eCmdType type ) { m_type=type; if (m_type == eCT_Get) m_command_id = EFC_CMD_IO_CONFIG_GET_ISOC_MAP; else m_command_id = EFC_CMD_IO_CONFIG_SET_ISOC_MAP; return true; } void EfcIsocMapIOConfigCmd::showEfcCmd() { unsigned int i; EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC IOCONFIG %s %s:\n", (m_type==eCT_Get?"GET":"SET"), eIOConfigRegisterToString(m_reg)); debugOutput(DEBUG_LEVEL_NORMAL, " Samplerate : %u\n", m_samplerate); debugOutput(DEBUG_LEVEL_NORMAL, " Flags : %u\n", m_flags); debugOutput(DEBUG_LEVEL_NORMAL, " Playback:"); debugOutput(DEBUG_LEVEL_NORMAL, " Num. of Entries : %u\n", m_num_playmap_entries); debugOutput(DEBUG_LEVEL_NORMAL, " Num. of Phys. Out: %u\n", m_num_phys_out); for (i = 0; i < EFC_MAX_ISOC_MAP_ENTRIES; i++) debugOutput(DEBUG_LEVEL_NORMAL, " Entriy %02d : %u\n", i, m_playmap[i]); debugOutput(DEBUG_LEVEL_NORMAL, " Record:"); debugOutput(DEBUG_LEVEL_NORMAL, " Num. of Entries : %u\n", m_num_recmap_entries); debugOutput(DEBUG_LEVEL_NORMAL, " Num. of Phys. In : %u\n", m_num_phys_in); for (i = 0; i < EFC_MAX_ISOC_MAP_ENTRIES; i++) debugOutput(DEBUG_LEVEL_NORMAL, " Entriy %02d : %u\n", i, m_recmap[i]); } } // namespace FireWorks libffado-2.4.5/src/fireworks/efc/efc_cmds_ioconfig.h0000644000175000001440000000561414206145246022022 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIREWORKS_EFC_CMD_IOCONFIG_H #define FIREWORKS_EFC_CMD_IOCONFIG_H #include "efc_cmd.h" namespace FireWorks { #define EFC_MAX_ISOC_MAP_ENTRIES 32 typedef struct tag_efc_isoc_map { uint32_t samplerate; uint32_t flags; uint32_t num_playmap_entries; uint32_t num_phys_out; uint32_t playmap[ EFC_MAX_ISOC_MAP_ENTRIES ]; uint32_t num_recmap_entries; uint32_t num_phys_in; uint32_t recmap[ EFC_MAX_ISOC_MAP_ENTRIES ]; } IsoChannelMap; class EfcGenericIOConfigCmd : public EfcCmd { public: EfcGenericIOConfigCmd(enum eIOConfigRegister r); virtual ~EfcGenericIOConfigCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual void showEfcCmd(); bool setType( enum eCmdType type ); enum eCmdType getType() {return m_type;}; bool setRegister( enum eIOConfigRegister r ); enum eIOConfigRegister getRegister() {return m_reg;}; virtual const char* getCmdName() const { return "EfcGenericIOConfigCmd"; }; uint32_t m_value; private: enum eCmdType m_type; enum eIOConfigRegister m_reg; }; class EfcIsocMapIOConfigCmd : public EfcCmd { public: EfcIsocMapIOConfigCmd(); virtual ~EfcIsocMapIOConfigCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual void showEfcCmd(); bool setType( enum eCmdType type ); enum eCmdType getType() {return m_type;}; virtual const char* getCmdName() const { return "EfcIsocMapIOConfigCmd"; }; uint32_t m_samplerate; uint32_t m_flags; uint32_t m_num_playmap_entries; uint32_t m_num_phys_out; uint32_t m_playmap[ EFC_MAX_ISOC_MAP_ENTRIES ]; uint32_t m_num_recmap_entries; uint32_t m_num_phys_in; uint32_t m_recmap[ EFC_MAX_ISOC_MAP_ENTRIES ]; private: enum eCmdType m_type; enum eIOConfigRegister m_reg; }; } // namespace FireWorks #endif // FIREWORKS_EFC_CMD_IOCONFIG_H libffado-2.4.5/src/fireworks/efc/efc_cmds_mixer.cpp0000644000175000001440000001570514206145246021706 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "efc_cmd.h" #include "efc_cmds_mixer.h" #include "libutil/ByteSwap.h" #include using namespace std; namespace FireWorks { EfcGenericMixerCmd::EfcGenericMixerCmd(enum eMixerTarget target, enum eMixerCommand command) : EfcCmd() , m_channel ( -1 ) , m_value ( 0 ) { m_type=eCT_Get; m_target=target; m_command=command; setTarget(target); setCommand(command); setType(eCT_Get); } EfcGenericMixerCmd::EfcGenericMixerCmd(enum eMixerTarget target, enum eMixerCommand command, int channel) : EfcCmd() , m_channel ( channel ) , m_value ( 0 ) { m_type=eCT_Get; m_target=target; m_command=command; setTarget(target); setCommand(command); setType(eCT_Get); } bool EfcGenericMixerCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; if (m_type == eCT_Get) { // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS+1; result &= EfcCmd::serialize ( se ); result &= se.write(CondSwapToBus32(m_channel), "Channel" ); } else { // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS+2; result &= EfcCmd::serialize ( se ); result &= se.write(CondSwapToBus32(m_channel), "Channel" ); result &= se.write(CondSwapToBus32(m_value), "Value" ); } if(!result) { debugWarning("Serialization failed\n"); } return result; } bool EfcGenericMixerCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); if (m_type == eCT_Get) { EFC_DESERIALIZE_AND_SWAP(de, (quadlet_t *)&m_channel, result); EFC_DESERIALIZE_AND_SWAP(de, &m_value, result); } if(!result) { debugWarning("Deserialization failed\n"); } return result; } bool EfcGenericMixerCmd::setType( enum eCmdType type ) { m_type=type; if (type == eCT_Get) { switch (m_command) { case eMC_Gain: m_command_id=EFC_CMD_MIXER_GET_GAIN; break; case eMC_Solo: m_command_id=EFC_CMD_MIXER_GET_SOLO; break; case eMC_Mute: m_command_id=EFC_CMD_MIXER_GET_MUTE; break; case eMC_Pan: m_command_id=EFC_CMD_MIXER_GET_PAN; break; case eMC_Nominal: m_command_id=EFC_CMD_MIXER_GET_NOMINAL; break; default: debugError("Invalid mixer get command: %d\n", m_command); return false; } } else { switch (m_command) { case eMC_Gain: m_command_id=EFC_CMD_MIXER_SET_GAIN; break; case eMC_Solo: m_command_id=EFC_CMD_MIXER_SET_SOLO; break; case eMC_Mute: m_command_id=EFC_CMD_MIXER_SET_MUTE; break; case eMC_Pan: m_command_id=EFC_CMD_MIXER_SET_PAN; break; case eMC_Nominal: m_command_id=EFC_CMD_MIXER_SET_NOMINAL; break; default: debugError("Invalid mixer set command: %d\n", m_command); return false; } } return true; } bool EfcGenericMixerCmd::setTarget( enum eMixerTarget target ) { m_target=target; switch (target) { case eMT_PhysicalOutputMix: m_category_id=EFC_CAT_PHYSICAL_OUTPUT_MIX; break; case eMT_PhysicalInputMix: m_category_id=EFC_CAT_PHYSICAL_INPUT_MIX; break; case eMT_PlaybackMix: m_category_id=EFC_CAT_PLAYBACK_MIX; break; case eMT_RecordMix: m_category_id=EFC_CAT_RECORD_MIX; break; default: debugError("Invalid mixer target: %d\n", target); return false; } return true; } bool EfcGenericMixerCmd::setCommand( enum eMixerCommand command ) { m_command=command; if (m_type == eCT_Get) { switch (command) { case eMC_Gain: m_command_id=EFC_CMD_MIXER_GET_GAIN; break; case eMC_Solo: m_command_id=EFC_CMD_MIXER_GET_SOLO; break; case eMC_Mute: m_command_id=EFC_CMD_MIXER_GET_MUTE; break; case eMC_Pan: m_command_id=EFC_CMD_MIXER_GET_PAN; break; case eMC_Nominal: m_command_id=EFC_CMD_MIXER_GET_NOMINAL; break; default: debugError("Invalid mixer get command: %d\n", command); return false; } } else { switch (command) { case eMC_Gain: m_command_id=EFC_CMD_MIXER_SET_GAIN; break; case eMC_Solo: m_command_id=EFC_CMD_MIXER_SET_SOLO; break; case eMC_Mute: m_command_id=EFC_CMD_MIXER_SET_MUTE; break; case eMC_Pan: m_command_id=EFC_CMD_MIXER_SET_PAN; break; case eMC_Nominal: m_command_id=EFC_CMD_MIXER_SET_NOMINAL; break; default: debugError("Invalid mixer set command: %d\n", command); return false; } } return true; } void EfcGenericMixerCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC %s %s %s:\n", (m_type==eCT_Get?"GET":"SET"), eMixerTargetToString(m_target), eMixerCommandToString(m_command)); debugOutput(DEBUG_LEVEL_NORMAL, " Channel : %d\n", m_channel); debugOutput(DEBUG_LEVEL_NORMAL, " Value : %u\n", m_value); } // --- The specific commands } // namespace FireWorks libffado-2.4.5/src/fireworks/efc/efc_cmds_mixer.h0000644000175000001440000000363314206145246021350 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIREWORKS_EFC_CMD_MIXER_H #define FIREWORKS_EFC_CMD_MIXER_H #include "efc_cmd.h" namespace FireWorks { class EfcGenericMixerCmd : public EfcCmd { public: EfcGenericMixerCmd(enum eMixerTarget, enum eMixerCommand); EfcGenericMixerCmd(enum eMixerTarget, enum eMixerCommand, int channel); virtual ~EfcGenericMixerCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual void showEfcCmd(); bool setType( enum eCmdType type ); enum eCmdType getType() {return m_type;}; bool setTarget( enum eMixerTarget target ); enum eMixerTarget getTarget() {return m_target;}; bool setCommand( enum eMixerCommand cmd ); enum eMixerCommand getCommand() {return m_command;}; virtual const char* getCmdName() const { return "EfcGenericMixerCmd"; } int32_t m_channel; uint32_t m_value; private: enum eCmdType m_type; enum eMixerTarget m_target; enum eMixerCommand m_command; }; } // namespace FireWorks #endif // FIREWORKS_EFC_CMD_MIXER_H libffado-2.4.5/src/fireworks/efc/efc_cmds_monitor.cpp0000644000175000001440000001107714206145246022247 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "efc_cmd.h" #include "efc_cmds_monitor.h" #include "libutil/ByteSwap.h" #include using namespace std; namespace FireWorks { const char *eMonitorCommandToString(const enum eMonitorCommand command) { switch (command) { case eMoC_Gain: return "eMC_Gain"; case eMoC_Solo: return "eMoC_Solo"; case eMoC_Mute: return "eMoC_Mute"; case eMoC_Pan: return "eMoC_Pan"; default: return "invalid"; } } EfcGenericMonitorCmd::EfcGenericMonitorCmd(enum eCmdType type, enum eMonitorCommand command) : EfcCmd() , m_input ( -1 ) , m_output ( -1 ) , m_value ( 0 ) , m_type ( type ) , m_command ( command ) { m_category_id=EFC_CAT_MONITOR_MIX; if (type == eCT_Get) { switch (command) { case eMoC_Gain: m_command_id=EFC_CMD_MIXER_GET_GAIN; break; case eMoC_Solo: m_command_id=EFC_CMD_MIXER_GET_SOLO; break; case eMoC_Mute: m_command_id=EFC_CMD_MIXER_GET_MUTE; break; case eMoC_Pan: m_command_id=EFC_CMD_MIXER_GET_PAN; break; default: debugError("Invalid mixer get command: %d\n", command); } } else { switch (command) { case eMoC_Gain: m_command_id=EFC_CMD_MIXER_SET_GAIN; break; case eMoC_Solo: m_command_id=EFC_CMD_MIXER_SET_SOLO; break; case eMoC_Mute: m_command_id=EFC_CMD_MIXER_SET_MUTE; break; case eMoC_Pan: m_command_id=EFC_CMD_MIXER_SET_PAN; break; default: debugError("Invalid mixer set command: %d\n", command); } } } bool EfcGenericMonitorCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; if (m_type == eCT_Get) { // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS+2; result &= EfcCmd::serialize ( se ); result &= se.write(CondSwapToBus32(m_input), "Input" ); result &= se.write(CondSwapToBus32(m_output), "Output" ); } else { // the length should be specified before // the header is serialized m_length=EFC_HEADER_LENGTH_QUADLETS+3; result &= EfcCmd::serialize ( se ); result &= se.write(CondSwapToBus32(m_input), "Input" ); result &= se.write(CondSwapToBus32(m_output), "Output" ); result &= se.write(CondSwapToBus32(m_value), "Value" ); } return result; } bool EfcGenericMonitorCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= EfcCmd::deserialize ( de ); if (m_type == eCT_Get) { EFC_DESERIALIZE_AND_SWAP(de, (quadlet_t *)&m_input, result); EFC_DESERIALIZE_AND_SWAP(de, (quadlet_t *)&m_output, result); EFC_DESERIALIZE_AND_SWAP(de, &m_value, result); } if(!result) { debugWarning("Deserialization failed\n"); } return result; } void EfcGenericMonitorCmd::showEfcCmd() { EfcCmd::showEfcCmd(); debugOutput(DEBUG_LEVEL_NORMAL, "EFC %s MONITOR %s:\n", (m_type==eCT_Get?"GET":"SET"), eMonitorCommandToString(m_command)); debugOutput(DEBUG_LEVEL_NORMAL, " Input : %d\n", m_input); debugOutput(DEBUG_LEVEL_NORMAL, " Output : %d\n", m_output); debugOutput(DEBUG_LEVEL_NORMAL, " Value : %u\n", m_value); } // --- The specific commands } // namespace FireWorks libffado-2.4.5/src/fireworks/efc/efc_cmds_monitor.h0000644000175000001440000000737714206145246021724 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIREWORKS_EFC_CMD_MONITOR_H #define FIREWORKS_EFC_CMD_MONITOR_H #include "efc_cmd.h" namespace FireWorks { enum eMonitorCommand { eMoC_Gain, eMoC_Solo, eMoC_Mute, eMoC_Pan, }; class EfcGenericMonitorCmd : public EfcCmd { public: enum eCmdType { eCT_Get, eCT_Set, }; public: EfcGenericMonitorCmd(enum eCmdType, enum eMonitorCommand); virtual ~EfcGenericMonitorCmd() {}; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual void showEfcCmd(); int32_t m_input; int32_t m_output; uint32_t m_value; private: enum eCmdType m_type; enum eMonitorCommand m_command; }; // --- Specific implementations class EfcGetMonitorGainCmd : public EfcGenericMonitorCmd { public: EfcGetMonitorGainCmd() : EfcGenericMonitorCmd(eCT_Get, eMoC_Gain) {}; virtual ~EfcGetMonitorGainCmd() {}; virtual const char* getCmdName() const { return "EfcGetMonitorGainCmd"; } }; class EfcSetMonitorGainCmd : public EfcGenericMonitorCmd { public: EfcSetMonitorGainCmd() : EfcGenericMonitorCmd(eCT_Set, eMoC_Gain) {}; virtual ~EfcSetMonitorGainCmd() {}; virtual const char* getCmdName() const { return "EfcSetMonitorGainCmd"; } }; class EfcGetMonitorSoloCmd : public EfcGenericMonitorCmd { public: EfcGetMonitorSoloCmd() : EfcGenericMonitorCmd(eCT_Get, eMoC_Solo) {}; virtual ~EfcGetMonitorSoloCmd() {}; virtual const char* getCmdName() const { return "EfcGetMonitorSoloCmd"; } }; class EfcSetMonitorSoloCmd : public EfcGenericMonitorCmd { public: EfcSetMonitorSoloCmd() : EfcGenericMonitorCmd(eCT_Set, eMoC_Solo) {}; virtual ~EfcSetMonitorSoloCmd() {}; virtual const char* getCmdName() const { return "EfcSetMonitorSoloCmd"; } }; class EfcGetMonitorMuteCmd : public EfcGenericMonitorCmd { public: EfcGetMonitorMuteCmd() : EfcGenericMonitorCmd(eCT_Get, eMoC_Mute) {}; virtual ~EfcGetMonitorMuteCmd() {}; virtual const char* getCmdName() const { return "EfcGetMonitorMuteCmd"; } }; class EfcSetMonitorMuteCmd : public EfcGenericMonitorCmd { public: EfcSetMonitorMuteCmd() : EfcGenericMonitorCmd(eCT_Set, eMoC_Mute) {}; virtual ~EfcSetMonitorMuteCmd() {}; virtual const char* getCmdName() const { return "EfcSetMonitorMuteCmd"; } }; class EfcGetMonitorPanCmd : public EfcGenericMonitorCmd { public: EfcGetMonitorPanCmd() : EfcGenericMonitorCmd(eCT_Get, eMoC_Pan) {}; virtual ~EfcGetMonitorPanCmd() {}; virtual const char* getCmdName() const { return "EfcGetMonitorPanCmd"; } }; class EfcSetMonitorPanCmd : public EfcGenericMonitorCmd { public: EfcSetMonitorPanCmd() : EfcGenericMonitorCmd(eCT_Set, eMoC_Pan) {}; virtual ~EfcSetMonitorPanCmd() {}; virtual const char* getCmdName() const { return "EfcSetMonitorPanCmd"; } }; } // namespace FireWorks #endif // FIREWORKS_EFC_CMD_MONITOR_H libffado-2.4.5/src/fireworks/fireworks_control.cpp0000644000175000001440000005414614206145246021757 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "fireworks_device.h" #include "fireworks_control.h" #include "efc/efc_avc_cmd.h" #include "efc/efc_cmd.h" #include "efc/efc_cmds_mixer.h" #include "efc/efc_cmds_monitor.h" #include "efc/efc_cmds_hardware_ctrl.h" #include #include using namespace std; // These classes provide support for the controls on the echo devices namespace FireWorks { MonitorControl::MonitorControl(FireWorks::Device& p, enum eMonitorControl c) : Control::MatrixMixer(&p, "MonitorControl") , m_control(c) , m_ParentDevice(p) { } MonitorControl::MonitorControl(FireWorks::Device& p, enum eMonitorControl c, std::string n) : Control::MatrixMixer(&p, n) , m_control(c) , m_ParentDevice(p) { } void MonitorControl::show() { debugOutput(DEBUG_LEVEL_NORMAL, "MonitorControl\n"); } std::string MonitorControl::getRowName( const int row ) { std::ostringstream rowname; rowname << "IN" << row; debugOutput(DEBUG_LEVEL_VERBOSE, "name for row %d is %s\n", row, rowname.str().c_str()); return rowname.str(); } std::string MonitorControl::getColName( const int col ) { std::ostringstream colname; colname << "OUT" << col; debugOutput(DEBUG_LEVEL_VERBOSE, "name for col %d is %s\n", col, colname.str().c_str()); return colname.str(); } int MonitorControl::canWrite( const int row, const int col ) { debugOutput(DEBUG_LEVEL_VERBOSE, "canWrite for row %d col %d is %d\n", row, col, true); return true; } double MonitorControl::setValue( const int row, const int col, const double val ) { double retval=0.0; bool did_command=false; if(row >= (int)m_ParentDevice.getHwInfo().m_nb_phys_audio_in) { debugError("specified row (%u) larger than number of rows (%d)\n", row, m_ParentDevice.getHwInfo().m_nb_phys_audio_in); return 0.0; } if(col >= (int)m_ParentDevice.getHwInfo().m_nb_phys_audio_out) { debugError("specified col (%u) larger than number of cols (%d)\n", col, m_ParentDevice.getHwInfo().m_nb_phys_audio_out); return 0.0; } // not a switch since we create variables if(m_control==eMC_Gain) { EfcSetMonitorGainCmd setCmd; setCmd.m_input = row; setCmd.m_output = col; setCmd.m_value = (uint32_t)val; if (!m_ParentDevice.doEfcOverAVC(setCmd)) { debugError("Cmd failed\n"); } // update the session block m_ParentDevice.m_session.h.monitorgains[row][col] = setCmd.m_value; retval = setCmd.m_value; did_command = true; } if(m_control==eMC_Pan) { EfcSetMonitorPanCmd setCmd; setCmd.m_input = row; setCmd.m_output = col; setCmd.m_value = (uint32_t)val; if (!m_ParentDevice.doEfcOverAVC(setCmd)) { debugError("Cmd failed\n"); } // update the session block m_ParentDevice.m_session.s.monitorpans[row][col] = setCmd.m_value; retval = setCmd.m_value; did_command = true; } if(m_control==eMC_Mute) { EfcSetMonitorMuteCmd setCmd; setCmd.m_input = row; setCmd.m_output = col; setCmd.m_value = (uint32_t)val; if (!m_ParentDevice.doEfcOverAVC(setCmd)) { debugError("Cmd failed\n"); } // update the session block if(setCmd.m_value) { m_ParentDevice.m_session.s.monitorflags[row][col] |= ECHO_SESSION_MUTE_BIT; } else { m_ParentDevice.m_session.s.monitorflags[row][col] &= ~ECHO_SESSION_MUTE_BIT; } retval = setCmd.m_value; did_command = true; } if(m_control==eMC_Solo) { EfcSetMonitorSoloCmd setCmd; setCmd.m_input = row; setCmd.m_output = col; setCmd.m_value = (uint32_t)val; if (!m_ParentDevice.doEfcOverAVC(setCmd)) { debugError("Cmd failed\n"); } // update the session block if(setCmd.m_value) { m_ParentDevice.m_session.s.monitorflags[row][col] |= ECHO_SESSION_SOLO_BIT; } else { m_ParentDevice.m_session.s.monitorflags[row][col] &= ~ECHO_SESSION_SOLO_BIT; } retval = setCmd.m_value; did_command = true; } debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for row %d col %d = %lf\n", row, col, retval); if (!did_command) { debugError("BUG: this should never happen due to enum\n"); } return retval; } double MonitorControl::getValue( const int row, const int col ) { double retval=0.0; bool did_command=false; if(row >= (int)m_ParentDevice.getHwInfo().m_nb_phys_audio_in) { debugError("specified row (%u) larger than number of rows (%d)\n", row, m_ParentDevice.getHwInfo().m_nb_phys_audio_in); return 0.0; } if(col >= (int)m_ParentDevice.getHwInfo().m_nb_phys_audio_out) { debugError("specified col (%u) larger than number of cols (%d)\n", col, m_ParentDevice.getHwInfo().m_nb_phys_audio_out); return 0.0; } if(m_control==eMC_Gain) { EfcGetMonitorGainCmd getCmd; getCmd.m_input=row; getCmd.m_output=col; if (!m_ParentDevice.doEfcOverAVC(getCmd)) { debugError("Cmd failed\n"); } retval=getCmd.m_value; did_command=true; } if(m_control==eMC_Pan) { EfcGetMonitorPanCmd getCmd; getCmd.m_input=row; getCmd.m_output=col; if (!m_ParentDevice.doEfcOverAVC(getCmd)) { debugError("Cmd failed\n"); } retval=getCmd.m_value; did_command=true; } if(m_control==eMC_Mute) { EfcGetMonitorMuteCmd getCmd; getCmd.m_input=row; getCmd.m_output=col; if (!m_ParentDevice.doEfcOverAVC(getCmd)) { debugError("Cmd failed\n"); } retval=getCmd.m_value; did_command=true; } if(m_control==eMC_Solo) { EfcGetMonitorSoloCmd getCmd; getCmd.m_input=row; getCmd.m_output=col; if (!m_ParentDevice.doEfcOverAVC(getCmd)) { debugError("Cmd failed\n"); } retval=getCmd.m_value; did_command=true; } debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for row %d col %d = %lf\n", row, col, retval); if (!did_command) { debugError("BUG: this should never happen due to enum\n"); } return retval; } int MonitorControl::getRowCount( ) { return m_ParentDevice.getHwInfo().m_nb_phys_audio_in; } int MonitorControl::getColCount( ) { return m_ParentDevice.getHwInfo().m_nb_phys_audio_out; } // --- the generic control element for single-value controls SimpleControl::SimpleControl(FireWorks::Device& p, enum eMixerTarget t, enum eMixerCommand c, int channel) : Control::Continuous(&p, "SimpleControl") , m_Slave(new EfcGenericMixerCmd(t, c, channel)) , m_ParentDevice(p) { } SimpleControl::SimpleControl(FireWorks::Device& p, enum eMixerTarget t, enum eMixerCommand c, int channel, std::string n) : Control::Continuous(&p, n) , m_Slave(new EfcGenericMixerCmd(t, c, channel)) , m_ParentDevice(p) { } SimpleControl::~SimpleControl() { delete m_Slave; } void SimpleControl::show() { debugOutput(DEBUG_LEVEL_NORMAL, "SimpleControl\n"); if(m_Slave) m_Slave->showEfcCmd(); } bool SimpleControl::setValue( const double val ) { if(m_Slave) { m_Slave->setType(eCT_Set); m_Slave->m_value = (uint32_t)val; if (!m_ParentDevice.doEfcOverAVC(*m_Slave)) { debugError("Cmd failed\n"); return 0.0; } // update the session block switch(m_Slave->getTarget()) { case eMT_PlaybackMix: switch(m_Slave->getCommand()) { case eMC_Gain: m_ParentDevice.m_session.h.playbackgains[m_Slave->m_channel] = m_Slave->m_value; break; default: // nothing break; } break; case eMT_PhysicalOutputMix: switch(m_Slave->getCommand()) { case eMC_Gain: m_ParentDevice.m_session.h.outputgains[m_Slave->m_channel] = m_Slave->m_value; break; default: // nothing break; } break; default: // nothing break; } debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for channel %d to %lf = %u\n", m_Slave->m_channel, val, m_Slave->m_value); return true; } else { debugError("No slave EFC command present\n"); return false; } } double SimpleControl::getValue( ) { if(m_Slave) { m_Slave->setType(eCT_Get); if (!m_ParentDevice.doEfcOverAVC(*m_Slave)) { debugError("Cmd failed\n"); return 0.0; } debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for channel %d = %u\n", m_Slave->m_channel, m_Slave->m_value); return m_Slave->m_value; } else { debugError("No slave EFC command present\n"); return 0.0; } } // --- the generic control element for on-off controls BinaryControl::BinaryControl(FireWorks::Device& p, enum eMixerTarget t, enum eMixerCommand c, int channel, int bit) : Control::Discrete(&p, "BinaryControl") , m_bit(bit) , m_Slave(new EfcGenericMixerCmd(t, c, channel)) , m_ParentDevice(p) { } BinaryControl::BinaryControl(FireWorks::Device& p, enum eMixerTarget t, enum eMixerCommand c, int channel, int bit, std::string n) : Control::Discrete(&p, n) , m_bit(bit) , m_Slave(new EfcGenericMixerCmd(t, c, channel)) , m_ParentDevice(p) { } BinaryControl::~BinaryControl() { delete m_Slave; } void BinaryControl::show() { debugOutput(DEBUG_LEVEL_NORMAL, "BinaryControl\n"); if(m_Slave) m_Slave->showEfcCmd(); } bool BinaryControl::setValue( const int val ) { if(m_Slave) { uint32_t reg; uint32_t old_reg; m_Slave->setType(eCT_Get); reg=m_Slave->m_value; old_reg=reg; if (val) { reg |= (1<setType(eCT_Set); m_Slave->m_value=reg; if (!m_ParentDevice.doEfcOverAVC(*m_Slave)) { debugError("Cmd failed\n"); return 0; } // update the session block switch(m_Slave->getTarget()) { case eMT_PlaybackMix: switch(m_Slave->getCommand()) { case eMC_Mute: m_ParentDevice.m_session.s.playbacks[m_Slave->m_channel].mute = m_Slave->m_value; break; case eMC_Solo: m_ParentDevice.m_session.s.playbacks[m_Slave->m_channel].solo = m_Slave->m_value; break; default: // nothing break; } break; case eMT_PhysicalOutputMix: switch(m_Slave->getCommand()) { case eMC_Mute: m_ParentDevice.m_session.s.outputs[m_Slave->m_channel].mute = m_Slave->m_value; break; case eMC_Nominal: m_ParentDevice.m_session.s.outputs[m_Slave->m_channel].shift = m_Slave->m_value; break; default: // nothing break; } break; case eMT_PhysicalInputMix: switch(m_Slave->getCommand()) { //case eMC_Pad: // m_ParentDevice.m_session.s.inputs[m_Slave->m_channel].pad = m_Slave->m_value; // break; case eMC_Nominal: m_ParentDevice.m_session.s.inputs[m_Slave->m_channel].shift = m_Slave->m_value; break; default: // nothing break; } break; default: // nothing break; } debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for channel %d to %d (reg: 0x%08X => 0x%08X)\n", m_Slave->m_channel, val, old_reg, reg); return true; } else { debugError("No slave EFC command present\n"); return false; } } int BinaryControl::getValue( ) { if(m_Slave) { // workaround for the failing get nominal command for input channels // get it from the session block if ((m_Slave->getTarget() == eMT_PhysicalInputMix) && (m_Slave->getCommand() == eMC_Nominal)) { int val = m_ParentDevice.m_session.s.inputs[m_Slave->m_channel].shift; debugOutput(DEBUG_LEVEL_VERBOSE, "input pad workaround: %08X\n", val); return val; } m_Slave->setType(eCT_Get); if (!m_ParentDevice.doEfcOverAVC(*m_Slave)) { debugError("Cmd failed\n"); return 0; } bool val= (m_Slave->m_value & (1<m_channel, m_Slave->m_value, val); return val; } else { debugError("No slave EFC command present\n"); return 0; } } // --- control element for flags SpdifModeControl::SpdifModeControl(FireWorks::Device& parent) : Control::Discrete(&parent, "SpdifModeControl") , m_ParentDevice(parent) { } SpdifModeControl::SpdifModeControl(FireWorks::Device& parent, std::string n) : Control::Discrete(&parent, n) , m_ParentDevice(parent) { } SpdifModeControl::~SpdifModeControl() { } void SpdifModeControl::show() { debugOutput(DEBUG_LEVEL_NORMAL, "SpdifModeControl\n"); } bool SpdifModeControl::setValue( const int val ) { EfcChangeFlagsCmd setCmd; if(val) { setCmd.m_setmask = FIREWORKS_EFC_FLAG_SPDIF_PRO; } else { setCmd.m_clearmask = FIREWORKS_EFC_FLAG_SPDIF_PRO; } debugOutput(DEBUG_LEVEL_VERBOSE, "setValue val: %d setmask: %08X, clear: %08X\n", val, setCmd.m_setmask, setCmd.m_clearmask); if (!m_ParentDevice.doEfcOverAVC(setCmd)) { debugError("Cmd failed\n"); return false; } return true; } int SpdifModeControl::getValue( ) { EfcGetFlagsCmd getCmd; if (!m_ParentDevice.doEfcOverAVC(getCmd)) { debugError("Cmd failed\n"); return 0; } debugOutput(DEBUG_LEVEL_VERBOSE, "got flags: %08X\n", getCmd.m_flags); if(getCmd.m_flags & FIREWORKS_EFC_FLAG_SPDIF_PRO) return 1; else return 0; } // --- io config controls IOConfigControl::IOConfigControl(FireWorks::Device& parent, enum eIOConfigRegister r) : Control::Discrete(&parent, "IOConfigControl") , m_Slave(new EfcGenericIOConfigCmd(r)) , m_ParentDevice(parent) { } IOConfigControl::IOConfigControl(FireWorks::Device& parent, enum eIOConfigRegister r, std::string n) : Control::Discrete(&parent, n) , m_Slave(new EfcGenericIOConfigCmd(r)) , m_ParentDevice(parent) { } IOConfigControl::~IOConfigControl() { delete m_Slave; } void IOConfigControl::show() { debugOutput(DEBUG_LEVEL_NORMAL, "IOConfigControl\n"); if(m_Slave) m_Slave->showEfcCmd(); } bool IOConfigControl::setValue( const int val ) { if(m_Slave) { m_Slave->setType(eCT_Set); m_Slave->m_value=val; if (!m_ParentDevice.doEfcOverAVC(*m_Slave)) { debugError("Cmd failed\n"); return 0; } debugOutput(DEBUG_LEVEL_VERBOSE, "setValue to %d \n", val); return true; } else { debugError("No slave EFC command present\n"); return false; } } int IOConfigControl::getValue( ) { if(m_Slave) { m_Slave->setType(eCT_Get); if (!m_ParentDevice.doEfcOverAVC(*m_Slave)) { debugError("Cmd failed\n"); return 0; } debugOutput(DEBUG_LEVEL_VERBOSE, "getValue: result=%d\n", m_Slave->m_value); return m_Slave->m_value; } else { debugError("No slave EFC command present\n"); return 0; } } // PlaybackRouting controls PlaybackRoutingControl::PlaybackRoutingControl(FireWorks::Device& parent) : Control::Discrete(&parent, "PlaybackRouting") , m_ParentDevice(parent) { } PlaybackRoutingControl::PlaybackRoutingControl(FireWorks::Device& parent, std::string n) : Control::Discrete(&parent, n) , m_ParentDevice(parent) { } PlaybackRoutingControl::~PlaybackRoutingControl() { } void PlaybackRoutingControl::show() { debugOutput(DEBUG_LEVEL_NORMAL, "PlaybackRouting\n"); } bool PlaybackRoutingControl::GetState(EfcIsocMapIOConfigCmd *cmd) { cmd->m_num_playmap_entries = 3; cmd->m_playmap[0] = 0; cmd->m_playmap[1] = 0; cmd->m_playmap[2] = 0; cmd->setType(eCT_Get); if (!m_ParentDevice.doEfcOverAVC(*cmd)) return false; return true; } bool PlaybackRoutingControl::setValue(int idx, int v) { EfcIsocMapIOConfigCmd setCmd; /* * NOTE: * Playback Stream ch1/2: 0 * Playback Stream ch3/4: 2 * Playback Stream ch5/6: 4 */ unsigned int value = v * 2; if (!GetState(&setCmd)) { debugError("Cmd failed\n"); return false; } setCmd.m_playmap[idx] = value; setCmd.setType(eCT_Set); if (!m_ParentDevice.doEfcOverAVC(setCmd)) { debugError("Cmd failed\n"); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, "setValue: result=%d:%d\n", idx, setCmd.m_playmap[idx]); return true; } int PlaybackRoutingControl::getValue(int idx) { EfcIsocMapIOConfigCmd getCmd; GetState(&getCmd); debugOutput(DEBUG_LEVEL_VERBOSE, "getValue: result=[%d][%d][%d]\n", getCmd.m_playmap[0], getCmd.m_playmap[1], getCmd.m_playmap[2]); return getCmd.m_playmap[idx] / 2; } // control to get hardware information HwInfoControl::HwInfoControl(FireWorks::Device& p, enum eHwInfoField f) : Control::Discrete(&p, "HwInfoControl") , m_ParentDevice(p) , m_Field(f) { } HwInfoControl::HwInfoControl(FireWorks::Device& p, enum eHwInfoField f, std::string n) : Control::Discrete(&p, n) , m_ParentDevice(p) , m_Field(f) { } HwInfoControl::~HwInfoControl() { } int HwInfoControl::getValue() { switch (m_Field) { case eHIF_PhysicalAudioOutCount: return m_ParentDevice.getHwInfo().m_nb_phys_audio_out; case eHIF_PhysicalAudioInCount: return m_ParentDevice.getHwInfo().m_nb_phys_audio_in; case eHIF_1394PlaybackCount: return m_ParentDevice.getHwInfo().m_nb_1394_playback_channels; case eHIF_1394RecordCount: return m_ParentDevice.getHwInfo().m_nb_1394_record_channels; case eHIF_GroupOutCount: return m_ParentDevice.getHwInfo().m_nb_out_groups; case eHIF_GroupInCount: return m_ParentDevice.getHwInfo().m_nb_in_groups; case eHIF_PhantomPower: return m_ParentDevice.getHwInfo().hasSoftwarePhantom(); case eHIF_OpticalInterface: return m_ParentDevice.getHwInfo().hasOpticalInterface(); case eHIF_PlaybackRouting: return m_ParentDevice.getHwInfo().hasPlaybackRouting(); default: debugError("Bogus field\n"); return 0; } } void HwInfoControl::show() { debugOutput(DEBUG_LEVEL_NORMAL, "HwInfoControl\n"); } // control to save settings MultiControl::MultiControl(FireWorks::Device& p, enum eType t) : Control::Discrete(&p, "MultiControl") , m_ParentDevice(p) , m_Type(t) { } MultiControl::MultiControl(FireWorks::Device& p, enum eType t, std::string n) : Control::Discrete(&p, n) , m_ParentDevice(p) , m_Type(t) { } MultiControl::~MultiControl() { } bool MultiControl::setValue(const int v) { switch(m_Type) { case eT_SaveSession: debugOutput(DEBUG_LEVEL_VERBOSE, "saving session\n"); return m_ParentDevice.saveSession(); case eT_Identify: debugOutput(DEBUG_LEVEL_VERBOSE, "identify device\n"); { EfcIdentifyCmd cmd; if (!m_ParentDevice.doEfcOverAVC(cmd)) { debugError("Cmd failed\n"); return false; } } return true; default: debugError("Bad type\n"); return false; } } void MultiControl::show() { debugOutput(DEBUG_LEVEL_NORMAL, "MultiControl\n"); switch(m_Type) { case eT_SaveSession: debugOutput(DEBUG_LEVEL_NORMAL, "Type: SaveSession\n"); break; case eT_Identify: debugOutput(DEBUG_LEVEL_NORMAL, "Type: Identify\n"); break; default: debugError("Bad type\n"); } } } // FireWorks libffado-2.4.5/src/fireworks/fireworks_control.h0000644000175000001440000001737214206145246021424 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIREWORKS_MATRIX_CONTROL_H #define FIREWORKS_MATRIX_CONTROL_H #include "debugmodule/debugmodule.h" #include "efc/efc_cmd.h" #include "efc/efc_cmds_hardware.h" #include "efc/efc_cmds_mixer.h" #include "efc/efc_cmds_monitor.h" #include "efc/efc_cmds_ioconfig.h" #include "libcontrol/BasicElements.h" #include "libcontrol/MatrixMixer.h" #include class ConfigRom; class Ieee1394Service; namespace FireWorks { class MonitorControl : public Control::MatrixMixer { public: enum eMonitorControl { eMC_Gain, eMC_Solo, eMC_Mute, eMC_Pan, }; public: MonitorControl(FireWorks::Device& parent, enum eMonitorControl); MonitorControl(FireWorks::Device& parent, enum eMonitorControl, std::string n); virtual ~MonitorControl() {}; virtual void show(); bool hasNames() const { return true; } virtual std::string getRowName( const int ); virtual std::string getColName( const int ); virtual int canWrite( const int, const int ); virtual double setValue( const int, const int, const double ); virtual double getValue( const int, const int ); virtual int getRowCount( ); virtual int getColCount( ); bool canConnect() const { return false; } // full map updates are unsupported virtual bool getCoefficientMap(int &) {return false;}; virtual bool storeCoefficientMap(int &) {return false;}; protected: enum eMonitorControl m_control; FireWorks::Device& m_ParentDevice; }; class SimpleControl : public Control::Continuous { public: SimpleControl(FireWorks::Device& parent, enum eMixerTarget, enum eMixerCommand, int channel); SimpleControl(FireWorks::Device& parent, enum eMixerTarget, enum eMixerCommand, int channel, std::string n); virtual ~SimpleControl(); virtual void show(); virtual bool setValue( const double ); virtual double getValue( ); virtual bool setValue(int idx, double v) {return setValue(v);}; virtual double getValue(int idx) {return getValue();}; virtual double getMinimum() {return -100.0;}; virtual double getMaximum() {return 10.0;}; protected: EfcGenericMixerCmd* m_Slave; FireWorks::Device& m_ParentDevice; }; // for on-off type of controls class BinaryControl : public Control::Discrete { public: BinaryControl(FireWorks::Device& parent, enum eMixerTarget, enum eMixerCommand, int channel, int bit); BinaryControl(FireWorks::Device& parent, enum eMixerTarget, enum eMixerCommand, int channel, int bit, std::string n); virtual ~BinaryControl(); virtual void show(); virtual bool setValue( const int ); virtual int getValue( ); virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 1;}; protected: int m_bit; EfcGenericMixerCmd* m_Slave; FireWorks::Device& m_ParentDevice; }; class SpdifModeControl : public Control::Discrete { public: SpdifModeControl(FireWorks::Device& parent); SpdifModeControl(FireWorks::Device& parent, std::string n); virtual ~SpdifModeControl(); virtual void show(); virtual bool setValue( const int ); virtual int getValue( ); virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 0;}; protected: FireWorks::Device& m_ParentDevice; }; // for on-off type of controls class IOConfigControl : public Control::Discrete { public: IOConfigControl(FireWorks::Device& parent, enum eIOConfigRegister); IOConfigControl(FireWorks::Device& parent, enum eIOConfigRegister, std::string n); virtual ~IOConfigControl(); virtual void show(); virtual bool setValue( const int ); virtual int getValue( ); virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 0;}; protected: int m_bit; EfcGenericIOConfigCmd* m_Slave; FireWorks::Device& m_ParentDevice; }; class PlaybackRoutingControl : public Control::Discrete { public: PlaybackRoutingControl(FireWorks::Device& parent); PlaybackRoutingControl(FireWorks::Device& parent, std::string n); virtual ~PlaybackRoutingControl(); virtual void show(); virtual bool setValue(int idx, int v); virtual int getValue(int idx); // not used virtual bool setValue(int v) {return 0;}; virtual int getValue() {return 0;}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 0;}; private: bool GetState(EfcIsocMapIOConfigCmd *cmd); FireWorks::Device& m_ParentDevice; }; class HwInfoControl : public Control::Discrete { public: enum eHwInfoField { eHIF_PhysicalAudioOutCount, eHIF_PhysicalAudioInCount, eHIF_1394PlaybackCount, eHIF_1394RecordCount, eHIF_GroupOutCount, eHIF_GroupInCount, eHIF_PhantomPower, eHIF_OpticalInterface, eHIF_PlaybackRouting }; public: HwInfoControl(FireWorks::Device& parent, enum eHwInfoField); HwInfoControl(FireWorks::Device& parent, enum eHwInfoField, std::string n); virtual ~HwInfoControl(); virtual void show(); virtual bool setValue( const int ) {return false;}; virtual int getValue( ); virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 0;}; protected: FireWorks::Device& m_ParentDevice; enum eHwInfoField m_Field; }; class MultiControl : public Control::Discrete { public: enum eType { eT_SaveSession, eT_Identify, }; public: MultiControl(FireWorks::Device& parent, enum eType); MultiControl(FireWorks::Device& parent, enum eType, std::string n); virtual ~MultiControl(); virtual void show(); virtual bool setValue( const int ); virtual int getValue( ) {return 0;}; virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 0;}; protected: FireWorks::Device& m_ParentDevice; enum eType m_Type; }; } // namespace FireWorks #endif libffado-2.4.5/src/fireworks/fireworks_device.cpp0000644000175000001440000007417414206145246021541 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // #include "config.h" #include "devicemanager.h" #include "fireworks_device.h" #include "efc/efc_avc_cmd.h" #include "efc/efc_cmds_flash.h" #include "audiofire/audiofire_device.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "fireworks/fireworks_control.h" #include "libutil/PosixMutex.h" #include "IntelFlashMap.h" #define ECHO_FLASH_ERASE_TIMEOUT_MILLISECS 2000 #define FIREWORKS_MIN_FIRMWARE_VERSION 0x04080000 #include #include #include using namespace std; // FireWorks is the platform used and developed by ECHO AUDIO namespace FireWorks { Device::Device(DeviceManager& d, ffado_smartptr( configRom )) : GenericAVC::Device( d, configRom) , m_poll_lock( new Util::PosixMutex("DEVPOLL") ) , m_efc_discovery_done ( false ) , m_MixerContainer ( NULL ) , m_HwInfoContainer ( NULL ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created FireWorks::Device (NodeID %d)\n", getConfigRom().getNodeId() ); } Device::~Device() { destroyMixer(); } void Device::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a FireWorks::Device\n"); if ( !m_efc_discovery_done) { if (!discoverUsingEFC()) { debugError("EFC discovery failed\n"); } } m_HwInfo.showEfcCmd(); GenericAVC::Device::showDevice(); } bool Device::probe( Util::Configuration& c, ConfigRom& configRom, bool generic ) { if(generic) { // try an EFC command EfcOverAVCCmd cmd( configRom.get1394Service() ); cmd.setCommandType( AVC::AVCCommand::eCT_Control ); cmd.setNodeId( configRom.getNodeId() ); cmd.setSubunitType( AVC::eST_Unit ); cmd.setSubunitId( 0xff ); cmd.setVerbose( configRom.getVerboseLevel() ); EfcHardwareInfoCmd hwInfo; hwInfo.setVerboseLevel(configRom.getVerboseLevel()); cmd.m_cmd = &hwInfo; if ( !cmd.fire()) { return false; } if ( cmd.getResponse() != AVC::AVCCommand::eR_Accepted) { return false; } if ( hwInfo.m_header.retval != EfcCmd::eERV_Ok && hwInfo.m_header.retval != EfcCmd::eERV_FlashBusy) { debugError( "EFC command failed\n" ); return false; } return true; } else { unsigned int vendorId = configRom.getNodeVendorId(); unsigned int modelId = configRom.getModelId(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); return c.isValid(vme) && vme.driver == Util::Configuration::eD_FireWorks; } } bool Device::discover() { unsigned int vendorId = getConfigRom().getNodeVendorId(); unsigned int modelId = getConfigRom().getModelId(); Util::Configuration &c = getDeviceManager().getConfiguration(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); if (c.isValid(vme) && vme.driver == Util::Configuration::eD_FireWorks) { debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", vme.vendor_name.c_str(), vme.model_name.c_str()); } else { debugWarning("Using generic ECHO Audio FireWorks support for unsupported device '%s %s'\n", getConfigRom().getVendorName().c_str(), getConfigRom().getModelName().c_str()); } // get the info from the EFC if ( !discoverUsingEFC() ) { return false; } // discover AVC-wise if ( !GenericAVC::Device::discoverGeneric() ) { debugError( "Could not discover GenericAVC::Device\n" ); return false; } if(!buildMixer()) { debugWarning("Could not build mixer\n"); } return true; } bool Device::discoverUsingEFC() { m_efc_discovery_done = false; m_HwInfo.setVerboseLevel(getDebugLevel()); if (!doEfcOverAVC(m_HwInfo)) { debugError("Could not read hardware capabilities\n"); return false; } // check the firmware version if (m_HwInfo.m_arm_version < FIREWORKS_MIN_FIRMWARE_VERSION) { fprintf(stderr, "Firmware version %u.%u (rev %u) not recent enough. FFADO requires at least version %u.%u (rev %u).\n", (m_HwInfo.m_arm_version >> 24) & 0xFF, (m_HwInfo.m_arm_version >> 16) & 0xFF, (m_HwInfo.m_arm_version >> 0) & 0xFFFF, (FIREWORKS_MIN_FIRMWARE_VERSION >> 24) & 0xFF, (FIREWORKS_MIN_FIRMWARE_VERSION >> 16) & 0xFF, (FIREWORKS_MIN_FIRMWARE_VERSION >> 0) & 0xFFFF ); return false; } m_current_clock = -1; m_efc_discovery_done = true; return true; } FFADODevice * Device::createDevice(DeviceManager& d, ffado_smartptr( configRom )) { unsigned int vendorId = configRom->getNodeVendorId(); // unsigned int modelId = configRom->getModelId(); switch(vendorId) { case FW_VENDORID_ECHO: return new ECHO::AudioFire(d, configRom ); default: return new Device(d, configRom ); } } bool Device::doEfcOverAVC(EfcCmd &c) { EfcOverAVCCmd cmd( get1394Service() ); cmd.setCommandType( AVC::AVCCommand::eCT_Control ); cmd.setNodeId( getConfigRom().getNodeId() ); cmd.setSubunitType( AVC::eST_Unit ); cmd.setSubunitId( 0xff ); cmd.setVerbose( getDebugLevel() ); cmd.m_cmd = &c; if (!cmd.fire()) { debugError( "EfcOverAVCCmd command failed\n" ); c.showEfcCmd(); return false; } if ( cmd.getResponse() != AVC::AVCCommand::eR_Accepted) { debugError( "EfcOverAVCCmd not accepted\n" ); return false; } if ( c.m_header.retval != EfcCmd::eERV_Ok && c.m_header.retval != EfcCmd::eERV_FlashBusy) { debugError( "EFC command failed\n" ); c.showEfcCmd(); return false; } return true; } bool Device::buildMixer() { bool result=true; debugOutput(DEBUG_LEVEL_VERBOSE, "Building a FireWorks mixer...\n"); destroyMixer(); // create the mixer object container m_MixerContainer = new Control::Container(this, "Mixer"); if (!m_MixerContainer) { debugError("Could not create mixer container...\n"); return false; } // create control objects for the audiofire // matrix mix controls result &= m_MixerContainer->addElement( new MonitorControl(*this, MonitorControl::eMC_Gain, "MonitorGain")); result &= m_MixerContainer->addElement( new MonitorControl(*this, MonitorControl::eMC_Mute, "MonitorMute")); result &= m_MixerContainer->addElement( new MonitorControl(*this, MonitorControl::eMC_Solo, "MonitorSolo")); result &= m_MixerContainer->addElement( new MonitorControl(*this, MonitorControl::eMC_Pan, "MonitorPan")); // Playback mix controls for (unsigned int ch=0;chaddElement( new BinaryControl(*this, eMT_PlaybackMix, eMC_Mute, ch, 0, node_name.str()+"Mute")); result &= m_MixerContainer->addElement( new BinaryControl(*this, eMT_PlaybackMix, eMC_Solo, ch, 0, node_name.str()+"Solo")); result &= m_MixerContainer->addElement( new SimpleControl(*this, eMT_PlaybackMix, eMC_Gain, ch, node_name.str()+"Gain")); } // Physical output mix controls for (unsigned int ch=0;chaddElement( new BinaryControl(*this, eMT_PhysicalOutputMix, eMC_Mute, ch, 0, node_name.str()+"Mute")); result &= m_MixerContainer->addElement( new BinaryControl(*this, eMT_PhysicalOutputMix, eMC_Nominal, ch, 1, node_name.str()+"Nominal")); result &= m_MixerContainer->addElement( new SimpleControl(*this, eMT_PhysicalOutputMix, eMC_Gain, ch, node_name.str()+"Gain")); } // Physical input mix controls for (unsigned int ch=0;chaddElement( // new BinaryControl(*this, eMT_PhysicalInputMix, eMC_Pad, ch, 0, node_name.str()+"Pad")); result &= m_MixerContainer->addElement( new BinaryControl(*this, eMT_PhysicalInputMix, eMC_Nominal, ch, 1, node_name.str()+"Nominal")); } // add hardware information controls m_HwInfoContainer = new Control::Container(this, "HwInfo"); result &= m_HwInfoContainer->addElement( new HwInfoControl(*this, HwInfoControl::eHIF_PhysicalAudioOutCount, "PhysicalAudioOutCount")); result &= m_HwInfoContainer->addElement( new HwInfoControl(*this, HwInfoControl::eHIF_PhysicalAudioInCount, "PhysicalAudioInCount")); result &= m_HwInfoContainer->addElement( new HwInfoControl(*this, HwInfoControl::eHIF_1394PlaybackCount, "1394PlaybackCount")); result &= m_HwInfoContainer->addElement( new HwInfoControl(*this, HwInfoControl::eHIF_1394RecordCount, "1394RecordCount")); result &= m_HwInfoContainer->addElement( new HwInfoControl(*this, HwInfoControl::eHIF_GroupOutCount, "GroupOutCount")); result &= m_HwInfoContainer->addElement( new HwInfoControl(*this, HwInfoControl::eHIF_GroupInCount, "GroupInCount")); result &= m_HwInfoContainer->addElement( new HwInfoControl(*this, HwInfoControl::eHIF_PhantomPower, "PhantomPower")); result &= m_HwInfoContainer->addElement( new HwInfoControl(*this, HwInfoControl::eHIF_OpticalInterface, "OpticalInterface")); result &= m_HwInfoContainer->addElement( new HwInfoControl(*this, HwInfoControl::eHIF_PlaybackRouting, "PlaybackRouting")); // add a save settings control result &= this->addElement( new MultiControl(*this, MultiControl::eT_SaveSession, "SaveSettings")); // add an identify control result &= this->addElement( new MultiControl(*this, MultiControl::eT_Identify, "Identify")); // spdif mode control result &= this->addElement( new SpdifModeControl(*this, "SpdifMode")); // check for IO config controls and add them if necessary if(m_HwInfo.hasMirroring()) { result &= this->addElement( new IOConfigControl(*this, eCR_Mirror, "ChannelMirror")); } if(m_HwInfo.hasOpticalInterface()) { result &= this->addElement( new IOConfigControl(*this, eCR_DigitalInterface, "DigitalInterface")); } if(m_HwInfo.hasSoftwarePhantom()) { result &= this->addElement( new IOConfigControl(*this, eCR_Phantom, "PhantomPower")); } if(m_HwInfo.hasPlaybackRouting()) { result &= this->addElement( new PlaybackRoutingControl(*this, "PlaybackRouting")); } if (!result) { debugWarning("One or more control elements could not be created."); // clean up those that couldn't be created destroyMixer(); return false; } if (!addElement(m_MixerContainer)) { debugWarning("Could not register mixer to device\n"); // clean up destroyMixer(); return false; } if (!addElement(m_HwInfoContainer)) { debugWarning("Could not register hwinfo to device\n"); // clean up destroyMixer(); return false; } // load the session block if (!loadSession()) { debugWarning("Could not load session\n"); } return true; } bool Device::destroyMixer() { debugOutput(DEBUG_LEVEL_VERBOSE, "destroy mixer...\n"); if (m_MixerContainer == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "no mixer to destroy...\n"); } else { if (!deleteElement(m_MixerContainer)) { debugError("Mixer present but not registered to the avdevice\n"); return false; } // remove and delete (as in free) child control elements m_MixerContainer->clearElements(true); delete m_MixerContainer; m_MixerContainer = NULL; } if (m_HwInfoContainer == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "no hwinfo to destroy...\n"); } else { if (!deleteElement(m_HwInfoContainer)) { debugError("HwInfo present but not registered to the avdevice\n"); return false; } // remove and delete (as in free) child control elements m_HwInfoContainer->clearElements(true); delete m_HwInfoContainer; m_HwInfoContainer = NULL; } return true; } bool Device::saveSession() { // save the session block // if ( !updateSession() ) { // debugError( "Could not update session\n" ); // } else { if ( !m_session.saveToDevice(*this) ) { debugError( "Could not save session block\n" ); } // } return true; } bool Device::loadSession() { if ( !m_session.loadFromDevice(*this) ) { debugError( "Could not load session block\n" ); return false; } return true; } /* * NOTE: * Firmware version 5.0 or later for AudioFire12 returns invalid values to * contents of response against this command. */ bool Device::updatePolledValues() { Util::MutexLockHelper lock(*m_poll_lock); return doEfcOverAVC(m_Polled); } #define ECHO_CHECK_AND_ADD_SR(v, x) \ { if(x >= m_HwInfo.m_min_sample_rate && x <= m_HwInfo.m_max_sample_rate) \ v.push_back(x); } std::vector Device::getSupportedSamplingFrequencies() { std::vector frequencies; ECHO_CHECK_AND_ADD_SR(frequencies, 22050); ECHO_CHECK_AND_ADD_SR(frequencies, 24000); ECHO_CHECK_AND_ADD_SR(frequencies, 32000); ECHO_CHECK_AND_ADD_SR(frequencies, 44100); ECHO_CHECK_AND_ADD_SR(frequencies, 48000); ECHO_CHECK_AND_ADD_SR(frequencies, 88200); ECHO_CHECK_AND_ADD_SR(frequencies, 96000); ECHO_CHECK_AND_ADD_SR(frequencies, 176400); ECHO_CHECK_AND_ADD_SR(frequencies, 192000); return frequencies; } FFADODevice::ClockSourceVector Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; if (!m_efc_discovery_done) { debugError("EFC discovery not done yet!\n"); return r; } uint32_t active_clock = getClockSrc(); if(EFC_CMD_HW_CHECK_FLAG(m_HwInfo.m_supported_clocks, EFC_CMD_HW_CLOCK_INTERNAL)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Internal clock supported\n"); ClockSource s=clockIdToClockSource(EFC_CMD_HW_CLOCK_INTERNAL); s.active=(active_clock == EFC_CMD_HW_CLOCK_INTERNAL); if (s.type != eCT_Invalid) r.push_back(s); } if(EFC_CMD_HW_CHECK_FLAG(m_HwInfo.m_supported_clocks, EFC_CMD_HW_CLOCK_SYTMATCH)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Syt Match clock supported\n"); ClockSource s=clockIdToClockSource(EFC_CMD_HW_CLOCK_SYTMATCH); s.active=(active_clock == EFC_CMD_HW_CLOCK_SYTMATCH); if (s.type != eCT_Invalid) r.push_back(s); } if(EFC_CMD_HW_CHECK_FLAG(m_HwInfo.m_supported_clocks, EFC_CMD_HW_CLOCK_WORDCLOCK)) { debugOutput(DEBUG_LEVEL_VERBOSE, "WordClock supported\n"); ClockSource s=clockIdToClockSource(EFC_CMD_HW_CLOCK_WORDCLOCK); s.active=(active_clock == EFC_CMD_HW_CLOCK_WORDCLOCK); if (s.type != eCT_Invalid) r.push_back(s); } if(EFC_CMD_HW_CHECK_FLAG(m_HwInfo.m_supported_clocks, EFC_CMD_HW_CLOCK_SPDIF)) { debugOutput(DEBUG_LEVEL_VERBOSE, "SPDIF clock supported\n"); ClockSource s=clockIdToClockSource(EFC_CMD_HW_CLOCK_SPDIF); s.active=(active_clock == EFC_CMD_HW_CLOCK_SPDIF); if (s.type != eCT_Invalid) r.push_back(s); } if(EFC_CMD_HW_CHECK_FLAG(m_HwInfo.m_supported_clocks, EFC_CMD_HW_CLOCK_ADAT_1)) { debugOutput(DEBUG_LEVEL_VERBOSE, "ADAT 1 clock supported\n"); ClockSource s=clockIdToClockSource(EFC_CMD_HW_CLOCK_ADAT_1); s.active=(active_clock == EFC_CMD_HW_CLOCK_ADAT_1); if (s.type != eCT_Invalid) r.push_back(s); } if(EFC_CMD_HW_CHECK_FLAG(m_HwInfo.m_supported_clocks, EFC_CMD_HW_CLOCK_ADAT_2)) { debugOutput(DEBUG_LEVEL_VERBOSE, "ADAT 2 clock supported\n"); ClockSource s=clockIdToClockSource(EFC_CMD_HW_CLOCK_ADAT_2); s.active=(active_clock == EFC_CMD_HW_CLOCK_ADAT_2); if (s.type != eCT_Invalid) r.push_back(s); } return r; } bool Device::isClockValid(uint32_t id) { // always valid if (id==EFC_CMD_HW_CLOCK_INTERNAL) return true; // the polled values tell whether each clock source is detected or not if (!updatePolledValues()) { debugError("Could not update polled values\n"); return false; } return EFC_CMD_HW_CHECK_FLAG(m_Polled.m_status,id); } bool Device::setActiveClockSource(ClockSource s) { bool result; debugOutput(DEBUG_LEVEL_VERBOSE, "setting clock source to id: %d\n",s.id); if(!isClockValid(s.id)) { debugError("Clock not valid\n"); return false; } result = setClockSrc(s.id); // From the ECHO sources: // "If this is a 1200F and the sample rate is being set via EFC, then // send the "phy reconnect command" so the device will vanish and reappear // with a new descriptor." // EfcPhyReconnectCmd rccmd; // if(!doEfcOverAVC(rccmd)) { // debugError("Phy reconnect failed\n"); // } else { // // sleep for one second such that the phy can get reconnected // sleep(1); // } return result; } FFADODevice::ClockSource Device::getActiveClockSource() { ClockSource s; uint32_t active_clock = getClockSrc(); s=clockIdToClockSource(active_clock); s.active=true; return s; } FFADODevice::ClockSource Device::clockIdToClockSource(uint32_t clockid) { ClockSource s; debugOutput(DEBUG_LEVEL_VERBOSE, "clock id: %u\n", clockid); switch (clockid) { case EFC_CMD_HW_CLOCK_INTERNAL: debugOutput(DEBUG_LEVEL_VERBOSE, "Internal clock\n"); s.type=eCT_Internal; s.description="Internal sync"; break; case EFC_CMD_HW_CLOCK_SYTMATCH: debugOutput(DEBUG_LEVEL_VERBOSE, "Syt Match\n"); s.type=eCT_SytMatch; s.description="SYT Match"; break; case EFC_CMD_HW_CLOCK_WORDCLOCK: debugOutput(DEBUG_LEVEL_VERBOSE, "WordClock\n"); s.type=eCT_WordClock; s.description="Word Clock"; break; case EFC_CMD_HW_CLOCK_SPDIF: debugOutput(DEBUG_LEVEL_VERBOSE, "SPDIF clock\n"); s.type=eCT_SPDIF; s.description="SPDIF"; break; case EFC_CMD_HW_CLOCK_ADAT_1: debugOutput(DEBUG_LEVEL_VERBOSE, "ADAT 1 clock\n"); s.type=eCT_ADAT; s.description="ADAT 1"; break; case EFC_CMD_HW_CLOCK_ADAT_2: debugOutput(DEBUG_LEVEL_VERBOSE, "ADAT 2 clock\n"); s.type=eCT_ADAT; s.description="ADAT 2"; break; default: debugError("Invalid clock id: %d\n",clockid); return s; // return an invalid ClockSource } s.id=clockid; s.valid=isClockValid(clockid); return s; } bool Device::getClock(EfcGetClockCmd &gccmd) { if (!doEfcOverAVC(gccmd)) return false; /* * NOTE: * Firmware version 5.0 or later for AudioFire12 returns invalid * values in contents of response against this command. */ if (gccmd.m_samplerate > 192000) { debugOutput(DEBUG_LEVEL_NORMAL, "Could not get sampling rate. Do fallback\n"); int sampling_rate; /* fallback to 'input/output plug signal format' command */ sampling_rate = GenericAVC::Device::getSamplingFrequency(); /* fallback failed */ if (!sampling_rate) { debugOutput(DEBUG_LEVEL_NORMAL, "Fallback failed\n"); return false; } gccmd.m_samplerate = sampling_rate; } if (gccmd.m_clock > EFC_CMD_HW_CLOCK_COUNT) { debugOutput(DEBUG_LEVEL_NORMAL, "Could not get clock info. Do fallback\n"); if (m_current_clock < 0) { /* fallback to internal clock source */ EfcSetClockCmd sccmd; sccmd.m_clock = EFC_CMD_HW_CLOCK_INTERNAL; sccmd.m_samplerate = gccmd.m_samplerate; sccmd.m_index = 0; if (!doEfcOverAVC(sccmd)) { debugOutput(DEBUG_LEVEL_NORMAL, "Fallback failed\n"); return false; } /* Cache clock source */ m_current_clock = sccmd.m_clock; } /* Fallback to cache */ gccmd.m_clock = m_current_clock; } return true; } uint32_t Device::getClockSrc() { EfcGetClockCmd gccmd; if (!getClock(gccmd)) return EFC_CMD_HW_CLOCK_UNSPECIFIED; debugOutput(DEBUG_LEVEL_VERBOSE, "Get current clock source: %d\n", gccmd.m_clock); return gccmd.m_clock; } int Device::getSamplingFrequency() { EfcGetClockCmd gccmd; if (!getClock(gccmd)) return 0; debugOutput(DEBUG_LEVEL_VERBOSE, "Get current sample rate: %d\n", gccmd.m_samplerate); return gccmd.m_samplerate; } bool Device::setClock(EfcSetClockCmd sccmd) { if (!doEfcOverAVC(sccmd)) { debugError("Could not set clock info\n"); return false; } /* Cache clock source for fallback. */ m_current_clock = sccmd.m_clock; return true; } bool Device::setClockSrc(uint32_t id) { bool err; EfcGetClockCmd gccmd; err = getClock(gccmd); if (!err) return err; EfcSetClockCmd sccmd; sccmd.m_clock = id; sccmd.m_samplerate = gccmd.m_samplerate; sccmd.m_index = 0; err = setClock(sccmd); if (err) debugOutput(DEBUG_LEVEL_VERBOSE, "Set current clock source: %d\n", sccmd.m_clock); return err; } bool Device::setSamplingFrequency(int samplerate) { bool err; EfcGetClockCmd gccmd; err = getClock(gccmd); if (!err) return err; EfcSetClockCmd sccmd; sccmd.m_clock = gccmd.m_clock; sccmd.m_samplerate = samplerate; sccmd.m_index = 0; err = setClock(sccmd); if (err) debugOutput(DEBUG_LEVEL_VERBOSE, "Set current sample rate: %d\n", sccmd.m_samplerate); return err; } bool Device::lockFlash(bool lock) { // some hardware doesn't need/support flash lock if (m_HwInfo.hasDSP()) { debugOutput(DEBUG_LEVEL_VERBOSE, "flash lock not needed\n"); return true; } EfcFlashLockCmd cmd; cmd.m_lock = lock; if(!doEfcOverAVC(cmd)) { debugError("Flash lock failed\n"); return false; } return true; } bool Device::writeFlash(uint32_t start, uint32_t len, uint32_t* buffer) { if(len <= 0 || 0xFFFFFFFF - len*4 < start) { debugError("bogus start/len: 0x%08X / %u\n", start, len); return false; } if(start & 0x03) { debugError("start address not quadlet aligned: 0x%08X\n", start); return false; } uint32_t start_addr = start; uint32_t stop_addr = start + len*4; uint32_t *target_buffer = buffer; EfcFlashWriteCmd cmd; // write EFC_FLASH_SIZE_BYTES at a time for(start_addr = start; start_addr < stop_addr; start_addr += EFC_FLASH_SIZE_BYTES) { cmd.m_address = start_addr; unsigned int quads_to_write = (stop_addr - start_addr)/4; if (quads_to_write > EFC_FLASH_SIZE_QUADS) { quads_to_write = EFC_FLASH_SIZE_QUADS; } cmd.m_nb_quadlets = quads_to_write; for(unsigned int i=0; i EFC_FLASH_SIZE_QUADS) { quads_to_read = EFC_FLASH_SIZE_QUADS; } uint32_t quadlets_read = 0; int ntries = 10000; do { cmd.m_address = start_addr + quadlets_read*4; unsigned int new_to_read = quads_to_read - quadlets_read; cmd.m_nb_quadlets = new_to_read; if(!doEfcOverAVC(cmd)) { debugError("Flash read failed for block 0x%08X (%d quadlets)\n", start_addr, quads_to_read); return false; } if(cmd.m_nb_quadlets != new_to_read) { debugOutput(DEBUG_LEVEL_VERBOSE, "Flash read didn't return enough data (%u/%u) \n", cmd.m_nb_quadlets, new_to_read); // continue trying } quadlets_read += cmd.m_nb_quadlets; // copy content for(unsigned int i=0; i quads_left) { blocksize_quads = quads_left; } // do the actual erase if (!eraseFlash(start_address)) { debugWarning("Could not erase flash block at 0x%08X\n", start_address); success = false; } else { // wait for the flash to become ready again if (!waitForFlash(ECHO_FLASH_ERASE_TIMEOUT_MILLISECS)) { debugError("Wait for flash timed out at address 0x%08X\n", start_address); return false; } // verify that the block is empty as an extra precaution if (!readFlash(start_address, blocksize_quads, verify)) { debugError("Could not read flash block at 0x%08X\n", start_address); return false; } // everything should be 0xFFFFFFFF if the erase was successful for (unsigned int i = 0; i < blocksize_quads; i++) { if (0xFFFFFFFF != verify[i]) { debugWarning("Flash erase verification failed.\n"); success = false; break; } } } if (success) { start_address += blocksize_bytes; quads_left -= blocksize_quads; nb_tries = 0; } else { nb_tries++; } if (nb_tries > max_nb_tries) { debugError("Needed too many tries to erase flash at 0x%08X\n", start_address); return false; } } while (quads_left > 0); return true; } bool Device::waitForFlash(unsigned int msecs) { bool ready; EfcFlashGetStatusCmd statusCmd; const unsigned int time_to_sleep_usecs = 10000; int wait_cycles = msecs * 1000 / time_to_sleep_usecs; do { if (!doEfcOverAVC(statusCmd)) { debugError("Could not read flash status\n"); return false; } if (statusCmd.m_header.retval == EfcCmd::eERV_FlashBusy) { ready = false; } else { ready = statusCmd.m_ready; } usleep(time_to_sleep_usecs); } while (!ready && wait_cycles--); if(wait_cycles == 0) { debugError("Timeout while waiting for flash\n"); return false; } return ready; } uint32_t Device::getSessionBase() { EfcFlashGetSessionBaseCmd cmd; if(!doEfcOverAVC(cmd)) { debugError("Could not get session base address\n"); return 0; // FIXME: arbitrary } return cmd.m_address; } } // FireWorks libffado-2.4.5/src/fireworks/fireworks_device.h0000644000175000001440000001124714206145246021176 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIREWORKS_DEVICE_H #define FIREWORKS_DEVICE_H #include "debugmodule/debugmodule.h" #include "genericavc/avc_avdevice.h" #include "efc/efc_cmd.h" #include "efc/efc_cmds_hardware.h" #include "efc/efc_cmds_hardware_ctrl.h" #include "fireworks_session_block.h" #include #include "libutil/Mutex.h" class ConfigRom; class Ieee1394Service; namespace FireWorks { class Device : public GenericAVC::Device { friend class MonitorControl; friend class SimpleControl; friend class BinaryControl; friend class IOConfigControl; public: Device( DeviceManager& d, ffado_smartptr( configRom ) ); virtual ~Device(); static bool probe( Util::Configuration&, ConfigRom& configRom, bool generic = false ); static FFADODevice * createDevice( DeviceManager& d, ffado_smartptr( configRom )); virtual bool discover(); virtual void showDevice(); virtual bool buildMixer(); virtual bool destroyMixer(); virtual std::vector getSupportedSamplingFrequencies(); virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); virtual int getSamplingFrequency(); virtual bool setSamplingFrequency( int ); const EfcHardwareInfoCmd getHwInfo() {return m_HwInfo;}; // protected: //? bool doEfcOverAVC(EfcCmd& c); /** * @brief Read flash * @param start start address * @param len length in quadlets (4 bytes) * @param buffer target buffer (should be 'len*4' bytes long) * @return true if successful */ bool readFlash(uint32_t start, uint32_t len, uint32_t* buffer); /** * @brief Write flash * @param start start address * @param len length in quadlets (4 bytes) * @param buffer target buffer (should be 'len*4' bytes long) * @return true if successful */ bool writeFlash(uint32_t start, uint32_t len, uint32_t* buffer); /** * @brief (un)lock the flash * @param lock true=locked, false=unlocked * @return true if successful */ bool lockFlash(bool lock); /** * @brief erase flash block * @param addr address of block to erase * @return true if successful */ bool eraseFlash(uint32_t addr); bool eraseFlashBlocks(uint32_t start_address, unsigned int nb_quads); /** * @brief wait until the device indicates the flash memory is ready * @param msecs time to wait before timeout * @return true if the flash is ready, false if timeout */ bool waitForFlash(unsigned int msecs); /** * @brief get the flash address of the session block * @return session block base address */ uint32_t getSessionBase(); /** * load the session block from the device * @return true if successful */ bool loadSession(); /** * save the session block to the device * @return true if successful */ bool saveSession(); // Echo specific stuff private: bool discoverUsingEFC(); FFADODevice::ClockSource clockIdToClockSource(uint32_t clockflag); bool isClockValid(uint32_t id); bool getClock(EfcGetClockCmd &gccmd); uint32_t getClockSrc(); bool setClock(EfcSetClockCmd sccmd); bool setClockSrc(uint32_t clock); /* * Audiofire12 often return wrong data for clock source. * This member is used to cache the latest clock source change. */ int m_current_clock; EfcHardwareInfoCmd m_HwInfo; bool updatePolledValues(); Util::Mutex* m_poll_lock; EfcPolledValuesCmd m_Polled; bool m_efc_discovery_done; protected: Session m_session; private: Control::Container *m_MixerContainer; Control::Container *m_HwInfoContainer; }; } // namespace FireWorks #endif libffado-2.4.5/src/fireworks/fireworks_firmware.cpp0000644000175000001440000003711614206145246022111 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "fireworks_device.h" #include "fireworks_firmware.h" #include "efc/efc_avc_cmd.h" #include "efc/efc_cmd.h" #include "efc/efc_cmds_flash.h" #include "libieee1394/configrom.h" #include "libieee1394/vendor_model_ids.h" #include #include #include #include #include #define DAT_EXTENSION "dat" // device id's #define AUDIOFIRE2 0x000af2 #define AUDIOFIRE4 0x000af4 #define AUDIOFIRE8 0x000af8 #define AUDIOFIRE12 0x00af12 #define AUDIOFIRE12HD 0x0af12d #define FWHDMI 0x00afd1 #define ONYX400F 0x00400f #define ONYX1200F 0x01200f #define FIREWORKS8 0x0000f8 using namespace std; template bool from_string(T& t, const std::string& s, std::ios_base& (*f)(std::ios_base&)) { std::istringstream iss(s); return !(iss >> f >> t).fail(); } // These classes provide support for reading/writing the firmware on // echo fireworks based devices namespace FireWorks { IMPL_DEBUG_MODULE( Firmware, Firmware, DEBUG_LEVEL_NORMAL ); IMPL_DEBUG_MODULE( FirmwareUtil, FirmwareUtil, DEBUG_LEVEL_NORMAL ); // the firmware class // some generic string generation functions const char *Firmware::eDatTypeToString(const enum Firmware::eDatType type) { switch (type) { case eDT_DspCode: return "Dsp Code"; case eDT_IceLynxCode: return "IceLynx Code"; case eDT_Data: return "Data"; case eDT_FPGACode: return "FPGA Code"; case eDT_DeviceName: return "Device Name"; default: return "invalid"; } } const enum Firmware::eDatType Firmware::intToeDatType(int type) { switch (type) { case (int)eDT_DspCode: return eDT_DspCode; case (int)eDT_IceLynxCode: return eDT_IceLynxCode; case (int)eDT_Data: return eDT_Data; case (int)eDT_FPGACode: return eDT_FPGACode; case (int)eDT_DeviceName: return eDT_DeviceName; default: return eDT_Invalid; } } Firmware::Firmware() : m_source( "none" ) , m_Type ( eDT_Invalid ) , m_flash_offset_address ( 0 ) , m_length_quads ( 0 ) , m_CRC32 ( 0 ) , m_checksum ( 0 ) , m_version ( 0 ) , m_append_crc ( false ) , m_footprint_quads ( 0 ) , m_data( NULL ) , m_valid( false ) { } Firmware::Firmware(const Firmware& f) { debugOutput(DEBUG_LEVEL_VERBOSE, "copy constructor\n"); m_source = f.m_source; m_Type = f.m_Type; m_flash_offset_address = f.m_flash_offset_address; m_length_quads = f.m_length_quads; m_CRC32 = f.m_CRC32; m_checksum = f.m_checksum; m_version = f.m_version; m_append_crc = f.m_append_crc; m_footprint_quads = f.m_footprint_quads; m_valid = f.m_valid; m_data = new uint32_t[m_length_quads]; memcpy(m_data, f.m_data, m_length_quads*sizeof(uint32_t)); } Firmware& Firmware::operator=(const Firmware& f) { debugOutput(DEBUG_LEVEL_VERBOSE, "assignment\n"); if (this != &f) { // make sure not same object // assign new vars m_source = f.m_source; m_Type = f.m_Type; m_flash_offset_address = f.m_flash_offset_address; m_length_quads = f.m_length_quads; m_CRC32 = f.m_CRC32; m_checksum = f.m_checksum; m_version = f.m_version; m_append_crc = f.m_append_crc; m_footprint_quads = f.m_footprint_quads; m_valid = f.m_valid; // replace dynamic data delete [] m_data; m_data = new uint32_t[m_length_quads]; memcpy(m_data, f.m_data, m_length_quads*sizeof(uint32_t)); } return *this; // Return ref for multiple assignment } Firmware::~Firmware() { if (m_data) delete[] m_data; } void Firmware::show() { #ifdef DEBUG debugOutput(DEBUG_LEVEL_NORMAL, "Firmware from %s\n", m_source.c_str()); debugOutput(DEBUG_LEVEL_NORMAL, " Valid? : %s\n", (m_valid?"Yes":"No")); debugOutput(DEBUG_LEVEL_NORMAL, " Type : %s\n", eDatTypeToString(m_Type)); if (m_Type == eDT_Invalid) return; unsigned int version_major = (m_version & 0xFF000000) >> 24; unsigned int version_minor = (m_version & 0x00FF0000) >> 16; unsigned int version_build = (m_version & 0x0000FFFF); debugOutput(DEBUG_LEVEL_NORMAL, " Address Offset : 0x%08X\n", m_flash_offset_address); debugOutput(DEBUG_LEVEL_NORMAL, " Length (Quadlets) : 0x%08X\n", m_length_quads); debugOutput(DEBUG_LEVEL_NORMAL, " CRC 32 : 0x%08X\n", m_CRC32); debugOutput(DEBUG_LEVEL_NORMAL, " Checksum : 0x%08X\n", m_checksum); debugOutput(DEBUG_LEVEL_NORMAL, " Firmware version : %02u.%02u.%02u (0x%08X)\n", version_major, version_minor, version_build, m_version); debugOutput(DEBUG_LEVEL_NORMAL, " Append CRC : %s\n", (m_append_crc?"Yes":"No")); debugOutput(DEBUG_LEVEL_NORMAL, " Footprint (Quadlets) : 0x%08X\n", m_footprint_quads); #endif } bool Firmware::operator==(const Firmware& f) { debugOutput(DEBUG_LEVEL_VERBOSE, "Comparing header...\n"); if(m_flash_offset_address != f.m_flash_offset_address) { debugOutput(DEBUG_LEVEL_VERBOSE, "Flash address differs: %08X != %08X\n", m_flash_offset_address, f.m_flash_offset_address); return false; } if(m_length_quads != f.m_length_quads) { debugOutput(DEBUG_LEVEL_VERBOSE, "Flash length differs: %08X != %08X\n", m_length_quads, f.m_length_quads); return false; } if(m_data == NULL && f.m_data == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "both firmwares have no data\n"); return true; } if(m_data == NULL || f.m_data == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "one of the firmwares has no data: %p != %p\n", m_data, f.m_data); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, "Comparing data...\n"); bool retval = true; for(unsigned int i=0; i ECHO_FIRMWARE_FILE_MAX_LENGTH_BYTES) { debugError("File too large (%d bytes).\n", size); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, " Checking magic...\n"); // read magic if( size < ECHO_FIRMWARE_MAGIC_LENGTH_BYTES) { debugError("File too small (%d bytes) to contain the magic header.\n", size); return false; } fwfile.seekg (0, ios::beg); getline(fwfile, m_magic); // get rid of the DOS-Style end of line string::size_type loc = m_magic.find( '\r' ); if( loc != string::npos ) { m_magic.erase(loc); } loc = m_magic.find( '\n' ); if( loc != string::npos ) { m_magic.erase(loc); } // check the magic if (m_magic != ECHO_FIRMWARE_MAGIC) { debugError("Magic was '%s' but should have been '%s'\n", m_magic.c_str(), ECHO_FIRMWARE_MAGIC); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, " magic OK...\n"); debugOutput(DEBUG_LEVEL_VERBOSE, " Reading header...\n"); // read header if( size < ECHO_FIRMWARE_MAGIC_LENGTH_BYTES + ECHO_FIRMWARE_HEADER_LENGTH_BYTES) { debugError("File too small to contain the header.\n"); return false; } for (int i=0; i < ECHO_FIRMWARE_HEADER_LENGTH_QUADLETS; i++) { std::string buffer; getline(fwfile, buffer); // get rid of the DOS-Style end of line string::size_type loc = buffer.find( '\r' ); if( loc != string::npos ) { buffer.erase(loc); } loc = buffer.find( '\n' ); if( loc != string::npos ) { buffer.erase(loc); } if (!from_string(m_header[i], buffer, std::hex)) { debugWarning("Could not convert '%s' to uint32_t\n", buffer.c_str()); return false; } debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " Header %02d: %08X\n", i, m_header[i]); } m_Type = intToeDatType(m_header[0]); m_flash_offset_address = m_header[1]; m_length_quads = m_header[2]; m_CRC32 = m_header[3]; m_checksum = m_header[4]; m_version = m_header[5]; m_append_crc = m_header[6] != 0; m_footprint_quads = m_header[7]; debugOutput(DEBUG_LEVEL_VERBOSE, " header ok...\n"); debugOutput(DEBUG_LEVEL_VERBOSE, " Reading data...\n"); delete[] m_data; m_data = new uint32_t[m_length_quads]; if(m_data == NULL) { debugError("could not allocate memory for firmware\n"); return false; } for (uint32_t i=0; i < m_length_quads; i++) { std::string buffer; getline(fwfile, buffer); // get rid of the DOS-Style end of line string::size_type loc = buffer.find( '\r' ); if( loc != string::npos ) { buffer.erase(loc); } loc = buffer.find( '\n' ); if( loc != string::npos ) { buffer.erase(loc); } if (!from_string(m_data[i], buffer, std::hex)) { debugWarning("Could not convert '%s' to uint32_t\n", buffer.c_str()); return false; } debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " Data %02d: %08X\n", i, m_data[i]); } debugOutput(DEBUG_LEVEL_VERBOSE, " data ok...\n"); fwfile.close(); m_source = filename; m_valid = true; return true; } bool Firmware::loadFromMemory(uint32_t *data, uint32_t addr, uint32_t len) { m_valid = false; // mark it as invalid for now m_Type = eDT_Invalid; // set some values (FIXME) m_flash_offset_address = addr; m_length_quads = len; m_CRC32 = 0; m_checksum = 0; m_version = 0; m_append_crc = false; m_footprint_quads = 0; // delete any old data delete[] m_data; m_data = new uint32_t[len]; if(m_data == NULL) { debugError("could not allocate memory for firmware\n"); return false; } // copy data memcpy(m_data, data, len*sizeof(uint32_t)); return true; } uint32_t Firmware::getWriteDataLen() { uint32_t retval = 0; if((m_append_crc != 0) && (m_length_quads < m_footprint_quads)) { retval += m_footprint_quads; } else { retval += m_length_quads; } return retval; } bool Firmware::getWriteData(uint32_t *buff) { // copy the payload data memcpy(buff, m_data, m_length_quads*4); // if necessary, add crc/version if((m_append_crc != 0) && (m_length_quads < m_footprint_quads)) { debugOutput(DEBUG_LEVEL_VERBOSE, "appending CRC and version\n"); buff[m_footprint_quads - 1] = m_CRC32; buff[m_footprint_quads - 2] = m_version; } return true; } void Firmware::dumpData() { debugWarning("-- char dump --"); hexDump((unsigned char*)m_data, m_length_quads*4); /* debugWarning("-- quadlet dump --"); hexDumpQuadlets(m_data, m_length_quads);*/ } // the firmware loader helper class const char *Af2Dats[] = { "Fireworks3" }; const char *Af4Dats[] = { "Fireworks3" }; const char *Af8Dats[] = { "bootstrap", "audiofire8", "audiofire8_E", "FireworksARM" }; const char *Af12Dats[] = { "bootstrap", "audiofire12", "audiofire12_E", "FireworksARM" }; FirmwareUtil::FirmwareUtil(FireWorks::Device& p) : m_Parent(p) { struct dat_list datlists[4] = { { FW_VENDORID_ECHO, AUDIOFIRE2, 0x04010000, 1, Af2Dats }, { FW_VENDORID_ECHO, AUDIOFIRE4, 0x04010000, 1, Af4Dats }, { FW_VENDORID_ECHO, AUDIOFIRE8, 0x04010000, 4, Af8Dats }, { FW_VENDORID_ECHO, AUDIOFIRE12, 0x04010000, 4, Af12Dats } }; assert(sizeof(datlists) <= sizeof(m_datlists)); memset(&m_datlists, 0, sizeof(m_datlists)); memcpy(&m_datlists, &datlists, sizeof(datlists)); } FirmwareUtil::~FirmwareUtil() { } bool FirmwareUtil::isValidForDevice(Firmware f) { std::string src = f.getSourceString(); uint32_t vendor = m_Parent.getConfigRom().getNodeVendorId(); uint32_t model = m_Parent.getConfigRom().getModelId(); for (unsigned int i=0; i. * */ #ifndef FIREWORKS_FIRMWARE_H #define FIREWORKS_FIRMWARE_H #include "debugmodule/debugmodule.h" #include "efc/efc_cmd.h" #include "efc/efc_cmds_hardware.h" #include "efc/efc_cmds_flash.h" #include "IntelFlashMap.h" #include class ConfigRom; class Ieee1394Service; namespace FireWorks { #define ECHO_FIRMWARE_MAGIC "1651 1 0 0 0" #define ECHO_FIRMWARE_MAGIC_LENGTH_BYTES 14 // the number of quadlets in the file #define ECHO_FIRMWARE_HEADER_LENGTH_QUADLETS 64 // note that the dat files are not binary files but have the quadlets // as "0x0ABCDE12\n" lines #define ECHO_FIRMWARE_HEADER_LENGTH_BYTES ( 12 * ECHO_FIRMWARE_HEADER_LENGTH_QUADLETS ) #define ECHO_FIRMWARE_FILE_MAX_LENGTH_QUADLETS ((384 * 1024) / 4) #define ECHO_FIRMWARE_FILE_MAX_LENGTH_BYTES (ECHO_FIRMWARE_FILE_MAX_LENGTH_QUADLETS * 12 + ECHO_FIRMWARE_HEADER_LENGTH_BYTES) #define ECHO_FIRMWARE_NUM_BOXTYPES 4 class Firmware { public: enum eDatType { eDT_DspCode = 0, eDT_IceLynxCode = 1, eDT_Data = 2, eDT_FPGACode = 3, eDT_DeviceName = 4, eDT_Invalid = 0xFF, }; static const char *eDatTypeToString(const enum eDatType target); static const enum eDatType intToeDatType(int type); public: Firmware(); Firmware(const Firmware& f); virtual ~Firmware(); Firmware& operator=(const Firmware& f); virtual bool loadFile(std::string filename); virtual bool loadFromMemory(uint32_t *data, uint32_t addr, uint32_t len); virtual bool isValid() {return m_valid;}; virtual std::string getSourceString() {return m_source;}; /** * @brief compare two firmwares * compares only the address, length and data content, not the other fields * @param x firmware to be compared to 'this' * @return true if equal */ bool operator==(const Firmware& x); /** * @brief get base address of the firmware image * @return base address of the firmware image */ virtual uint32_t getAddress() {return m_flash_offset_address;}; /** * @brief get length of the firmware image (in quadlets) * @return length of the firmware image (in quadlets) */ virtual uint32_t getLength() {return m_length_quads;}; /** * @brief get length of data to be written to the device (in quadlets) * @return length (in quadlets) */ virtual uint32_t getWriteDataLen(); /** * @brief prepares data to be written to the device * @param buff buffer to copy data into. should be at least getWriteDataLen() quadlets. * @return true if successful */ virtual bool getWriteData(uint32_t *buff); virtual void show(); virtual void setVerboseLevel(int l) {setDebugLevel(l);}; void dumpData(); protected: // filename std::string m_source; // header data enum eDatType m_Type; uint32_t m_flash_offset_address; uint32_t m_length_quads; uint32_t m_CRC32; uint32_t m_checksum; uint32_t m_version; bool m_append_crc; // true to append uint32_t m_footprint_quads; std::string m_magic; uint32_t m_header[ECHO_FIRMWARE_HEADER_LENGTH_QUADLETS]; uint32_t *m_data; bool m_valid; private: DECLARE_DEBUG_MODULE; }; class FirmwareUtil { public: FirmwareUtil(FireWorks::Device& parent); virtual ~FirmwareUtil(); virtual void show(); virtual void setVerboseLevel(int l) {setDebugLevel(l);}; /** * @brief reads firmware block from device * @param start start address * @param length number of quadlets to read * @return Firmware structure containing the read data */ Firmware getFirmwareFromDevice(uint32_t start, uint32_t length); /** * @brief writes a firmware to the device * @param f firmware to write * @return true if successful */ bool writeFirmwareToDevice(Firmware f); /** * @brief erases the flash memory starting at addr * @param address * @param nb_quads * @return true if successful, false otherwise */ bool eraseBlocks(unsigned int address, unsigned int nb_quads); /** * @brief checks whether a firmware is valid for this device * @param f firmware to check * @return true if valid, false if not */ bool isValidForDevice(Firmware f); protected: FireWorks::Device& m_Parent; private: struct dat_list { uint32_t vendorid; uint32_t boxtype; uint32_t minversion; int count; const char **filenames; }; struct dat_list m_datlists[ECHO_FIRMWARE_NUM_BOXTYPES]; private: DECLARE_DEBUG_MODULE; }; } // namespace FireWorks #endif libffado-2.4.5/src/fireworks/fireworks_session_block.cpp0000644000175000001440000003572214206145246023133 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "fireworks_session_block.h" #include "fireworks_device.h" #include "libutil/ByteSwap.h" #include // These classes provide support for reading/writing session blocks on // echo fireworks based devices // does not support legacy (v1) sessions using namespace std; namespace FireWorks { const uint32_t ECHO_SESSION_CRC_TABLE[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; IMPL_DEBUG_MODULE( Session, Session, DEBUG_LEVEL_NORMAL ); Session::Session() { } Session::~Session() { } bool Session::loadFromDevice(Device &d) { size_t len = sizeof(SessionHeader) + sizeof(SubSession); size_t start = d.getSessionBase(); if (start == 0) { debugError("Invalid session base\n"); return false; } uint32_t data[len/4]; if(!d.readFlash(start, len/4, data)) { debugError("Flash read failed\n"); return false; } if(!loadFromMemory(data, len)) { debugError("Could not load session block from device memory dump\n"); return false; } return true; } bool Session::saveToDevice(Device &d) { size_t len = sizeof(SessionHeader) + sizeof(SubSession); size_t start = d.getSessionBase(); if (start == 0) { debugError("Invalid session base\n"); return false; } // update the CRC h.crc = calculateCRC(); uint32_t data[len/4]; if(!saveToMemory(data, len)) { debugError("Could not save session to memory block\n"); return false; } if (!d.lockFlash(true)) { debugError(" Could not lock flash\n"); return false; } if (!d.eraseFlashBlocks(start, len/4)) { debugError(" Could not erase memory\n"); return false; } if(!d.writeFlash(start, len/4, data)) { debugError("Writing to flash failed.\n"); return false; } if (!d.lockFlash(false)) { debugError(" Could not unlock flash\n"); return false; } return true; } bool Session::loadFromFile(std::string filename) { debugOutput(DEBUG_LEVEL_VERBOSE, "Loading session from file %s\n", filename.c_str()); fstream sessfile; debugOutput(DEBUG_LEVEL_VERBOSE, " Loading file...\n"); sessfile.open( filename.c_str(), ios::in | ios::ate | ios::binary); if ( !sessfile.is_open() ) { debugError("Could not open file.\n"); return false; } // get file size int size; size = (int)sessfile.tellg() - ECHO_SESSION_FILE_START_OFFSET; sessfile.seekg(ECHO_SESSION_FILE_START_OFFSET, ios_base::beg); debugOutput(DEBUG_LEVEL_VERBOSE, " Reading data, size = %d bytes, %d quads...\n", size, size/4); char data[size]; sessfile.read(data, size); sessfile.close(); if (sessfile.eof()) { debugError("EOF while reading file\n"); return false; } if(!loadFromMemory(data, size)) { debugError("Could not load session block from file\n"); return false; } return true; } bool Session::saveToFile(std::string filename) { debugOutput(DEBUG_LEVEL_VERBOSE, "Saving session to file %s\n", filename.c_str()); fstream sessfile; debugOutput(DEBUG_LEVEL_VERBOSE, " Loading file...\n"); sessfile.open( filename.c_str(), ios::out | ios::trunc | ios::binary); if ( !sessfile.is_open() ) { debugError("Could not open file.\n"); return false; } // FIXME: figure out what the file header means debugOutput(DEBUG_LEVEL_VERBOSE, " Writing file header...\n"); char header[ECHO_SESSION_FILE_START_OFFSET]; sessfile.write(header, ECHO_SESSION_FILE_START_OFFSET); debugOutput(DEBUG_LEVEL_VERBOSE, " Writing session data...\n"); int size = sizeof(SessionHeader) + sizeof(SubSession); char data[size]; if(!saveToMemory(data, size)) { debugError("Could not save session to memory block\n"); return false; } sessfile.write(data, size); sessfile.close(); return true; } bool Session::loadFromMemory(void *buff, size_t len) { if (len != sizeof(SessionHeader) + sizeof(SubSession)) { debugError("Invalid session length\n"); return false; } char *raw = (char *)buff; memcpy(&h, raw, sizeof(SessionHeader)); memcpy(&s, raw+sizeof(SessionHeader), sizeof(SubSession)); if (len != h.size_quads*4) { debugWarning("size not correct: got %zd, should be %d according to data\n", len, h.size_quads*4); } #if __BYTE_ORDER == __BIG_ENDIAN unsigned int i=0; uint32_t *data = (uint32_t *)(&s); for(; i < sizeof(SubSession)/4; i++) { *data = ByteSwap32(*data); data++; } #endif return true; } bool Session::saveToMemory(void *buff, size_t max_len) { if (max_len < sizeof(SessionHeader) + sizeof(SubSession)) { debugError("Max length too small\n"); return false; } char *raw = (char *)buff; memcpy(raw, &h, sizeof(SessionHeader)); memcpy(raw+sizeof(SessionHeader), &s, sizeof(SubSession)); #if __BYTE_ORDER == __BIG_ENDIAN unsigned int i=0; uint32_t *data = (uint32_t *)(raw+sizeof(SessionHeader)); for(; i < sizeof(SubSession)/4; i++) { *data = ByteSwap32(*data); data++; } #endif return true; } uint32_t Session::calculateCRC() { size_t len = sizeof(SessionHeader) + sizeof(SubSession); char data[len]; memcpy(data, &h, sizeof(SessionHeader)); memcpy(data+sizeof(SessionHeader), &s, sizeof(SubSession)); return calculateCRC(data, len); } uint32_t Session::calculateCRC(void *memblock, size_t max_len) { if (max_len < sizeof(SessionHeader) + sizeof(SubSession)) { debugError("block too small\n"); return 0; } // based upon CRC code provided by ECHO byte_t *data = (byte_t*)memblock; uint32_t b; int bytecount; uint32_t remainder = ECHO_SESSION_CRC_INITIAL_REMAINDER; // skip the first two fields (length and CRC) data += ECHO_SESSION_CRC_START_OFFSET_BYTES; // total bytecount bytecount = sizeof(SessionHeader) + sizeof(SubSession) - ECHO_SESSION_CRC_START_OFFSET_BYTES; #if __BYTE_ORDER == __BIG_ENDIAN // // This is a big-endian machine; calculate the CRC for the first part // of the data structure using big-endian mode // int bebytes, offset; offset = 3; bebytes = bytecount - sizeof(SubSession); while (bebytes > 0) { b = data[offset]; b = ( remainder ^ b) & 0xFF; remainder = ECHO_SESSION_CRC_TABLE[b] ^ ( remainder >> 8 ); offset--; if (offset < 0) { offset = 3; bebytes -= sizeof(uint32_t); data += sizeof(uint32_t); } } #endif // // Do the little-endian part of the session // // Compute the CRC a byte at time, starting with the LSB of each quad // while (bytecount > 0) { b = ( remainder ^ *data ) & 0xFF; remainder = ECHO_SESSION_CRC_TABLE[b] ^ ( remainder >> 8 ); data++; bytecount--; } // // The final remainder is the CRC result. // return (remainder ^ ECHO_SESSION_CRC_FINAL_XOR_VALUE); } void Session::show() { debugOutput(DEBUG_LEVEL_NORMAL, "Session Block\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Size.............: %u (%08X)\n", h.size_quads, h.size_quads); debugOutput(DEBUG_LEVEL_NORMAL, " CRC read.........: %12u (%08X)\n", h.crc, h.crc); uint32_t crc = calculateCRC(); debugOutput(DEBUG_LEVEL_NORMAL, " CRC calculated...: %12u (%08X)\n", crc, crc); debugOutput(DEBUG_LEVEL_NORMAL, " Version..........: %u (%08X)\n", h.version, h.version); debugOutput(DEBUG_LEVEL_NORMAL, " Flags............: %u (%08X)\n", h.flags, h.flags); debugOutput(DEBUG_LEVEL_NORMAL, " Mirror Channel...: %d (%08X)\n", h.mirror_channel, h.mirror_channel); debugOutput(DEBUG_LEVEL_NORMAL, " Digital Mode.....: %d (%08X)\n", h.digital_mode, h.digital_mode); debugOutput(DEBUG_LEVEL_NORMAL, " Clock............: %d (%08X)\n", h.clock, h.clock); debugOutput(DEBUG_LEVEL_NORMAL, " Rate.............: %d (%08X)\n", h.rate, h.rate); debugOutput(DEBUG_LEVEL_NORMAL, " Gains:\n"); for(unsigned int in = 0; in < ECHO_SESSION_MAX_PHY_AUDIO_IN; in++) { debugOutput(DEBUG_LEVEL_NORMAL, " MON %02u: ", in); for(unsigned int out = 0; out < ECHO_SESSION_MAX_PHY_AUDIO_OUT; out++) { debugOutputShort(DEBUG_LEVEL_NORMAL, "%08X ", h.monitorgains[in][out]); flushDebugOutput(); } debugOutputShort(DEBUG_LEVEL_NORMAL, "\n"); } debugOutput(DEBUG_LEVEL_NORMAL, " PGAIN : "); for(unsigned int out = 0; out < ECHO_SESSION_MAX_PHY_AUDIO_OUT; out++) { debugOutputShort(DEBUG_LEVEL_NORMAL, "%08X ", h.playbackgains[out]); flushDebugOutput(); } debugOutputShort(DEBUG_LEVEL_NORMAL, "\n"); debugOutput(DEBUG_LEVEL_NORMAL, " OGAIN : "); for(unsigned int out = 0; out < ECHO_SESSION_MAX_PHY_AUDIO_OUT; out++) { debugOutputShort(DEBUG_LEVEL_NORMAL, "%08X ", h.outputgains[out]); flushDebugOutput(); } debugOutputShort(DEBUG_LEVEL_NORMAL, "\n"); debugOutput(DEBUG_LEVEL_NORMAL, " Input settings:\n"); for(unsigned int in = 0; in < ECHO_SESSION_MAX_PHY_AUDIO_IN; in++) { debugOutput(DEBUG_LEVEL_NORMAL, " IN %02u: shift: %02X, pad: %02X, label: %s\n", in, s.inputs[in].shift, s.inputs[in].pad, s.inputs[in].label); flushDebugOutput(); } debugOutput(DEBUG_LEVEL_NORMAL, " Pans:\n"); for(unsigned int in = 0; in < ECHO_SESSION_MAX_PHY_AUDIO_IN; in++) { debugOutput(DEBUG_LEVEL_NORMAL, " IN %02u: ", in); for(unsigned int out = 0; out < ECHO_SESSION_MAX_PHY_AUDIO_OUT; out++) { debugOutputShort(DEBUG_LEVEL_NORMAL, "%03u ", s.monitorpans[in][out]); flushDebugOutput(); } debugOutputShort(DEBUG_LEVEL_NORMAL, "\n"); } debugOutput(DEBUG_LEVEL_NORMAL, " Flags:\n"); for(unsigned int in = 0; in < ECHO_SESSION_MAX_PHY_AUDIO_IN; in++) { debugOutput(DEBUG_LEVEL_NORMAL, " IN %02u: ", in); for(unsigned int out = 0; out < ECHO_SESSION_MAX_PHY_AUDIO_OUT; out++) { debugOutputShort(DEBUG_LEVEL_NORMAL, "%02X ", s.monitorflags[in][out]); flushDebugOutput(); } debugOutputShort(DEBUG_LEVEL_NORMAL, "\n"); } debugOutput(DEBUG_LEVEL_NORMAL, " Playback settings:\n"); for(unsigned int out = 0; out < ECHO_SESSION_MAX_PHY_AUDIO_OUT; out++) { debugOutput(DEBUG_LEVEL_NORMAL, " PBK %02u: mute: %02X, solo: %02X, label: %s\n", out, s.playbacks[out].mute, s.playbacks[out].solo, s.playbacks[out].label); } debugOutput(DEBUG_LEVEL_NORMAL, " Output settings:\n"); for(unsigned int out = 0; out < ECHO_SESSION_MAX_PHY_AUDIO_OUT; out++) { debugOutput(DEBUG_LEVEL_NORMAL, " OUT %02u: mute: %02X, shift: %02X, label: %s\n", out, s.outputs[out].mute, s.outputs[out].shift, s.outputs[out].label); flushDebugOutput(); } } void Session::dumpData() { } } // FireWorks libffado-2.4.5/src/fireworks/fireworks_session_block.h0000644000175000001440000001016514206145246022572 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIREWORKS_SESSION_BLOCK_H #define FIREWORKS_SESSION_BLOCK_H #include "debugmodule/debugmodule.h" #include #include "efc/efc_cmds_ioconfig.h" namespace FireWorks { #define ECHO_SESSION_FILE_START_OFFSET 0x0040 // we only support version 2 #define SESSION_VERSION 0x00000200 #define ECHO_SESSION_MAX_PHY_AUDIO_IN 40 #define ECHO_SESSION_MAX_PHY_AUDIO_OUT 40 #define ECHO_SESSION_MAX_1394_REC_CHAN 40 #define ECHO_SESSION_MAX_1394_PLAY_CHAN 40 #define ECHO_SESSION_MAX_LABEL_SIZE 22 #define ECHO_SESSION_MUTE_BIT 1 #define ECHO_SESSION_SOLO_BIT 2 #define ECHO_SESSION_PAD_BIT 4 // Phantom power bit is set in the "flags" #define ECHO_SESSION_FLAG_PHANTOM_POWER_BIT 31 #define ECHO_SESSION_FLAG_PHANTOM_POWER (1 << ECHO_SESSION_FLAG_PHANTOM_POWER_BIT) // CRC codes #define ECHO_SESSION_CRC_START_OFFSET_BYTES ( sizeof(uint32_t) * 2 ) #define ECHO_SESSION_CRC_START_OFFSET_QUADS ( ECHO_SESSION_CRC_START_OFFSET_BYTES/sizeof(uint32_t) ) #define ECHO_SESSION_CRC_INITIAL_REMAINDER 0xFFFFFFFF #define ECHO_SESSION_CRC_FINAL_XOR_VALUE 0xFFFFFFFF class Device; class Session { public: typedef struct { byte_t shift; byte_t pad; char label[ECHO_SESSION_MAX_LABEL_SIZE]; } InputSettings; typedef struct { byte_t mute; byte_t solo; char label[ECHO_SESSION_MAX_LABEL_SIZE]; } PlaybackSettings; typedef struct { byte_t mute; byte_t shift; char label[ECHO_SESSION_MAX_LABEL_SIZE]; } OutputSettings; typedef struct { InputSettings inputs[ECHO_SESSION_MAX_PHY_AUDIO_IN]; byte_t monitorpans[ECHO_SESSION_MAX_PHY_AUDIO_IN][ECHO_SESSION_MAX_PHY_AUDIO_OUT]; byte_t monitorflags[ECHO_SESSION_MAX_PHY_AUDIO_IN][ECHO_SESSION_MAX_PHY_AUDIO_OUT]; PlaybackSettings playbacks[ECHO_SESSION_MAX_PHY_AUDIO_OUT]; OutputSettings outputs[ECHO_SESSION_MAX_PHY_AUDIO_OUT]; } SubSession; typedef struct { uint32_t size_quads; uint32_t crc; uint32_t version; uint32_t flags; int32_t mirror_channel; int32_t digital_mode; int32_t clock; int32_t rate; uint32_t monitorgains[ECHO_SESSION_MAX_PHY_AUDIO_IN][ECHO_SESSION_MAX_PHY_AUDIO_OUT]; uint32_t playbackgains[ECHO_SESSION_MAX_PHY_AUDIO_OUT]; uint32_t outputgains[ECHO_SESSION_MAX_PHY_AUDIO_OUT]; IsoChannelMap ChannelMap2X; IsoChannelMap ChannelMap4X; } SessionHeader; public: Session(); virtual ~Session(); bool loadFromDevice(Device &); bool saveToDevice(Device &); bool loadFromFile(std::string name); bool saveToFile(std::string name); bool loadFromMemory(void *buff, size_t len); bool saveToMemory(void *buff, size_t max_len); virtual void setVerboseLevel(int l) {setDebugLevel(l);}; void dumpData(); void show(); uint32_t calculateCRC(); uint32_t calculateCRC(void *memblock, size_t max_len); SessionHeader h; SubSession s; private: DECLARE_DEBUG_MODULE; }; } // FireWorks #endif //FIREWORKS_SESSION_BLOCK_H libffado-2.4.5/src/fireworks/IntelFlashMap.h0000644000175000001440000000412311003642125020314 0ustar jwoitheusers// Copyright ECHO AUDIO // // defines for Fireworks flash // Using bottom boot Intel TE28F160C3 flash part // #ifndef _INTEL_BOTTOM_BOOT_MAP_H_ #define _INTEL_BOTTOM_BOOT_MAP_H_ //----------------------------------------------------------------------------- // // Defines for Intel flash part // //----------------------------------------------------------------------------- #define PROGRAMBLOCK_SIZE_WORD16 0x1000 // 16-bit words #define MAINBLOCK_SIZE_WORD16 0x8000 // 16-bit words #define PROGRAMBLOCK_SIZE_BYTES (PROGRAMBLOCK_SIZE_WORD16 * 2) #define MAINBLOCK_SIZE_BYTES (MAINBLOCK_SIZE_WORD16 * 2) #define PROGRAMBLOCK_SIZE_QUADS (PROGRAMBLOCK_SIZE_WORD16 / 2) #define MAINBLOCK_SIZE_QUADS (MAINBLOCK_SIZE_WORD16 / 2) #define MAINBLOCKS_BASE_OFFSET_BYTES 0x10000 #define FLASH_SIZE_BYTES 0x200000 // 2 MB #define FLASH_SIZE_QUADS (FLASH_SIZE_BYTES / 4) //----------------------------------------------------------------------------- // // memory map // // Small blocks (8 kbytes each) from 0 - 0xffff // Large blocks (32 kbytes each) from 0x010000 - 0x1fffff // //----------------------------------------------------------------------------- // // Fireworks 2.1 // #define DSP_EMERGENCY_IMAGE_OFFSET_BYTES_FW21 0x00140000 #define ARM_IMAGE_OFFSET_BYTES_FW21 0x00100000 #define DSP_IMAGE_OFFSET_BYTES_FW21 0x000C0000 #define FPGA_IMAGE_OFFSET_BYTES_FW21 0x00080000 #define BOOT_IMAGE_OFFSET_BYTES_FW21 0x00000000 #define SESSION_OFFSET_BYTES_FW21 0x00008000 #define ARM_IMAGE_OFFSET_QUADS_FW21 (ARM_IMAGE_OFFSET_BYTES_FW21/4) #define DSP_IMAGE_OFFSET_QUADS_FW21 (DSP_IMAGE_OFFSET_BYTES_FW21/4) #define BOOT_IMAGE_OFFSET_QUADS_FW21 (BOOT_IMAGE_OFFSET_BYTES_FW21/4) // // Fireworks 3 // #define FPGA_IMAGE_OFFSET_BYTES_FW3 0x00000000 #define ARM_IMAGE_OFFSET_BYTES_FW3 0x00100000 #define NAME_BLOCK_OFFSET_BYTES_FW3 0x001E0000 #define SESSION_OFFSET_BYTES_FW3 0x001F0000 // // Fireworks HDMI // #define FWHDMI_CLOCK_RATIOS_OFFSET_BYTES 0x00006000 #endif // _INTEL_BOTTOM_BOOT_MAP_H_ libffado-2.4.5/src/genericavc/0000755000175000001440000000000014206145612015556 5ustar jwoitheuserslibffado-2.4.5/src/genericavc/avc_avdevice.cpp0000644000175000001440000007464114206145246020720 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "devicemanager.h" #include "genericavc/avc_avdevice.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libavc/avc_definitions.h" #include "libavc/general/avc_plug_info.h" #include "libavc/general/avc_extended_plug_info.h" #include "libavc/general/avc_subunit_info.h" #include "debugmodule/debugmodule.h" #include #include #include #include "libutil/ByteSwap.h" #include #include #include #include #include "stanton/scs.h" namespace GenericAVC { IMPL_DEBUG_MODULE( Device, Device, DEBUG_LEVEL_NORMAL ); Device::Device( DeviceManager& d, ffado_smartptr( configRom )) : FFADODevice( d, configRom ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created GenericAVC::Device (NodeID %d)\n", getConfigRom().getNodeId() ); addOption(Util::OptionContainer::Option("snoopMode",false)); } Device::~Device() { for ( StreamProcessorVectorIterator it = m_receiveProcessors.begin(); it != m_receiveProcessors.end(); ++it ) { delete *it; } for ( StreamProcessorVectorIterator it = m_transmitProcessors.begin(); it != m_transmitProcessors.end(); ++it ) { delete *it; } } bool Device::probe( Util::Configuration& c, ConfigRom& configRom, bool generic ) { if(generic) { // check if we have a music subunit AVC::SubUnitInfoCmd subUnitInfoCmd( configRom.get1394Service() ); subUnitInfoCmd.setCommandType( AVC::AVCCommand::eCT_Status ); subUnitInfoCmd.m_page = 0; subUnitInfoCmd.setNodeId( configRom.getNodeId() ); subUnitInfoCmd.setVerbose( configRom.getVerboseLevel() ); if ( !subUnitInfoCmd.fire() ) { debugError( "Subunit info command failed\n" ); return false; } for ( int i = 0; i < subUnitInfoCmd.getNrOfValidEntries(); ++i ) { AVC::subunit_type_t subunit_type = subUnitInfoCmd.m_table[i].m_subunit_type; if (subunit_type == AVC::eST_Music) return true; } return false; } else { // check if device is in supported devices list unsigned int vendorId = configRom.getNodeVendorId(); unsigned int modelId = configRom.getModelId(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); return c.isValid(vme) && vme.driver == Util::Configuration::eD_GenericAVC; return false; } } FFADODevice * Device::createDevice(DeviceManager& d, ffado_smartptr( configRom )) { unsigned int vendorId = configRom->getNodeVendorId(); unsigned int modelId = configRom->getModelId(); switch (vendorId) { case FW_VENDORID_STANTON: if (modelId == 0x00001000 ) { return new Stanton::ScsDevice(d, configRom); } default: return new GenericAVC::Device(d, configRom); } return NULL; } bool Device::discover() { Util::MutexLockHelper lock(m_DeviceMutex); unsigned int vendorId = getConfigRom().getNodeVendorId(); unsigned int modelId = getConfigRom().getModelId(); Util::Configuration &c = getDeviceManager().getConfiguration(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); if (c.isValid(vme) && vme.driver == Util::Configuration::eD_GenericAVC) { debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", vme.vendor_name.c_str(), vme.model_name.c_str()); } else { debugWarning("Using generic AV/C support for unsupported device '%s %s'\n", getConfigRom().getVendorName().c_str(), getConfigRom().getModelName().c_str()); } return discoverGeneric(); } bool Device::discoverGeneric() { if ( !Unit::discover() ) { debugError( "Could not discover unit\n" ); return false; } if((getAudioSubunit( 0 ) == NULL)) { debugError( "Unit doesn't have an Audio subunit.\n"); return false; } if((getMusicSubunit( 0 ) == NULL)) { debugError( "Unit doesn't have a Music subunit.\n"); return false; } return true; } void Device::setVerboseLevel(int l) { Util::MutexLockHelper lock(m_DeviceMutex); setDebugLevel(l); m_pPlugManager->setVerboseLevel(l); FFADODevice::setVerboseLevel(l); AVC::Unit::setVerboseLevel(l); debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); } enum FFADODevice::eStreamingState Device::getStreamingState() { // check the IEC plug control registers to see if the device is streaming // a bit of a hack, but will do until we come up with something better struct iec61883_oPCR oPCR0; struct iec61883_iPCR iPCR0; quadlet_t *oPCR0q = (quadlet_t *)&oPCR0; quadlet_t *iPCR0q = (quadlet_t *)&iPCR0; if(!get1394Service().read(getNodeId() | 0xFFC0, CSR_REGISTER_BASE + CSR_O_PCR_0, 1, oPCR0q)) { debugWarning("Could not read oPCR0 register\n"); } if(!get1394Service().read(getNodeId() | 0xFFC0, CSR_REGISTER_BASE + CSR_I_PCR_0, 1, iPCR0q)) { debugWarning("Could not read iPCR0 register\n"); } *oPCR0q = CondSwapFromBus32(*oPCR0q); *iPCR0q = CondSwapFromBus32(*iPCR0q); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "iPCR0: %08X, oPCR0: %08X\n", *iPCR0q, *oPCR0q); if(iPCR0.n_p2p_connections > 0 && oPCR0.n_p2p_connections > 0) { return eSS_Both; } else if (iPCR0.n_p2p_connections > 0) { return eSS_Receiving; } else if (oPCR0.n_p2p_connections > 0) { return eSS_Sending; } else { return eSS_Idle; } } int Device::getSamplingFrequency( ) { AVC::Plug* inputPlug = getPlugById( m_pcrPlugs, AVC::Plug::eAPD_Input, 0 ); if ( !inputPlug ) { debugError( "setSampleRate: Could not retrieve iso input plug 0\n" ); return false; } AVC::Plug* outputPlug = getPlugById( m_pcrPlugs, AVC::Plug::eAPD_Output, 0 ); if ( !outputPlug ) { debugError( "setSampleRate: Could not retrieve iso output plug 0\n" ); return false; } int samplerate_playback=inputPlug->getSampleRate(); int samplerate_capture=outputPlug->getSampleRate(); if (samplerate_playback != samplerate_capture) { debugWarning("Samplerates for capture and playback differ!\n"); } return samplerate_capture; } bool Device::setSamplingFrequency( int s ) { Util::MutexLockHelper lock(m_DeviceMutex); bool snoopMode=false; if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } if(snoopMode) { int current_sr=getSamplingFrequency(); if (current_sr != s ) { debugError("In snoop mode it is impossible to set the sample rate.\n"); debugError("Please start the client with the correct setting.\n"); return false; } return true; } else { AVC::Plug* plug = getPlugById( m_pcrPlugs, AVC::Plug::eAPD_Input, 0 ); if ( !plug ) { debugError( "setSampleRate: Could not retrieve iso input plug 0\n" ); return false; } if ( !plug->setSampleRate( s ) ) { debugError( "setSampleRate: Setting sample rate failed\n" ); return false; } plug = getPlugById( m_pcrPlugs, AVC::Plug::eAPD_Output, 0 ); if ( !plug ) { debugError( "setSampleRate: Could not retrieve iso output plug 0\n" ); return false; } if ( !plug->setSampleRate( s ) ) { debugError( "setSampleRate: Setting sample rate failed\n" ); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "setSampleRate: Set sample rate to %d\n", s ); return true; } // not executable return false; } bool Device::supportsSamplingFrequency( int s ) { Util::MutexLockHelper lock(m_DeviceMutex); AVC::Plug* plug = getPlugById( m_pcrPlugs, AVC::Plug::eAPD_Input, 0 ); if ( !plug ) { debugError( "Could not retrieve iso input plug 0\n" ); return false; } if ( !plug->supportsSampleRate( s ) ) { debugError( "sample rate not supported by input plug\n" ); return false; } plug = getPlugById( m_pcrPlugs, AVC::Plug::eAPD_Output, 0 ); if ( !plug ) { debugError( "Could not retrieve iso output plug 0\n" ); return false; } if ( !plug->supportsSampleRate( s ) ) { debugError( "sample rate not supported by output plug\n" ); return false; } return true; } #define GENERICAVC_CHECK_AND_ADD_SR(v, x) \ { if(supportsSamplingFrequency(x)) \ v.push_back(x); } std::vector Device::getSupportedSamplingFrequencies() { if (m_supported_frequencies_cache.size() == 0) { GENERICAVC_CHECK_AND_ADD_SR(m_supported_frequencies_cache, 22050); GENERICAVC_CHECK_AND_ADD_SR(m_supported_frequencies_cache, 24000); GENERICAVC_CHECK_AND_ADD_SR(m_supported_frequencies_cache, 32000); GENERICAVC_CHECK_AND_ADD_SR(m_supported_frequencies_cache, 44100); GENERICAVC_CHECK_AND_ADD_SR(m_supported_frequencies_cache, 48000); GENERICAVC_CHECK_AND_ADD_SR(m_supported_frequencies_cache, 88200); GENERICAVC_CHECK_AND_ADD_SR(m_supported_frequencies_cache, 96000); GENERICAVC_CHECK_AND_ADD_SR(m_supported_frequencies_cache, 176400); GENERICAVC_CHECK_AND_ADD_SR(m_supported_frequencies_cache, 192000); } return m_supported_frequencies_cache; } FFADODevice::ClockSourceVector Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; Util::MutexLockHelper lock(m_DeviceMutex); AVC::PlugVector syncMSUInputPlugs = m_pPlugManager->getPlugsByType( AVC::eST_Music, 0, 0xff, 0xff, AVC::Plug::eAPA_SubunitPlug, AVC::Plug::eAPD_Input, AVC::Plug::eAPT_Sync ); if ( !syncMSUInputPlugs.size() ) { // there exist devices which do not have a sync plug // or their av/c model is broken. return r; } for ( SyncInfoVector::const_iterator it = getSyncInfos().begin(); it != getSyncInfos().end(); ++it ) { const SyncInfo si=*it; ClockSource s=syncInfoToClockSource(*it); r.push_back(s); } return r; } bool Device::setActiveClockSource(ClockSource s) { AVC::Plug *src = m_pPlugManager->getPlug( s.id ); if (!src) { debugError("Could not find plug with id %d\n", s.id); return false; } Util::MutexLockHelper lock(m_DeviceMutex); for ( SyncInfoVector::const_iterator it = getSyncInfos().begin(); it != getSyncInfos().end(); ++it ) { const SyncInfo si=*it; if (si.m_source==src) { return setActiveSync(si); } } return false; } FFADODevice::ClockSource Device::getActiveClockSource() { const SyncInfo* si=getActiveSyncInfo(); if ( !si ) { debugError( "Could not retrieve active sync information\n" ); ClockSource s; s.type=eCT_Invalid; return s; } debugOutput(DEBUG_LEVEL_VERBOSE, "Active Sync mode: %s\n", si->m_description.c_str() ); return syncInfoToClockSource(*si); } FFADODevice::ClockSource Device::syncInfoToClockSource(const SyncInfo& si) { ClockSource s; // the description is easy // it can be that we overwrite it later s.description=si.m_description; // FIXME: always valid at the moment s.valid=true; assert(si.m_source); s.id=si.m_source->getGlobalId(); // now figure out what type this is switch(si.m_source->getPlugType()) { case AVC::Plug::eAPT_IsoStream: s.type=eCT_SytMatch; break; case AVC::Plug::eAPT_Sync: if(si.m_source->getPlugAddressType() == AVC::Plug::eAPA_PCR) { s.type=eCT_SytStream; // this is logical } else if(si.m_source->getPlugAddressType() == AVC::Plug::eAPA_SubunitPlug) { s.type=eCT_Internal; // this assumes some stuff } else if(si.m_source->getPlugAddressType() == AVC::Plug::eAPA_ExternalPlug) { std::string plugname=si.m_source->getName(); s.description=plugname; // this is basically due to Focusrites interpretation if(plugname.find( "SPDIF", 0 ) != string::npos) { s.type=eCT_SPDIF; // this assumes the name will tell us } else { s.type=eCT_WordClock; // this assumes a whole lot more } } else { s.type=eCT_Invalid; } break; case AVC::Plug::eAPT_Digital: if(si.m_source->getPlugAddressType() == AVC::Plug::eAPA_ExternalPlug) { std::string plugname=si.m_source->getName(); s.description=plugname; // this is basically due to Focusrites interpretation if(plugname.find( "ADAT", 0 ) != string::npos) { s.type=eCT_ADAT; // this assumes the name will tell us } else if(plugname.find( "SPDIF", 0 ) != string::npos) { s.type=eCT_SPDIF; // this assumes the name will tell us } else { s.type=eCT_WordClock; // this assumes a whole lot more } } else { s.type=eCT_Invalid; } break; default: s.type=eCT_Invalid; break; } // is it active? const SyncInfo* active=getActiveSyncInfo(); if (active) { if ((active->m_source == si.m_source) && (active->m_destination == si.m_destination)) s.active=true; else s.active=false; } else s.active=false; return s; } bool Device::lock() { bool snoopMode=false; Util::MutexLockHelper lock(m_DeviceMutex); if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } if (snoopMode) { // don't lock } else { // return Unit::reserve(4); } return true; } bool Device::unlock() { bool snoopMode=false; Util::MutexLockHelper lock(m_DeviceMutex); if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } if (snoopMode) { // don't unlock } else { // return Unit::reserve(0); } return true; } void Device::showDevice() { FFADODevice::showDevice(); AVC::Unit::show(); flushDebugOutput(); } bool Device::prepare() { bool snoopMode = false; Util::MutexLockHelper lock(m_DeviceMutex); if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } /////////// // get plugs AVC::Plug* inputPlug = getPlugById( m_pcrPlugs, AVC::Plug::eAPD_Input, 0 ); if ( !inputPlug ) { debugError( "setSampleRate: Could not retrieve iso input plug 0\n" ); return false; } AVC::Plug* outputPlug = getPlugById( m_pcrPlugs, AVC::Plug::eAPD_Output, 0 ); if ( !outputPlug ) { debugError( "setSampleRate: Could not retrieve iso output plug 0\n" ); return false; } // get the device specific and/or global SP configuration Util::Configuration &config = getDeviceManager().getConfiguration(); // base value is the config.h value float recv_sp_dll_bw = STREAMPROCESSOR_DLL_BW_HZ; float xmit_sp_dll_bw = STREAMPROCESSOR_DLL_BW_HZ; int xmit_max_cycles_early_transmit = AMDTP_MAX_CYCLES_TO_TRANSMIT_EARLY; int xmit_transfer_delay = AMDTP_TRANSMIT_TRANSFER_DELAY; int xmit_min_cycles_before_presentation = AMDTP_MIN_CYCLES_BEFORE_PRESENTATION; // we can override that globally config.getValueForSetting("streaming.common.recv_sp_dll_bw", recv_sp_dll_bw); config.getValueForSetting("streaming.common.xmit_sp_dll_bw", xmit_sp_dll_bw); config.getValueForSetting("streaming.amdtp.xmit_max_cycles_early_transmit", xmit_max_cycles_early_transmit); config.getValueForSetting("streaming.amdtp.xmit_transfer_delay", xmit_transfer_delay); config.getValueForSetting("streaming.amdtp.xmit_min_cycles_before_presentation", xmit_min_cycles_before_presentation); // or override in the device section uint32_t vendorid = getConfigRom().getNodeVendorId(); uint32_t modelid = getConfigRom().getModelId(); config.getValueForDeviceSetting(vendorid, modelid, "recv_sp_dll_bw", recv_sp_dll_bw); config.getValueForDeviceSetting(vendorid, modelid, "xmit_sp_dll_bw", xmit_sp_dll_bw); config.getValueForDeviceSetting(vendorid, modelid, "xmit_max_cycles_early_transmit", xmit_max_cycles_early_transmit); config.getValueForDeviceSetting(vendorid, modelid, "xmit_transfer_delay", xmit_transfer_delay); config.getValueForDeviceSetting(vendorid, modelid, "xmit_min_cycles_before_presentation", xmit_min_cycles_before_presentation); // initialize the SP's debugOutput( DEBUG_LEVEL_VERBOSE, "Initializing receive processor...\n"); // create & add streamprocessors Streaming::StreamProcessor *p; if ( outputPlug->getNrOfChannels() == 0 ) { debugError("Receive plug has no channels\n"); return false; } p = new Streaming::AmdtpReceiveStreamProcessor(*this, outputPlug->getNrOfChannels()); if(!p->init()) { debugFatal("Could not initialize receive processor!\n"); delete p; return false; } if (!addPlugToProcessor(*outputPlug, p, Streaming::Port::E_Capture)) { debugFatal("Could not add plug to processor!\n"); delete p; return false; } if(!p->setDllBandwidth(recv_sp_dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete p; return false; } m_receiveProcessors.push_back(p); // do the transmit processor debugOutput( DEBUG_LEVEL_VERBOSE, "Initializing transmit processor%s...\n", (snoopMode?" in snoop mode":"")); if (snoopMode) { // we are snooping, so this is receive too. p=new Streaming::AmdtpReceiveStreamProcessor(*this, inputPlug->getNrOfChannels()); } else { Streaming::AmdtpTransmitStreamProcessor *t; t = new Streaming::AmdtpTransmitStreamProcessor(*this, inputPlug->getNrOfChannels()); #if AMDTP_ALLOW_PAYLOAD_IN_NODATA_XMIT // FIXME: it seems that some BeBoB devices can't handle NO-DATA without payload // FIXME: make this a config value too t->sendPayloadForNoDataPackets(true); #endif // transmit control parameters t->setMaxCyclesToTransmitEarly(xmit_max_cycles_early_transmit); t->setTransferDelay(xmit_transfer_delay); t->setMinCyclesBeforePresentation(xmit_min_cycles_before_presentation); p=t; } if(!p->init()) { debugFatal("Could not initialize transmit processor %s!\n", (snoopMode?" in snoop mode":"")); delete p; return false; } if (snoopMode) { if (!addPlugToProcessor(*inputPlug, p, Streaming::Port::E_Capture)) { debugFatal("Could not add plug to processor!\n"); return false; } if(!p->setDllBandwidth(recv_sp_dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete p; return false; } } else { if (!addPlugToProcessor(*inputPlug, p, Streaming::Port::E_Playback)) { debugFatal("Could not add plug to processor!\n"); return false; } if(!p->setDllBandwidth(xmit_sp_dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete p; return false; } } // we put this SP into the transmit SP vector, // no matter if we are in snoop mode or not // this allows us to find out what direction // a certain stream should have. m_transmitProcessors.push_back(p); return true; } bool Device::addPlugToProcessor( AVC::Plug& plug, Streaming::StreamProcessor *processor, Streaming::AmdtpAudioPort::E_Direction direction) { std::string id=std::string("dev?"); if(!getOption("id", id)) { debugWarning("Could not retrieve id parameter, defauling to 'dev?'\n"); } AVC::Plug::ClusterInfoVector& clusterInfos = plug.getClusterInfos(); for ( AVC::Plug::ClusterInfoVector::const_iterator it = clusterInfos.begin(); it != clusterInfos.end(); ++it ) { const AVC::Plug::ClusterInfo* clusterInfo = &( *it ); AVC::Plug::ChannelInfoVector channelInfos = clusterInfo->m_channelInfos; for ( AVC::Plug::ChannelInfoVector::const_iterator it = channelInfos.begin(); it != channelInfos.end(); ++it ) { const AVC::Plug::ChannelInfo* channelInfo = &( *it ); std::ostringstream portname; portname << id << "_" << channelInfo->m_name; Streaming::Port *p=NULL; switch(clusterInfo->m_portType) { case AVC::ExtendedPlugInfoClusterInfoSpecificData::ePT_Speaker: case AVC::ExtendedPlugInfoClusterInfoSpecificData::ePT_Headphone: case AVC::ExtendedPlugInfoClusterInfoSpecificData::ePT_Microphone: case AVC::ExtendedPlugInfoClusterInfoSpecificData::ePT_Line: case AVC::ExtendedPlugInfoClusterInfoSpecificData::ePT_Analog: debugOutput(DEBUG_LEVEL_VERBOSE, " Adding audio channel %s (pos=0x%02X, loc=0x%02X)\n", channelInfo->m_name.c_str(), channelInfo->m_streamPosition, channelInfo->m_location); p=new Streaming::AmdtpAudioPort( *processor, portname.str(), direction, channelInfo->m_streamPosition, channelInfo->m_location, Streaming::AmdtpPortInfo::E_MBLA ); break; case AVC::ExtendedPlugInfoClusterInfoSpecificData::ePT_MIDI: debugOutput(DEBUG_LEVEL_VERBOSE, " Adding MIDI channel '%s' (pos=0x%02X, loc=0x%02X)\n", portname.str().c_str() /*channelInfo->m_name.c_str()*/, channelInfo->m_streamPosition, processor->getPortCount(Streaming::Port::E_Midi)); p=new Streaming::AmdtpMidiPort( *processor, portname.str(), direction, channelInfo->m_streamPosition, // Workaround for out-of-spec hardware // should be: // channelInfo->m_location, // but now we renumber the midi channels' location as they // are discovered processor->getPortCount(Streaming::Port::E_Midi), Streaming::AmdtpPortInfo::E_Midi ); break; case AVC::ExtendedPlugInfoClusterInfoSpecificData::ePT_SPDIF: case AVC::ExtendedPlugInfoClusterInfoSpecificData::ePT_ADAT: case AVC::ExtendedPlugInfoClusterInfoSpecificData::ePT_TDIF: case AVC::ExtendedPlugInfoClusterInfoSpecificData::ePT_MADI: case AVC::ExtendedPlugInfoClusterInfoSpecificData::ePT_Digital: debugOutput(DEBUG_LEVEL_VERBOSE, " Adding digital audio channel %s (pos=0x%02X, loc=0x%02X)\n", channelInfo->m_name.c_str(), channelInfo->m_streamPosition, channelInfo->m_location); p=new Streaming::AmdtpAudioPort( *processor, portname.str(), direction, channelInfo->m_streamPosition, channelInfo->m_location, Streaming::AmdtpPortInfo::E_MBLA ); break; case AVC::ExtendedPlugInfoClusterInfoSpecificData::ePT_NoType: default: // unsupported break; } if (!p) { debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n", channelInfo->m_name.c_str()); } } } return true; } int Device::getStreamCount() { int retval; Util::MutexLockHelper lock(m_DeviceMutex); retval = m_receiveProcessors.size() + m_transmitProcessors.size(); return retval; } Streaming::StreamProcessor * Device::getStreamProcessorByIndex(int i) { if (i<(int)m_receiveProcessors.size()) { return m_receiveProcessors.at(i); } else if (i<(int)m_receiveProcessors.size() + (int)m_transmitProcessors.size()) { return m_transmitProcessors.at(i-m_receiveProcessors.size()); } return NULL; } bool Device::startStreamByIndex(int i) { int iso_channel=-1; bool snoopMode=false; if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } if (i<(int)m_receiveProcessors.size()) { int n=i; Streaming::StreamProcessor *p=m_receiveProcessors.at(n); if(snoopMode) { // a stream from the device to another host // FIXME: put this into a decent framework! // we should check the oPCR[n] on the device struct iec61883_oPCR opcr; if (iec61883_get_oPCRX( get1394Service().getHandle(), getConfigRom().getNodeId() | 0xffc0, (quadlet_t *)&opcr, n)) { debugWarning("Error getting the channel for SP %d\n",i); return false; } iso_channel=opcr.channel; } else { iso_channel=get1394Service().allocateIsoChannelCMP( getConfigRom().getNodeId() | 0xffc0, n, get1394Service().getLocalNodeId()| 0xffc0, -1); } if (iso_channel<0) { debugError("Could not allocate ISO channel for SP %d\n",i); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, "Started SP %d on channel %d\n",i,iso_channel); p->setChannel(iso_channel); return true; } else if (i<(int)m_receiveProcessors.size() + (int)m_transmitProcessors.size()) { int n=i-m_receiveProcessors.size(); Streaming::StreamProcessor *p=m_transmitProcessors.at(n); if(snoopMode) { // a stream from another host to the device // FIXME: put this into a decent framework! // we should check the iPCR[n] on the device struct iec61883_iPCR ipcr; if (iec61883_get_iPCRX( get1394Service().getHandle(), getConfigRom().getNodeId() | 0xffc0, (quadlet_t *)&ipcr, n)) { debugWarning("Error getting the channel for SP %d\n",i); return false; } iso_channel=ipcr.channel; } else { iso_channel=get1394Service().allocateIsoChannelCMP( get1394Service().getLocalNodeId()| 0xffc0, -1, getConfigRom().getNodeId() | 0xffc0, n); } if (iso_channel<0) { debugError("Could not allocate ISO channel for SP %d\n",i); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, "Started SP %d on channel %d\n",i,iso_channel); p->setChannel(iso_channel); return true; } debugError("SP index %d out of range!\n",i); return false; } bool Device::stopStreamByIndex(int i) { bool snoopMode=false; if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } if (i<(int)m_receiveProcessors.size()) { int n=i; Streaming::StreamProcessor *p=m_receiveProcessors.at(n); // can't stop it if it's not running if(p->getChannel() == -1) { debugOutput(DEBUG_LEVEL_VERBOSE, "SP %d not running\n",i); return true; } if(snoopMode) { } else { // deallocate ISO channel if(!get1394Service().freeIsoChannel(p->getChannel())) { debugError("Could not deallocate iso channel for SP %d\n",i); return false; } } p->setChannel(-1); return true; } else if (i<(int)m_receiveProcessors.size() + (int)m_transmitProcessors.size()) { int n=i-m_receiveProcessors.size(); Streaming::StreamProcessor *p=m_transmitProcessors.at(n); // can't stop it if it's not running if(p->getChannel() == -1) { debugOutput(DEBUG_LEVEL_VERBOSE, "SP %d not running\n",i); return true; } if(snoopMode) { } else { // deallocate ISO channel if(!get1394Service().freeIsoChannel(p->getChannel())) { debugError("Could not deallocate iso channel for SP %d\n",i); return false; } } p->setChannel(-1); return true; } debugError("SP index %d out of range!\n",i); return false; } bool Device::serialize( std::string basePath, Util::IOSerialize& ser ) const { bool result; result = AVC::Unit::serialize( basePath, ser ); result &= serializeOptions( basePath + "Options", ser ); return result; } bool Device::deserialize( std::string basePath, Util::IODeserialize& deser ) { bool result; result = AVC::Unit::deserialize( basePath, deser ); return result; } } libffado-2.4.5/src/genericavc/avc_avdevice.h0000644000175000001440000000750114206145246020354 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef GENERICAVC_DEVICE_H #define GENERICAVC_DEVICE_H #include "ffadodevice.h" #include "libutil/Configuration.h" #include "libavc/avc_definitions.h" #include "libavc/general/avc_unit.h" #include "libavc/general/avc_subunit.h" #include "libavc/general/avc_plug.h" #include "libstreaming/amdtp/AmdtpReceiveStreamProcessor.h" #include "libstreaming/amdtp/AmdtpTransmitStreamProcessor.h" #include "libstreaming/amdtp/AmdtpPort.h" #include "libstreaming/amdtp/AmdtpPortInfo.h" #include "debugmodule/debugmodule.h" class ConfigRom; class Ieee1394Service; namespace GenericAVC { class Device : public FFADODevice, public AVC::Unit { public: Device( DeviceManager& d, ffado_smartptr( configRom )); virtual ~Device(); static bool probe( Util::Configuration&, ConfigRom& configRom, bool generic = false ); virtual bool discover(); static FFADODevice * createDevice( DeviceManager& d, ffado_smartptr( configRom )); virtual bool serialize( std::string basePath, Util::IOSerialize& ser ) const; virtual bool deserialize( std::string basePath, Util::IODeserialize& deser ); virtual void setVerboseLevel(int l); virtual void showDevice(); virtual bool setSamplingFrequency( int ); virtual bool supportsSamplingFrequency( int s ); virtual int getSamplingFrequency( ); virtual std::vector getSupportedSamplingFrequencies(); virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); virtual int getStreamCount(); virtual Streaming::StreamProcessor *getStreamProcessorByIndex(int i); virtual enum eStreamingState getStreamingState(); virtual bool prepare(); virtual bool lock(); virtual bool unlock(); virtual bool startStreamByIndex(int i); virtual bool stopStreamByIndex(int i); // redefinition to resolve ambiguity virtual Ieee1394Service& get1394Service() { return FFADODevice::get1394Service(); }; virtual ConfigRom& getConfigRom() const { return FFADODevice::getConfigRom(); }; protected: bool discoverGeneric(); virtual bool addPlugToProcessor( AVC::Plug& plug, Streaming::StreamProcessor *processor, Streaming::AmdtpAudioPort::E_Direction direction); /* bool setSamplingFrequencyPlug( AVC::Plug& plug, AVC::Plug::EPlugDirection direction, AVC::ESamplingFrequency samplingFrequency );*/ // streaming stuff typedef std::vector< Streaming::StreamProcessor * > StreamProcessorVector; typedef std::vector< Streaming::StreamProcessor * >::iterator StreamProcessorVectorIterator; StreamProcessorVector m_receiveProcessors; StreamProcessorVector m_transmitProcessors; DECLARE_DEBUG_MODULE; private: ClockSource syncInfoToClockSource(const SyncInfo& si); std::vector m_supported_frequencies_cache; }; } #endif //GENERICAVC_AVDEVICE_H libffado-2.4.5/src/genericavc/stanton/0000755000175000001440000000000014206145612017244 5ustar jwoitheuserslibffado-2.4.5/src/genericavc/stanton/scs.cpp0000644000175000001440000003331714206145246020552 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "scs.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/ByteSwap.h" #include "libutil/Functors.h" namespace GenericAVC { namespace Stanton { ScsDevice::ScsDevice( DeviceManager& d, ffado_smartptr( configRom )) : GenericAVC::Device( d , configRom) , m_hss1394handler( NULL ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created GenericAVC::Stanton::ScsDevice (NodeID %d)\n", getConfigRom().getNodeId() ); } ScsDevice::~ScsDevice() { if (m_hss1394handler) { get1394Service().unregisterARMHandler(m_hss1394handler); delete m_hss1394handler; m_hss1394handler = NULL; } } bool ScsDevice::discover() { // HSS1394 is now being handled by an ALSA MIDI driver. It is therefore // not appropriate that FFADO install an ARM to receive these messages; // doing so will clearly interfere with the other driver. // if(!initMessageHandler()) { // debugError("Could not initialize HSS1394 message handler\n"); // return false; // } return Device::discover(); } bool ScsDevice::initMessageHandler() { fb_nodeaddr_t addr = HSS1394_BASE_ADDRESS; quadlet_t cmdBuffer[2]; memset(cmdBuffer, 0, sizeof(cmdBuffer)); // read the current value present in the register, i.e. read-ping if(!readRegBlock(addr, (quadlet_t *)cmdBuffer, 1) ) { debugError("Could not read from addr 0x%012" PRIX64 "\n", addr); } else { int version = cmdBuffer[0] & 0xFFFF; debugOutput(DEBUG_LEVEL_VERBOSE, "Read Ping response: %08X, Version: %d\n", cmdBuffer[0], version); if((cmdBuffer[0] >> 24 & 0xFF) != HSS1394_CMD_PING_RESPONSE) { debugWarning("Bogus device response to ping! (%08X)\n", cmdBuffer[0]); } } // do a write ping memset(cmdBuffer, 0, sizeof(cmdBuffer)); cmdBuffer[0] |= HSS1394_CMD_PING << 24; // execute the command if(!writeRegBlock(addr, (quadlet_t *)cmdBuffer, 1)) { debugError("Could not write to addr 0x%012" PRIX64 "\n", addr); } else { debugOutput(DEBUG_LEVEL_VERBOSE, "Write Ping succeeded\n"); } // get a notifier to handle device notifications nodeaddr_t notify_address; notify_address = get1394Service().findFreeARMBlock( HSS1394_RESPONSE_ADDRESS, HSS1394_MAX_PACKET_SIZE+1, HSS1394_MAX_PACKET_SIZE+1); if (notify_address == 0xFFFFFFFFFFFFFFFFLLU) { debugError("Could not find free ARM block for notification\n"); return false; } m_hss1394handler = new ScsDevice::HSS1394Handler(*this, notify_address); if(!m_hss1394handler) { debugError("Could not allocate notifier\n"); return false; } if (!get1394Service().registerARMHandler(m_hss1394handler)) { debugError("Could not register HSS1394 handler\n"); delete m_hss1394handler; m_hss1394handler = NULL; return false; } // configure device to use the allocated ARM address for communication // the address change command cmdBuffer[0] = 0; cmdBuffer[0] |= HSS1394_CMD_CHANGE_ADDRESS << 24; // the address is the argument cmdBuffer[0] |= (notify_address >> 32) & 0xFFFF; cmdBuffer[1] = notify_address & 0xFFFFFFFF; // execute the command if(!writeRegBlock(addr, (quadlet_t *)cmdBuffer, 2)) { debugError("Could not write to addr 0x%012" PRIX64 "\n", addr); return false; } // request the device to echo something cmdBuffer[0] = 0; cmdBuffer[0] |= HSS1394_CMD_ECHO_AS_USER_DATA << 24; // the address is the argument cmdBuffer[0] |= 0x1234; cmdBuffer[1] = 0x56789ABC; // execute the command if(!writeRegBlock(addr, (quadlet_t *)cmdBuffer, 2)) { debugError("Could not write to addr 0x%012" PRIX64 "\n", addr); return false; } return true; } bool ScsDevice::writeHSS1394Message(enum eMessageType message_type, byte_t* buffer, size_t len) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Writing message type: %02X, length: %zd bytes\n", message_type, len); size_t len_quadlets = len/4 + 1; fb_nodeaddr_t addr = HSS1394_BASE_ADDRESS; unsigned char tmpbuffer[len_quadlets*4]; tmpbuffer[0] = message_type; memcpy(tmpbuffer+1, buffer, len); // memcpy(tmpbuffer, buffer, len); quadlet_t *cmdBuffer = (quadlet_t *)tmpbuffer; hexDump(tmpbuffer, len_quadlets*4); // we have to cond-byte-swap this because the memory stuff is assumed to be // in big-endian byteSwapFromBus(cmdBuffer, len_quadlets); // execute the command if(!writeRegBlock(addr, (quadlet_t *)cmdBuffer, len_quadlets)) { debugError("Could not write to addr 0x%012" PRIX64 "\n", addr); return false; } return true; } bool ScsDevice::readRegBlock(fb_nodeaddr_t addr, fb_quadlet_t *data, size_t length_quads, size_t blocksize_quads) { debugOutput(DEBUG_LEVEL_VERBOSE,"Reading register 0x%016" PRIX64 ", length %zd quadlets, to %p\n", addr, length_quads, data); fb_nodeid_t nodeId = getNodeId() | 0xFFC0; int quads_done = 0; while(quads_done < (int)length_quads) { fb_nodeaddr_t curr_addr = addr + quads_done*4; fb_quadlet_t *curr_data = data + quads_done; int quads_todo = length_quads - quads_done; if (quads_todo > (int)blocksize_quads) { debugOutput(DEBUG_LEVEL_VERBOSE, "Truncating read from %d to %zd quadlets\n", quads_todo, blocksize_quads); quads_todo = blocksize_quads; } #ifdef DEBUG if (quads_todo < 0) { debugError("BUG: quads_todo < 0: %d\n", quads_todo); } #endif debugOutput(DEBUG_LEVEL_VERBOSE, "reading addr: 0x%016" PRIX64 ", %d quads to %p\n", curr_addr, quads_todo, curr_data); if(!get1394Service().read( nodeId, curr_addr, quads_todo, curr_data ) ) { debugError("Could not read %d quadlets from node 0x%04X addr 0x%012" PRIX64 "\n", quads_todo, nodeId, curr_addr); return false; } quads_done += quads_todo; } byteSwapFromBus(data, length_quads); return true; } bool ScsDevice::writeRegBlock(fb_nodeaddr_t addr, fb_quadlet_t *data, size_t length_quads, size_t blocksize_quads) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Writing register 0x%016" PRIX64 ", length: %zd quadlets, from %p\n", addr, length_quads, data); fb_quadlet_t data_out[length_quads]; memcpy(data_out, data, length_quads*4); byteSwapToBus(data_out, length_quads); fb_nodeid_t nodeId = getNodeId() | 0xFFC0; int quads_done = 0; while(quads_done < (int)length_quads) { fb_nodeaddr_t curr_addr = addr + quads_done*4; fb_quadlet_t *curr_data = data_out + quads_done; int quads_todo = length_quads - quads_done; if (quads_todo > (int)blocksize_quads) { debugOutput(DEBUG_LEVEL_VERBOSE, "Truncating write from %d to %zd quadlets\n", quads_todo, blocksize_quads); quads_todo = blocksize_quads; } #ifdef DEBUG if (quads_todo < 0) { debugError("BUG: quads_todo < 0: %d\n", quads_todo); } #endif debugOutput(DEBUG_LEVEL_VERBOSE, "writing addr: 0x%016" PRIX64 ", %d quads from %p\n", curr_addr, quads_todo, curr_data); if(!get1394Service().write( nodeId, addr, quads_todo, curr_data ) ) { debugError("Could not write %d quadlets to node 0x%04X addr 0x%012" PRIX64 "\n", quads_todo, nodeId, curr_addr); return false; } quads_done += quads_todo; } return true; } void ScsDevice::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a GenericAVC::Stanton::ScsDevice\n"); GenericAVC::Device::showDevice(); } // the incoming HSS1394 handler ScsDevice::HSS1394Handler::HSS1394Handler(Device &d, nodeaddr_t start) : ARMHandler(d.get1394Service(), start, HSS1394_MAX_PACKET_SIZE+1, RAW1394_ARM_READ | RAW1394_ARM_WRITE | RAW1394_ARM_LOCK, RAW1394_ARM_WRITE, 0) , m_device(d) { // switch over the debug module to that of this device instead of the 1394 service m_debugModule = d.m_debugModule; } ScsDevice::HSS1394Handler::~HSS1394Handler() { } bool ScsDevice::HSS1394Handler::handleRead(struct raw1394_arm_request *arm_req) { debugWarning("Unexpected Read transaction received\n"); printRequest(arm_req); return true; } bool ScsDevice::HSS1394Handler::handleWrite(struct raw1394_arm_request *arm_req) { debugOutput(DEBUG_LEVEL_VERBOSE, "HSS Write\n"); printRequest(arm_req); size_t payload_len = 0; enum eMessageType message_type = eMT_Undefined; // extract the data if(arm_req->buffer_length > 1) { message_type = byteToMessageType(arm_req->buffer[0]); payload_len = arm_req->buffer_length - 1; } else { debugWarning("Received empty message\n"); return false; } // figure out what handler to call switch(message_type) { case eMT_UserData: for ( MessageHandlerVectorIterator it = m_userDataMessageHandlers.begin(); it != m_userDataMessageHandlers.end(); ++it ) { MessageFunctor* func = *it; debugOutput(DEBUG_LEVEL_VERBOSE, "Calling functor %p\n", func); ( *func )(arm_req->buffer + 1, payload_len); } break; case eMT_DebugData: case eMT_UserTagBase: case eMT_UserTagTop: case eMT_Reset: case eMT_ChangeAddress: case eMT_Ping: case eMT_PingResponse: case eMT_EchoAsUserData: case eMT_Undefined: default: debugWarning("Unexpected Message of type: %02X\n", message_type); } return true; } bool ScsDevice::HSS1394Handler::handleLock(struct raw1394_arm_request *arm_req) { debugWarning("Unexpected Lock transaction received\n"); printRequest(arm_req); return true; } bool ScsDevice::HSS1394Handler::addMessageHandler(enum eMessageType message_type, MessageFunctor* functor) { debugOutput(DEBUG_LEVEL_VERBOSE, "Adding Message handler (%p) for message type %02X\n", functor, message_type); // figure out what handler to call switch(message_type) { case eMT_UserData: // FIXME: one handler can be added multiple times, is this what we want? m_userDataMessageHandlers.push_back( functor ); return true; case eMT_DebugData: case eMT_UserTagBase: case eMT_UserTagTop: case eMT_Reset: case eMT_ChangeAddress: case eMT_Ping: case eMT_PingResponse: case eMT_EchoAsUserData: case eMT_Undefined: default: debugError("Handlers not supported for messages of type: %02X\n", message_type); return false; } } bool ScsDevice::HSS1394Handler::removeMessageHandler(enum eMessageType message_type, MessageFunctor* functor) { debugOutput(DEBUG_LEVEL_VERBOSE, "Removing Message handler (%p) for message type %02X\n", functor, message_type); // figure out what handler to call switch(message_type) { case eMT_UserData: // FIXME: one handler can be present multiple times, how do we handle this? for ( MessageHandlerVectorIterator it = m_userDataMessageHandlers.begin(); it != m_userDataMessageHandlers.end(); ++it ) { if ( *it == functor ) { debugOutput(DEBUG_LEVEL_VERBOSE, " found\n"); m_userDataMessageHandlers.erase( it ); return true; } } debugOutput(DEBUG_LEVEL_VERBOSE, " not found\n"); return false; case eMT_DebugData: case eMT_UserTagBase: case eMT_UserTagTop: case eMT_Reset: case eMT_ChangeAddress: case eMT_Ping: case eMT_PingResponse: case eMT_EchoAsUserData: case eMT_Undefined: default: debugError("Handlers not supported for messages of type: %02X\n", message_type); return false; } } enum ScsDevice::eMessageType ScsDevice::HSS1394Handler::byteToMessageType(byte_t tag) { switch(tag) { case HSS1394_CMD_USER_DATA: return eMT_UserData; case HSS1394_CMD_DEBUG_DATA: return eMT_DebugData; case HSS1394_CMD_USER_TAG_BASE: return eMT_UserTagBase; case HSS1394_CMD_USER_TAG_TOP: return eMT_UserTagTop; case HSS1394_CMD_RESET: return eMT_Reset; case HSS1394_CMD_CHANGE_ADDRESS: return eMT_ChangeAddress; case HSS1394_CMD_PING: return eMT_Ping; case HSS1394_CMD_PING_RESPONSE: return eMT_PingResponse; case HSS1394_CMD_ECHO_AS_USER_DATA: return eMT_EchoAsUserData; case HSS1394_CMD_UNDEFINED: default: return eMT_Undefined; } } } } libffado-2.4.5/src/genericavc/stanton/scs.h0000644000175000001440000001003514206145246020207 0ustar jwoitheusers/* * Copyright (C) 2009 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef GENERICAVC_STANTON_SCS_DEVICE_H #define GENERICAVC_STANTON_SCS_DEVICE_H #include "genericavc/avc_avdevice.h" #include "libieee1394/ieee1394service.h" namespace Util { class Functor; } namespace GenericAVC { namespace Stanton { #define HSS1394_BASE_ADDRESS 0x0000c007dedadadaULL #define HSS1394_RESPONSE_ADDRESS 0x0000c007E0000000ULL #define HSS1394_MAX_PACKET_SIZE 63 #define HSS1394_INVALID_NETWORK_ID 0xff #define HSS1394_MAX_RETRIES 32 #define HSS1394_CMD_USER_DATA 0x00 #define HSS1394_CMD_DEBUG_DATA 0x01 #define HSS1394_CMD_USER_TAG_BASE 0x10 #define HSS1394_CMD_USER_TAG_TOP 0xEF #define HSS1394_CMD_RESET 0xF0 #define HSS1394_CMD_CHANGE_ADDRESS 0xF1 #define HSS1394_CMD_PING 0xF2 #define HSS1394_CMD_PING_RESPONSE 0xF3 #define HSS1394_CMD_ECHO_AS_USER_DATA 0xF4 #define HSS1394_CMD_UNDEFINED 0xFF class ScsDevice : public GenericAVC::Device { public: class HSS1394Handler; enum eMessageType { eMT_UserData = HSS1394_CMD_USER_DATA, eMT_DebugData = HSS1394_CMD_DEBUG_DATA, eMT_UserTagBase = HSS1394_CMD_USER_TAG_BASE, eMT_UserTagTop = HSS1394_CMD_USER_TAG_TOP, eMT_Reset = HSS1394_CMD_RESET, eMT_ChangeAddress = HSS1394_CMD_CHANGE_ADDRESS, eMT_Ping = HSS1394_CMD_PING, eMT_PingResponse = HSS1394_CMD_PING_RESPONSE, eMT_EchoAsUserData = HSS1394_CMD_ECHO_AS_USER_DATA, eMT_Undefined = HSS1394_CMD_UNDEFINED, }; public: ScsDevice( DeviceManager& d, ffado_smartptr( configRom )); virtual ~ScsDevice(); virtual void showDevice(); virtual bool initMessageHandler(); bool discover(); bool writeHSS1394Message(enum eMessageType message_type, byte_t*, size_t); // private: // FIXME: should be private!!! HSS1394Handler *m_hss1394handler; private: bool writeRegBlock(fb_nodeaddr_t addr, fb_quadlet_t *data, size_t length, size_t blocksize_quads=32); bool readRegBlock(fb_nodeaddr_t addr, fb_quadlet_t *data, size_t length, size_t blocksize_quads=32); public: class HSS1394Handler : public Ieee1394Service::ARMHandler { public: class MessageFunctor { public: MessageFunctor() {}; virtual ~MessageFunctor() {} virtual void operator() (byte_t *, size_t len) = 0; }; typedef std::vector MessageHandlerVector; typedef std::vector::iterator MessageHandlerVectorIterator; public: HSS1394Handler(Device &, nodeaddr_t start); virtual ~HSS1394Handler(); virtual bool handleRead(struct raw1394_arm_request *); virtual bool handleWrite(struct raw1394_arm_request *); virtual bool handleLock(struct raw1394_arm_request *); bool addMessageHandler(enum eMessageType message_type, MessageFunctor* functor); bool removeMessageHandler(enum eMessageType message_type, MessageFunctor* functor); private: Device &m_device; MessageHandlerVector m_userDataMessageHandlers; private: enum eMessageType byteToMessageType(byte_t); }; }; } } #endif // GENERICAVC_STANTON_SCS_DEVICE_H libffado-2.4.5/src/libavc/0000755000175000001440000000000014206145612014710 5ustar jwoitheuserslibffado-2.4.5/src/libavc/audiosubunit/0000755000175000001440000000000014206145612017423 5ustar jwoitheuserslibffado-2.4.5/src/libavc/audiosubunit/avc_audiosubunit.cpp0000644000175000001440000000370614206145246023504 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_audiosubunit.h" #include "../general/avc_subunit.h" #include "../audiosubunit/avc_function_block.h" #include "../audiosubunit/avc_descriptor_audio.h" #include namespace AVC { //////////////////////////////////////////// SubunitAudio::SubunitAudio( Unit& unit, subunit_t id ) : Subunit( unit, eST_Audio, id ) , m_identifier_descriptor ( new AVCAudioIdentifierDescriptor( &unit, this ) ) { } SubunitAudio::SubunitAudio() : Subunit() { } SubunitAudio::~SubunitAudio() { } bool SubunitAudio::discover() { debugOutput(DEBUG_LEVEL_NORMAL, "Discovering %s...\n", getName()); if ( !Subunit::discover() ) { return false; } // load the descriptor (if not already loaded) // m_identifier_descriptor->setVerboseLevel(DEBUG_LEVEL_VERY_VERBOSE); // if (m_identifier_descriptor != NULL) { // if(!m_identifier_descriptor->load()) { // debugWarning("Could not load Audio Subunit Identifier descriptor\n"); // } // } return true; } const char* SubunitAudio::getName() { return "AVC::AudioSubunit"; } } libffado-2.4.5/src/libavc/audiosubunit/avc_audiosubunit.h0000644000175000001440000000362114206145246023145 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVC_AUDIOSUBUNIT_H #define AVC_AUDIOSUBUNIT_H #include "debugmodule/debugmodule.h" #include "../general/avc_subunit.h" #include #include "../audiosubunit/avc_function_block.h" namespace AVC { class Unit; // ///////////////////////////// class SubunitAudio: public Subunit { public: SubunitAudio( Unit& avDevice, subunit_t id ); SubunitAudio(); virtual ~SubunitAudio(); virtual bool discover(); virtual const char* getName(); protected: virtual bool serializeChild( std::string basePath, Util::IOSerialize& ser ) const { return true; } virtual bool deserializeChild( std::string basePath, Util::IODeserialize& deser, Unit& avDevice ) { return true; } virtual bool deserializeUpdateChild( std::string basePath, Util::IODeserialize& deser ) { return true; } class AVCAudioIdentifierDescriptor* m_identifier_descriptor; }; } #endif libffado-2.4.5/src/libavc/audiosubunit/avc_descriptor_audio.cpp0000644000175000001440000001761714206145246024336 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_descriptor_audio.h" #include "../descriptors/avc_descriptor.h" #include "../descriptors/avc_descriptor_cmd.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "../general/avc_subunit.h" #include "../general/avc_unit.h" #include "libutil/ByteSwap.h" namespace AVC { // ---------------------- bool AVCAudioClusterInformation::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= se.write(m_cluster_info_length, "AVCAudioClusterInformation m_cluster_info_length"); result &= se.write(m_number_of_channels, "AVCAudioClusterInformation m_number_of_channels"); result &= se.write(m_ChConfigType, "AVCAudioClusterInformation m_ChConfigType"); result &= se.write(m_Predefined_ChannelConfig, "AVCAudioClusterInformation m_Predefined_ChannelConfig"); if(m_cluster_info_length > 4) { for(int i=0; i 4) { m_channel_name_IDs.clear(); for(int i=0; i tmp_vect = m_root_object_list_IDs; for(int i=0; i. * */ /** * Implements the AV/C Descriptors/InfoBlocks for the Audio Subunit as in TA1999008 * */ #ifndef AVCDESCRIPTORAUDIO_H #define AVCDESCRIPTORAUDIO_H #include "../descriptors/avc_descriptor.h" #include "../avc_definitions.h" #include "../general/avc_generic.h" #include "../general/avc_plug.h" #include "debugmodule/debugmodule.h" #include #include class Ieee1394Service; // forward declarations namespace Util { namespace Cmd { class IOSSerialize; class IISDeserialize; } } namespace AVC { class AVCAudioClusterInformation { public: virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); AVCAudioClusterInformation( ) {}; virtual ~AVCAudioClusterInformation() {}; protected: private: uint16_t m_cluster_info_length; byte_t m_number_of_channels; byte_t m_ChConfigType; uint16_t m_Predefined_ChannelConfig; std::vector m_channel_name_IDs; }; class AVCAudioConfigurationDependentInformation { public: virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); AVCAudioConfigurationDependentInformation( ) {}; virtual ~AVCAudioConfigurationDependentInformation() {}; protected: private: uint16_t m_configuration_dependent_info_length; uint16_t m_configuration_ID; AVCAudioClusterInformation m_master_cluster_information; byte_t m_number_of_subunit_source_plug_link_information; std::vector m_subunit_source_plug_link_informations; byte_t m_number_of_function_block_dependent_information; }; class AVCAudioSubunitDependentInformation { public: virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); AVCAudioSubunitDependentInformation( ) {}; virtual ~AVCAudioSubunitDependentInformation() {}; protected: private: uint16_t m_audio_subunit_dependent_info_fields_length; byte_t m_audio_subunit_version; byte_t m_number_of_configurations; std::vector m_configurations; }; /** * Audio Subunit Identifier Descriptor */ class AVCAudioIdentifierDescriptor : public AVCDescriptor { public: virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); AVCAudioIdentifierDescriptor( Unit* unit, Subunit* subunit ); virtual ~AVCAudioIdentifierDescriptor() {} virtual const char* getDescriptorName() const {return "AVCAudioIdentifierDescriptor";}; private: byte_t m_generation_ID; byte_t m_size_of_list_ID; byte_t m_size_of_object_ID; byte_t m_size_of_object_position; uint16_t m_number_of_root_object_lists; std::vector m_root_object_list_IDs; uint16_t m_audio_subunit_dependent_length; AVCAudioSubunitDependentInformation m_audio_subunit_dependent_info; }; } #endif libffado-2.4.5/src/libavc/audiosubunit/avc_function_block.cpp0000644000175000001440000005423214206145246023770 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_function_block.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include namespace AVC { ///////////////////////////////// FunctionBlockFeatureVolume::FunctionBlockFeatureVolume() : IBusData() , m_controlDataLength( 2 ) , m_volume( 0 ) { } FunctionBlockFeatureVolume::FunctionBlockFeatureVolume( const FunctionBlockFeatureVolume& rhs ) : m_controlDataLength( rhs.m_controlDataLength ) , m_volume( rhs.m_volume ) { } FunctionBlockFeatureVolume::~FunctionBlockFeatureVolume() { } bool FunctionBlockFeatureVolume::serialize( Util::Cmd::IOSSerialize& se ) { bool bStatus; byte_t val; bStatus = se.write( m_controlDataLength, "FunctionBlockFeatureVolume controlDataLength" ); val = (byte_t)(m_volume >> 8); bStatus &= se.write( val, "FunctionBlockFeatureVolume volume high" ); val = m_volume & 0xff; bStatus &= se.write( val, "FunctionBlockFeatureVolume volume low" ); return bStatus; } bool FunctionBlockFeatureVolume::deserialize( Util::Cmd::IISDeserialize& de ) { bool bStatus; byte_t val; bStatus = de.read( &m_controlDataLength ); bStatus &= de.read( &val ); m_volume = val << 8; bStatus &= de.read( &val ); m_volume |= val; return bStatus; } FunctionBlockFeatureVolume* FunctionBlockFeatureVolume::clone() const { return new FunctionBlockFeatureVolume( *this ); } ///////////////////////////////// FunctionBlockFeatureLRBalance::FunctionBlockFeatureLRBalance() : IBusData() , m_controlDataLength( 2 ) , m_lrBalance( 0 ) { } FunctionBlockFeatureLRBalance::FunctionBlockFeatureLRBalance( const FunctionBlockFeatureLRBalance& rhs ) : m_controlDataLength( rhs.m_controlDataLength ) , m_lrBalance( rhs.m_lrBalance ) { } FunctionBlockFeatureLRBalance::~FunctionBlockFeatureLRBalance() { } bool FunctionBlockFeatureLRBalance::serialize( Util::Cmd::IOSSerialize& se ) { bool bStatus; byte_t val; bStatus = se.write( m_controlDataLength, "FunctionBlockFeatureLRBalance controlDataLength" ); val = (byte_t)(m_lrBalance >> 8); bStatus &= se.write( val, "FunctionBlockFeatureLRBalance LR Balance high" ); val = m_lrBalance & 0xff; bStatus &= se.write( val, "FunctionBlockFeatureLRBalance LR Balance low" ); return bStatus; } bool FunctionBlockFeatureLRBalance::deserialize( Util::Cmd::IISDeserialize& de ) { bool bStatus; byte_t val; bStatus = de.read( &m_controlDataLength ); bStatus &= de.read( &val ); m_lrBalance = val << 8; bStatus &= de.read( &val ); m_lrBalance |= val; return bStatus; } FunctionBlockFeatureLRBalance* FunctionBlockFeatureLRBalance::clone() const { return new FunctionBlockFeatureLRBalance( *this ); } ///////////////////////////////// FunctionBlockProcessingMixer::FunctionBlockProcessingMixer() : IBusData() , m_controlSelector( FunctionBlockProcessing::eCSE_Processing_Mixer ) , m_controlDataLength( 0x02 ) , m_mixerSetting( 0x0000 ) { } FunctionBlockProcessingMixer::FunctionBlockProcessingMixer( const FunctionBlockProcessingMixer& rhs ) : m_controlSelector( rhs.m_controlSelector ), m_controlDataLength( 0x02 ), m_mixerSetting( 0x00 ) { } FunctionBlockProcessingMixer::~FunctionBlockProcessingMixer() { } bool FunctionBlockProcessingMixer::serialize( Util::Cmd::IOSSerialize& se ) { bool bStatus; bStatus = se.write( m_controlSelector, "FunctionBlockProcessingMixer controlSelector" ); bStatus &= se.write( m_controlDataLength, "FunctionBlockProcessingMixer controlDataLength" ); bStatus &= se.write( m_mixerSetting, "FunctionBlockProcessingMixer mixerSetting" ); return bStatus; } bool FunctionBlockProcessingMixer::deserialize( Util::Cmd::IISDeserialize& de ) { bool bStatus; byte_t padding; bStatus = de.read( &m_controlSelector ); bStatus &= de.read( &m_controlDataLength ); bStatus &= de.read( &m_mixerSetting ); bStatus &= de.read( &padding ); bStatus &= de.read( &padding ); return bStatus; } FunctionBlockProcessingMixer* FunctionBlockProcessingMixer::clone() const { return new FunctionBlockProcessingMixer( *this ); } ///////////////////////////////// FunctionBlockProcessingEnhancedMixer::FunctionBlockProcessingEnhancedMixer() : IBusData() , m_controlSelector( FunctionBlockProcessing::eCSE_Processing_EnhancedMixer ) , m_statusSelector( eSS_Level ) , m_controlDataLength( 0 ) { } FunctionBlockProcessingEnhancedMixer::FunctionBlockProcessingEnhancedMixer( const FunctionBlockProcessingEnhancedMixer& rhs ) : m_controlSelector( rhs.m_controlSelector ) , m_statusSelector( rhs.m_statusSelector ) { } FunctionBlockProcessingEnhancedMixer::~FunctionBlockProcessingEnhancedMixer() { } bool FunctionBlockProcessingEnhancedMixer::serialize( Util::Cmd::IOSSerialize& se ) { bool bStatus; byte_t data_length_hi, data_length_lo; bStatus = se.write( m_controlSelector, "FunctionBlockProcessingEnhancedMixer controlSelector" ); bStatus &= se.write( m_statusSelector, "FunctionBlockProcessingEnhancedMixer statusSelector" ); switch (m_statusSelector) { case eSS_ProgramableState: m_controlDataLength=m_ProgramableStateData.size()/8; data_length_hi=(m_controlDataLength >> 8); data_length_lo=(m_controlDataLength & 0xFF); bStatus &= se.write( data_length_hi, "FunctionBlockProcessingEnhancedMixer controlDataLengthHi" ); bStatus &= se.write( data_length_lo, "FunctionBlockProcessingEnhancedMixer controlDataLengthLo" ); for (int i=0;i> 8); data_length_lo=(m_controlDataLength & 0xFF); bStatus &= se.write( data_length_hi, "FunctionBlockProcessingEnhancedMixer controlDataLengthHi" ); bStatus &= se.write( data_length_lo, "FunctionBlockProcessingEnhancedMixer controlDataLengthLo" ); for (int i=0;i> 8; byte_t value_lo=value & 0xFF; bStatus &= se.write( value_hi, "FunctionBlockProcessingEnhancedMixer data" ); bStatus &= se.write( value_lo, "FunctionBlockProcessingEnhancedMixer data" ); } break; } return bStatus; } bool FunctionBlockProcessingEnhancedMixer::deserialize( Util::Cmd::IISDeserialize& de ) { bool bStatus; bStatus = de.read( &m_controlSelector ); // NOTE: the returned value is currently bogus, so overwrite it m_controlSelector=FunctionBlockProcessing::eCSE_Processing_EnhancedMixer; bStatus &= de.read( &m_statusSelector ); // same here m_statusSelector = eSS_Level; //m_statusSelector = eSS_ProgramableState; byte_t data_length_hi; byte_t data_length_lo; bStatus &= de.read( &data_length_hi ); bStatus &= de.read( &data_length_lo ); m_controlDataLength = (data_length_hi << 8) + data_length_lo; printf("m_controlDataLength = %d\n", m_controlDataLength); switch (m_statusSelector) { case eSS_ProgramableState: m_ProgramableStateData.clear(); for (int i=0;i=0;j--) { byte_t bit_value; bit_value=(((1<serialize( se ); break; case eCSE_Feature_LRBalance: bStatus &= m_pLRBalance->serialize( se ); break; case eCSE_Feature_Mute: case eCSE_Feature_FRBalance: case eCSE_Feature_Bass: case eCSE_Feature_Mid: case eCSE_Feature_Treble: case eCSE_Feature_GEQ: case eCSE_Feature_AGC: case eCSE_Feature_Delay: case eCSE_Feature_BassBoost: case eCSE_Feature_Loudness: default: bStatus = false; } return bStatus; } bool FunctionBlockFeature::deserialize( Util::Cmd::IISDeserialize& de ) { bool bStatus; bStatus = de.read( &m_selectorLength ); bStatus &= de.read( &m_audioChannelNumber ); bStatus &= de.read( &m_controlSelector ); switch( m_controlSelector ) { case eCSE_Feature_Volume: bStatus &= m_pVolume->deserialize( de ); break; case eCSE_Feature_LRBalance: bStatus &= m_pLRBalance->deserialize( de ); break; case eCSE_Feature_Mute: case eCSE_Feature_FRBalance: case eCSE_Feature_Bass: case eCSE_Feature_Mid: case eCSE_Feature_Treble: case eCSE_Feature_GEQ: case eCSE_Feature_AGC: case eCSE_Feature_Delay: case eCSE_Feature_BassBoost: case eCSE_Feature_Loudness: default: bStatus = false; } return bStatus; } FunctionBlockFeature* FunctionBlockFeature::clone() const { return new FunctionBlockFeature( *this ); } ///////////////////////////////// FunctionBlockProcessing::FunctionBlockProcessing() : IBusData() , m_selectorLength( 0x04 ) , m_fbInputPlugNumber( 0x00 ) , m_inputAudioChannelNumber( 0x00 ) , m_outputAudioChannelNumber( 0x00 ) , m_pMixer( 0 ) , m_pEnhancedMixer( 0 ) { } FunctionBlockProcessing::FunctionBlockProcessing( const FunctionBlockProcessing& rhs ) : m_selectorLength( rhs.m_selectorLength ) , m_fbInputPlugNumber( rhs.m_fbInputPlugNumber ) , m_inputAudioChannelNumber( rhs.m_inputAudioChannelNumber ) , m_outputAudioChannelNumber( rhs.m_outputAudioChannelNumber ) { if ( rhs.m_pMixer ) { m_pMixer = new FunctionBlockProcessingMixer( *rhs.m_pMixer ); } else if ( rhs.m_pEnhancedMixer ) { m_pEnhancedMixer = new FunctionBlockProcessingEnhancedMixer( *rhs.m_pEnhancedMixer ); } } FunctionBlockProcessing::~FunctionBlockProcessing() { delete m_pMixer; m_pMixer = 0; delete m_pEnhancedMixer; m_pEnhancedMixer = 0; } bool FunctionBlockProcessing::serialize( Util::Cmd::IOSSerialize& se ) { bool bStatus; bStatus = se.write( m_selectorLength, "FunctionBlockProcessing selectorLength" ); bStatus &= se.write( m_fbInputPlugNumber, "FunctionBlockProcessing fbInputPlugNumber" ); bStatus &= se.write( m_inputAudioChannelNumber, "FunctionBlockProcessing inputAudioChannelNumber" ); bStatus &= se.write( m_outputAudioChannelNumber, "FunctionBlockProcessing outputAudioChannelNumber" ); if ( m_pMixer ) { bStatus &= m_pMixer->serialize( se ); } else if ( m_pEnhancedMixer ) { bStatus &= m_pEnhancedMixer->serialize( se ); } else { bStatus = false; } return bStatus; } bool FunctionBlockProcessing::deserialize( Util::Cmd::IISDeserialize& de ) { // NOTE: apparently the fbCmd of the STATUS type, // with EnhancedMixer controlSelector returns with this // controlSelector type changed to Mixer, making it // impossible to choose the correct response handler // based upon the response only. // HACK: we assume that it is the same as the sent one // we also look at our data structure to figure out what we sent byte_t controlSelector=eCSE_Processing_Unknown; if(m_pMixer) { controlSelector=eCSE_Processing_Mixer; } else if(m_pEnhancedMixer) { controlSelector=eCSE_Processing_EnhancedMixer; } bool bStatus; bStatus = de.read( &m_selectorLength ); bStatus &= de.read( &m_fbInputPlugNumber ); bStatus &= de.read( &m_inputAudioChannelNumber ); bStatus &= de.read( &m_outputAudioChannelNumber ); byte_t controlSelector_response; bStatus &= de.peek( &controlSelector_response ); /* debugOutput(DEBUG_LEVEL_VERBOSE,"ctrlsel: orig = %02X, resp = %02X\n", controlSelector, controlSelector_response);*/ switch( controlSelector ) { case eCSE_Processing_Mixer: if ( !m_pMixer ) { m_pMixer = new FunctionBlockProcessingMixer; } bStatus &= m_pMixer->deserialize( de ); break; case eCSE_Processing_EnhancedMixer: if ( !m_pEnhancedMixer ) { m_pEnhancedMixer = new FunctionBlockProcessingEnhancedMixer; } bStatus &= m_pEnhancedMixer->deserialize( de ); break; case eCSE_Processing_Enable: case eCSE_Processing_Mode: default: bStatus = false; } byte_t tmp; if (de.peek(&tmp)) { debugOutput(DEBUG_LEVEL_VERBOSE,"Unprocessed bytes:\n"); while (de.read(&tmp)) { debugOutput(DEBUG_LEVEL_VERBOSE," %02X\n",tmp); } } return bStatus; } FunctionBlockProcessing* FunctionBlockProcessing::clone() const { return new FunctionBlockProcessing( *this ); } ///////////////////////////////// FunctionBlockCodec::FunctionBlockCodec() : IBusData() { } FunctionBlockCodec::FunctionBlockCodec( const FunctionBlockCodec& rhs ) : IBusData() { } FunctionBlockCodec::~FunctionBlockCodec() { } bool FunctionBlockCodec::serialize( Util::Cmd::IOSSerialize& se ) { return false; } bool FunctionBlockCodec::deserialize( Util::Cmd::IISDeserialize& de ) { return false; } FunctionBlockCodec* FunctionBlockCodec::clone() const { return new FunctionBlockCodec( *this ); } ///////////////////////////////// ///////////////////////////////// FunctionBlockCmd::FunctionBlockCmd( Ieee1394Service& ieee1394service, EFunctionBlockType eType, function_block_id_t id, EControlAttribute eCtrlAttrib ) : AVCCommand( ieee1394service, AVC1394_FUNCTION_BLOCK_CMD ) , m_functionBlockType( eType ) , m_functionBlockId( id ) , m_controlAttribute( eCtrlAttrib ) , m_pFBSelector( 0 ) , m_pFBFeature( 0 ) , m_pFBProcessing( 0 ) , m_pFBCodec( 0 ) { setSubunitType( eST_Audio ); switch( m_functionBlockType ) { case eFBT_Selector: m_pFBSelector = new FunctionBlockSelector; break; case eFBT_Feature: m_pFBFeature = new FunctionBlockFeature; break; case eFBT_Processing: m_pFBProcessing = new FunctionBlockProcessing; break; case eFBT_Codec: m_pFBCodec = new FunctionBlockCodec; break; } } FunctionBlockCmd::FunctionBlockCmd( const FunctionBlockCmd& rhs ) : AVCCommand( rhs ) , m_functionBlockType( rhs.m_functionBlockType ) , m_functionBlockId( rhs.m_functionBlockId ) , m_controlAttribute( rhs.m_controlAttribute ) , m_pFBSelector( new FunctionBlockSelector( *rhs.m_pFBSelector ) ) , m_pFBFeature( new FunctionBlockFeature( *rhs.m_pFBFeature ) ) , m_pFBProcessing( new FunctionBlockProcessing( *rhs.m_pFBProcessing ) ) , m_pFBCodec( new FunctionBlockCodec( *rhs.m_pFBCodec ) ) { } FunctionBlockCmd::~FunctionBlockCmd() { delete m_pFBSelector; m_pFBSelector = 0; delete m_pFBFeature; m_pFBFeature = 0; delete m_pFBProcessing; m_pFBProcessing = 0; delete m_pFBCodec; m_pFBCodec = 0; } bool FunctionBlockCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool bStatus; bStatus = AVCCommand::serialize( se ); bStatus &= se.write( m_functionBlockType, "FunctionBlockCmd functionBlockType" ); bStatus &= se.write( m_functionBlockId, "FunctionBlockCmd functionBlockId" ); bStatus &= se.write( m_controlAttribute, "FunctionBlockCmd controlAttribute" ); switch( m_functionBlockType ) { case eFBT_Selector: if ( m_pFBSelector ) { bStatus &= m_pFBSelector->serialize( se ); } else { bStatus = false; } break; case eFBT_Feature: if ( m_pFBFeature ) { bStatus &= m_pFBFeature->serialize( se ); } else { bStatus = false; } break; case eFBT_Processing: if ( m_pFBProcessing ) { bStatus &= m_pFBProcessing->serialize( se ); } else { bStatus = false; } break; case eFBT_Codec: if ( m_pFBCodec ) { bStatus &= m_pFBCodec->serialize( se ); } else { bStatus = false; } break; default: bStatus = false; } return bStatus; } bool FunctionBlockCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool bStatus; bStatus = AVCCommand::deserialize( de ); bStatus &= de.read( &m_functionBlockType ); bStatus &= de.read( &m_functionBlockId ); bStatus &= de.read( &m_controlAttribute ); switch( m_functionBlockType ) { case eFBT_Selector: if ( !m_pFBSelector ) { m_pFBSelector = new FunctionBlockSelector; } bStatus &= m_pFBSelector->deserialize( de ); break; case eFBT_Feature: if ( !m_pFBFeature ) { m_pFBFeature = new FunctionBlockFeature; } bStatus &= m_pFBFeature->deserialize( de ); break; case eFBT_Processing: if ( !m_pFBProcessing ) { m_pFBProcessing = new FunctionBlockProcessing; } bStatus &= m_pFBProcessing->deserialize( de ); break; case eFBT_Codec: if ( !m_pFBCodec ) { m_pFBCodec = new FunctionBlockCodec; } bStatus &= m_pFBCodec->deserialize( de ); break; default: bStatus = false; } return bStatus; } FunctionBlockCmd* FunctionBlockCmd::clone() const { return new FunctionBlockCmd( *this ); } } libffado-2.4.5/src/libavc/audiosubunit/avc_function_block.h0000644000175000001440000002264714206145246023442 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCFUNCTIONBLOCK_H #define AVCFUNCTIONBLOCK_H #include "../general/avc_extended_cmd_generic.h" #include "../general/avc_generic.h" #include using namespace std; namespace AVC { class FunctionBlockFeatureVolume: public IBusData { public: FunctionBlockFeatureVolume(); FunctionBlockFeatureVolume( const FunctionBlockFeatureVolume& rhs ); virtual ~FunctionBlockFeatureVolume(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual FunctionBlockFeatureVolume* clone() const; control_data_length_t m_controlDataLength; u_int16_t m_volume; }; /////////////////////////////////////////// class FunctionBlockFeatureLRBalance: public IBusData { public: FunctionBlockFeatureLRBalance(); FunctionBlockFeatureLRBalance( const FunctionBlockFeatureLRBalance& rhs ); virtual ~FunctionBlockFeatureLRBalance(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual FunctionBlockFeatureLRBalance* clone() const; control_data_length_t m_controlDataLength; u_int16_t m_lrBalance; }; /////////////////////////////////////////// class FunctionBlockProcessingMixer: public IBusData { public: FunctionBlockProcessingMixer(); FunctionBlockProcessingMixer( const FunctionBlockProcessingMixer& rhs ); virtual ~FunctionBlockProcessingMixer(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual FunctionBlockProcessingMixer* clone() const; control_selector_t m_controlSelector; control_data_length_t m_controlDataLength; u_int16_t m_mixerSetting; }; /////////////////////////////////////////// /* * The enhanced mixer feature function block is not * working on all current BeBoB devices. This code * is there for not really tested or even working. */ class FunctionBlockProcessingEnhancedMixer: public IBusData { public: enum EStatusSelector { eSS_ProgramableState = 0x00, eSS_Level = 0x01, }; FunctionBlockProcessingEnhancedMixer(); FunctionBlockProcessingEnhancedMixer( const FunctionBlockProcessingEnhancedMixer& rhs ); virtual ~FunctionBlockProcessingEnhancedMixer(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual FunctionBlockProcessingEnhancedMixer* clone() const; control_selector_t m_controlSelector; status_selector_t m_statusSelector; control_data_ext_length_t m_controlDataLength; vector m_ProgramableStateData; vector m_LevelData; }; /////////////////////////////////////////// /////////////////////////////////////////// class FunctionBlockSelector: public IBusData { // untested public: // Control selector encoding enum EControlSelectorEncoding { eCSE_Selector_Unknown = 0x00, eCSE_Selector_Selector = 0x01, }; FunctionBlockSelector(); FunctionBlockSelector( const FunctionBlockSelector& rhs ); virtual ~FunctionBlockSelector(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual FunctionBlockSelector* clone() const; selector_length_t m_selectorLength; input_fb_plug_number_t m_inputFbPlugNumber; control_selector_t m_controlSelector; }; /////////////////////////////////////////// class FunctionBlockFeature: public IBusData { // no complete implementation public: // Control selector encoding enum EControlSelectorEncoding { eCSE_Feature_Unknown = 0x00, eCSE_Feature_Mute = 0x01, eCSE_Feature_Volume = 0x02, eCSE_Feature_LRBalance = 0x03, eCSE_Feature_FRBalance = 0x04, eCSE_Feature_Bass = 0x05, eCSE_Feature_Mid = 0x06, eCSE_Feature_Treble = 0x07, eCSE_Feature_GEQ = 0x08, eCSE_Feature_AGC = 0x09, eCSE_Feature_Delay = 0x0a, eCSE_Feature_BassBoost = 0x0b, eCSE_Feature_Loudness = 0x0c, }; FunctionBlockFeature(); FunctionBlockFeature( const FunctionBlockFeature& rhs ); virtual ~FunctionBlockFeature(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual FunctionBlockFeature* clone() const; selector_length_t m_selectorLength; audio_channel_number_t m_audioChannelNumber; control_selector_t m_controlSelector; FunctionBlockFeatureVolume* m_pVolume; FunctionBlockFeatureLRBalance* m_pLRBalance; }; /////////////////////////////////////////// class FunctionBlockProcessing: public IBusData { // no complete implementation public: // Function block selector enum EProcessingTypeEncoding { ePTE_Mixer = 0x01, ePTE_Generic = 0x02, ePTE_UpDown = 0x03, ePTE_DolbyProLogic = 0x04, ePTE_3dStereoExtender = 0x05, ePTE_Reverberation = 0x06, ePTE_Chorus = 0x07, ePTE_DynamicRangeCompression = 0x08, }; // Control selector encoding enum EControlSelectorEncoding { eCSE_Processing_Unknown = 0x00, eCSE_Processing_Enable = 0x01, eCSE_Processing_Mode = 0x02, eCSE_Processing_Mixer = 0x03, eCSE_Processing_EnhancedMixer = 0xf1, // lots of definitions missing }; FunctionBlockProcessing(); FunctionBlockProcessing( const FunctionBlockProcessing& rhs ); virtual ~FunctionBlockProcessing(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual FunctionBlockProcessing* clone() const; selector_length_t m_selectorLength; input_fb_plug_number_t m_fbInputPlugNumber; input_audio_channel_number_t m_inputAudioChannelNumber; output_audio_channel_number_t m_outputAudioChannelNumber; FunctionBlockProcessingMixer* m_pMixer; FunctionBlockProcessingEnhancedMixer* m_pEnhancedMixer; }; /////////////////////////////////////////// class FunctionBlockCodec: public IBusData { // dummy implementation public: // CODEC type endcoding enum ECodecTypeEncoding { eCTE_Unknown = 0x00, eCTE_Ac3Decoder = 0x01, eCTE_MpegDecoder = 0x02, eCTE_DtsDecoder = 0x03, }; FunctionBlockCodec(); FunctionBlockCodec( const FunctionBlockCodec& rhs ); virtual ~FunctionBlockCodec(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual FunctionBlockCodec* clone() const; }; /////////////////////////////////////////// /////////////////////////////////////////// #define AVC1394_FUNCTION_BLOCK_CMD 0xB8 class FunctionBlockCmd: public AVCCommand { public: enum EFunctionBlockType { eFBT_Selector = 0x80, eFBT_Feature = 0x81, eFBT_Processing = 0x82, eFBT_Codec = 0x83, }; enum EControlAttribute { eCA_Resolution = 0x01, eCA_Minimum = 0x02, eCA_Maximum = 0x03, eCA_Default = 0x04, eCA_Duration = 0x08, eCA_Current = 0x10, eCA_Move = 0x18, eCA_Delta = 0x19, }; FunctionBlockCmd( Ieee1394Service& ieee1394service, EFunctionBlockType eType, function_block_id_t id, EControlAttribute eCtrlAttrib ); FunctionBlockCmd( const FunctionBlockCmd& rhs ); virtual ~FunctionBlockCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual FunctionBlockCmd* clone() const; virtual const char* getCmdName() const { return "FunctionBlockCmd"; } function_block_type_t m_functionBlockType; function_block_id_t m_functionBlockId; control_attribute_t m_controlAttribute; FunctionBlockSelector* m_pFBSelector; FunctionBlockFeature* m_pFBFeature; FunctionBlockProcessing* m_pFBProcessing; FunctionBlockCodec* m_pFBCodec; }; } #endif libffado-2.4.5/src/libavc/avc_definitions.cpp0000644000175000001440000001207014206145246020563 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #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 ) { std::string 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; } } } libffado-2.4.5/src/libavc/avc_definitions.h0000644000175000001440000001100414206145246020224 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCDEFINITIONS_H #define AVCDEFINITIONS_H #include #include #include #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; enum ESubunitType { eST_Monitor = 0x00, eST_Audio = 0x01, eST_Printer = 0x02, eST_Disc = 0x03, eST_VCR = 0x04, eST_Tuner = 0x05, eST_CA = 0x06, eST_Camera = 0x07, eST_Panel = 0x09, eST_BulltinBoard = 0x0A, eST_CameraStorage = 0x0B, eST_Music = 0x0C, eST_VendorUnique = 0x1C, eST_Reserved = 0x1D, eST_Extended = 0x1E, eST_Unit = 0x1F, }; #define AVC1394_SUBUNIT_ID_RESERVED 0x06 enum ESubunitType byteToSubunitType(byte_t s); /** * \brief the possible sampling frequencies */ enum ESamplingFrequency { eSF_22050Hz = 0x00, eSF_24000Hz = 0x01, eSF_32000Hz = 0x02, eSF_44100Hz = 0x03, eSF_48000Hz = 0x04, eSF_88200Hz = 0x0A, eSF_96000Hz = 0x05, eSF_176400Hz = 0x06, eSF_192000Hz = 0x07, eSF_AnyLow = 0x0B, eSF_AnyMid = 0x0C, eSF_AnyHigh = 0x0D, eSF_None = 0x0E, eSF_DontCare = 0x0F, }; /** * \brief Convert from ESamplingFrequency to an integer * @param freq * @return */ int convertESamplingFrequency(ESamplingFrequency freq); /** * \brief Convert from integer to ESamplingFrequency * @param sampleRate * @return */ ESamplingFrequency parseSampleRate( int sampleRate ); std::ostream& operator<<( std::ostream& stream, ESamplingFrequency samplingFrequency ); /** * \brief Convert from a FDF SFC field value to an integer sample rate * @param fdf fdf sfc field value * @return sample rate */ unsigned int fdfSfcToSampleRate(byte_t fdf); /** * \brief Convert from an integer sample rate to a78 FDF SFC field value * @param rate integer sample rate * @return fdf sfc field value */ byte_t sampleRateToFdfSfc(unsigned int rate); } #endif // AVCDEFINITIONS_H libffado-2.4.5/src/libavc/ccm/0000755000175000001440000000000014206145612015452 5ustar jwoitheuserslibffado-2.4.5/src/libavc/ccm/avc_signal_source.cpp0000644000175000001440000001563214206145246021656 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_signal_source.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "libutil/ByteSwap.h" #include using namespace std; #define AVC1394_CMD_SIGNAL_SOURCE 0x1A namespace AVC { SignalUnitAddress::SignalUnitAddress() : m_plugId( ePI_Invalid ) { } bool SignalUnitAddress::serialize( Util::Cmd::IOSSerialize& se ) { byte_t reserved = 0xff; se.write( reserved, "SignalUnitAddress" ); se.write( m_plugId, "SignalUnitAddress plugId" ); return true; } bool SignalUnitAddress::deserialize( Util::Cmd::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( eST_Reserved ) , m_subunitId( AVC1394_SUBUNIT_ID_RESERVED ) , m_plugId( ePI_Invalid ) { } bool SignalSubunitAddress::serialize( Util::Cmd::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::Cmd::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::Cmd::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::Cmd::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; } } libffado-2.4.5/src/libavc/ccm/avc_signal_source.h0000644000175000001440000000556114206145246021323 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCSIGNALSOURCE_H #define AVCSIGNALSOURCE_H #include "../general/avc_generic.h" #include "../avc_definitions.h" 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::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual SignalUnitAddress* clone() const; byte_t m_plugId; }; class SignalSubunitAddress: public SignalAddress { public: SignalSubunitAddress(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual SignalSubunitAddress* clone() const; byte_t m_subunitType; byte_t m_subunitId; byte_t m_plugId; }; class SignalSourceCmd: public AVCCommand { public: enum eOutputStatus { eOS_Effective = 0, eOS_NotEffective = 1, eOS_InsufficientResource = 2, eOS_Ready = 3, eOS_Output = 4, }; public: SignalSourceCmd( Ieee1394Service& ieee1394service ); virtual ~SignalSourceCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::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 libffado-2.4.5/src/libavc/descriptors/0000755000175000001440000000000014206145612017251 5ustar jwoitheuserslibffado-2.4.5/src/libavc/descriptors/avc_descriptor.cpp0000644000175000001440000004016014206145246022770 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "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" #include 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::Cmd::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::Cmd::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 %zd\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::Cmd::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::Cmd::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::show() { } 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::Cmd::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::Cmd::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::Cmd::IISDeserialize& de, uint16_t *type ) { return de.peek(type, 2); } bool AVCInfoBlock::peekBlockLength( Util::Cmd::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(); } void AVCInfoBlock::show() { } // --------- //FIXME: find out the correct id for this AVCRawTextInfoBlock::AVCRawTextInfoBlock( ) : AVCInfoBlock( 0x000A ) {} AVCRawTextInfoBlock::~AVCRawTextInfoBlock( ) { clear(); } bool AVCRawTextInfoBlock::clear() { return true; } bool AVCRawTextInfoBlock::serialize( Util::Cmd::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::Cmd::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::Cmd::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::Cmd::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; } } libffado-2.4.5/src/libavc/descriptors/avc_descriptor.h0000644000175000001440000001407014206145246022436 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** * 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 class Ieee1394Service; namespace AVC { class Unit; class Subunit; /** * 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::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::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::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::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(); virtual void show(); 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::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); static bool peekBlockType( Util::Cmd::IISDeserialize& de, uint16_t * ); static bool peekBlockLength( Util::Cmd::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; virtual void show(); private: }; class AVCRawTextInfoBlock : public AVCInfoBlock { public: virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::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::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::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 libffado-2.4.5/src/libavc/descriptors/avc_descriptor_cmd.cpp0000644000175000001440000001463014206145246023616 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_descriptor_cmd.h" #include "avc_descriptor.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "libutil/ByteSwap.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::Cmd::IOSSerialize& se ) { AVCCommand::serialize( se ); if(m_specifier==NULL) { debugError("m_specifier==NULL\n"); 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::Cmd::IISDeserialize& de ) { AVCCommand::deserialize( de ); if(m_specifier==NULL) { debugError("m_specifier==NULL\n"); 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 %d\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::Cmd::IOSSerialize& se ) { AVCCommand::serialize( se ); if(m_specifier==NULL) { debugError("m_specifier==NULL\n"); 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::Cmd::IISDeserialize& de ) { AVCCommand::deserialize( de ); if(m_specifier==NULL) { debugError("m_specifier==NULL\n"); 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\n"); 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\n"); 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; } } } libffado-2.4.5/src/libavc/descriptors/avc_descriptor_cmd.h0000644000175000001440000000603114206145246023257 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCDESCRIPTORCMD_H #define AVCDESCRIPTORCMD_H #include "../general/avc_generic.h" namespace AVC { #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 #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::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::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::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::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 libffado-2.4.5/src/libavc/general/0000755000175000001440000000000014206145612016325 5ustar jwoitheuserslibffado-2.4.5/src/libavc/general/avc_connect.cpp0000644000175000001440000000270514206145246021322 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_connect.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "libutil/ByteSwap.h" #include using namespace std; namespace AVC { ConnectCmd::ConnectCmd(Ieee1394Service& ieee1394service) : AVCCommand( ieee1394service, AVC1394_CMD_CONNECT ) { } ConnectCmd::~ConnectCmd() { } bool ConnectCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= AVCCommand::serialize( se ); return result; } bool ConnectCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= AVCCommand::deserialize( de ); return result; } } libffado-2.4.5/src/libavc/general/avc_connect.h0000644000175000001440000000242614206145246020767 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCCONNECT_H #define AVCCONNECT_H #include "avc_generic.h" #define AVC1394_CMD_CONNECT 0x24 namespace AVC { class ConnectCmd: public AVCCommand { public: ConnectCmd(Ieee1394Service& ieee1394service); virtual ~ConnectCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "ConnectCmd"; } }; } #endif // AVCCONNECT_H libffado-2.4.5/src/libavc/general/avc_extended_cmd_generic.cpp0000644000175000001440000003664014206145246024015 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_extended_cmd_generic.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" namespace AVC { UnitPlugAddress::UnitPlugAddress( EPlugType plugType, plug_type_t plugId ) : m_plugType( plugType ) , m_plugId( plugId ) , m_reserved( 0xff ) { } UnitPlugAddress::~UnitPlugAddress() { } bool UnitPlugAddress::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_plugType, "UnitPlugAddress plugType" ); se.write( m_plugId, "UnitPlugAddress plugId" ); se.write( m_reserved, "UnitPlugAddress reserved" ); return true; } bool UnitPlugAddress::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_plugType ); de.read( &m_plugId ); de.read( &m_reserved ); return true; } UnitPlugAddress* UnitPlugAddress::clone() const { return new UnitPlugAddress( *this ); } //////////////////////////////////////////////////////////// SubunitPlugAddress::SubunitPlugAddress( plug_id_t plugId ) : m_plugId( plugId ) , m_reserved0( 0xff ) , m_reserved1( 0xff ) { } SubunitPlugAddress::~SubunitPlugAddress() { } bool SubunitPlugAddress::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_plugId, "SubunitPlugAddress plugId" ); se.write( m_reserved0, "SubunitPlugAddress reserved0" ); se.write( m_reserved1, "SubunitPlugAddress reserved1" ); return true; } bool SubunitPlugAddress::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_plugId ); de.read( &m_reserved0 ); de.read( &m_reserved1 ); return true; } SubunitPlugAddress* SubunitPlugAddress::clone() const { return new SubunitPlugAddress( *this ); } //////////////////////////////////////////////////////////// FunctionBlockPlugAddress::FunctionBlockPlugAddress( function_block_type_t functionBlockType, function_block_id_t functionBlockId, plug_id_t plugId ) : m_functionBlockType( functionBlockType ) , m_functionBlockId( functionBlockId ) , m_plugId( plugId ) { } FunctionBlockPlugAddress::~FunctionBlockPlugAddress() { } bool FunctionBlockPlugAddress::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_functionBlockType, "FunctionBlockPlugAddress functionBlockType" ); se.write( m_functionBlockId, "FunctionBlockPlugAddress functionBlockId" ); se.write( m_plugId, "FunctionBlockPlugAddress plugId" ); return true; } bool FunctionBlockPlugAddress::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_functionBlockType ); de.read( &m_functionBlockId ); de.read( &m_plugId ); return true; } FunctionBlockPlugAddress* FunctionBlockPlugAddress:: clone() const { return new FunctionBlockPlugAddress( *this ); } //////////////////////////////////////////////////////////// UndefinedPlugAddress::UndefinedPlugAddress() : m_reserved0( 0xff ) , m_reserved1( 0xff ) , m_reserved2( 0xff ) { } UndefinedPlugAddress::~UndefinedPlugAddress() { } bool UndefinedPlugAddress::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_reserved0, "UndefinedPlugAddress reserved0" ); se.write( m_reserved1, "UndefinedPlugAddress reserved1" ); se.write( m_reserved2, "UndefinedPlugAddress reserved2" ); return true; } bool UndefinedPlugAddress::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_reserved0 ); de.read( &m_reserved1 ); de.read( &m_reserved2 ); return true; } UndefinedPlugAddress* UndefinedPlugAddress:: clone() const { return new UndefinedPlugAddress( *this ); } //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// UnitPlugSpecificDataPlugAddress::UnitPlugSpecificDataPlugAddress( EPlugType plugType, plug_type_t plugId ) : m_plugType( plugType ) , m_plugId( plugId ) , m_reserved0( 0xff ) , m_reserved1( 0xff ) , m_reserved2( 0xff ) { } UnitPlugSpecificDataPlugAddress::~UnitPlugSpecificDataPlugAddress() { } bool UnitPlugSpecificDataPlugAddress::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_plugType, "UnitPlugSpecificDataPlugAddress plugType" ); se.write( m_plugId, "UnitPlugSpecificDataPlugAddress plugId" ); se.write( m_reserved0, "UnitPlugSpecificDataPlugAddress reserved0" ); se.write( m_reserved1, "UnitPlugSpecificDataPlugAddress reserved1" ); se.write( m_reserved2, "UnitPlugSpecificDataPlugAddress reserved2" ); return true; } bool UnitPlugSpecificDataPlugAddress::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_plugType ); de.read( &m_plugId ); de.read( &m_reserved0 ); de.read( &m_reserved1 ); de.read( &m_reserved2 ); return true; } UnitPlugSpecificDataPlugAddress* UnitPlugSpecificDataPlugAddress::clone() const { return new UnitPlugSpecificDataPlugAddress( *this ); } //////////////////////////////////////////////////////////// SubunitPlugSpecificDataPlugAddress::SubunitPlugSpecificDataPlugAddress( ESubunitType subunitType, subunit_id_t subunitId, plug_id_t plugId ) : m_subunitType( subunitType ) , m_subunitId( subunitId ) , m_plugId( plugId ) , m_reserved0( 0xff ) , m_reserved1( 0xff ) { } SubunitPlugSpecificDataPlugAddress::~SubunitPlugSpecificDataPlugAddress() { } bool SubunitPlugSpecificDataPlugAddress::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_subunitType, "SubunitPlugSpecificDataPlugAddress subunitType" ); se.write( m_subunitId, "SubunitPlugSpecificDataPlugAddress subunitId" ); se.write( m_plugId, "SubunitPlugSpecificDataPlugAddress plugId" ); se.write( m_reserved0, "SubunitPlugSpecificDataPlugAddress reserved0" ); se.write( m_reserved1, "SubunitPlugSpecificDataPlugAddress reserved1" ); return true; } bool SubunitPlugSpecificDataPlugAddress::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_subunitType ); de.read( &m_subunitId ); de.read( &m_plugId ); de.read( &m_reserved0 ); de.read( &m_reserved1 ); return true; } SubunitPlugSpecificDataPlugAddress* SubunitPlugSpecificDataPlugAddress::clone() const { return new SubunitPlugSpecificDataPlugAddress( *this ); } //////////////////////////////////////////////////////////// FunctionBlockPlugSpecificDataPlugAddress::FunctionBlockPlugSpecificDataPlugAddress( ESubunitType subunitType, subunit_id_t subunitId, function_block_type_t functionBlockType, function_block_id_t functionBlockId, plug_id_t plugId ) : m_subunitType( subunitType ) , m_subunitId( subunitId ) , m_functionBlockType( functionBlockType ) , m_functionBlockId( functionBlockId ) , m_plugId( plugId ) { } FunctionBlockPlugSpecificDataPlugAddress::~FunctionBlockPlugSpecificDataPlugAddress() { } bool FunctionBlockPlugSpecificDataPlugAddress::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_subunitType, "FunctionPlugSpecificDataBlockPlugAddress subunitType" ); se.write( m_subunitId, "FunctionPlugSpecificDataBlockPlugAddress subunitId" ); se.write( m_functionBlockType, "FunctionBlockPlugSpecificDataPlugAddress functionBlockType" ); se.write( m_functionBlockId, "FunctionBlockPlugSpecificDataPlugAddress functionBlockId" ); se.write( m_plugId, "FunctionBlockPlugSpecificDataPlugAddress plugId" ); return true; } bool FunctionBlockPlugSpecificDataPlugAddress::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_subunitType ); de.read( &m_subunitId ); de.read( &m_functionBlockType ); de.read( &m_functionBlockId ); de.read( &m_plugId ); return true; } FunctionBlockPlugSpecificDataPlugAddress* FunctionBlockPlugSpecificDataPlugAddress:: clone() const { return new FunctionBlockPlugSpecificDataPlugAddress( *this ); } //////////////////////////////////////////////////////////// UndefinedPlugSpecificDataPlugAddress::UndefinedPlugSpecificDataPlugAddress() : m_reserved0( 0xff ) , m_reserved1( 0xff ) , m_reserved2( 0xff ) , m_reserved3( 0xff ) , m_reserved4( 0xff ) { } UndefinedPlugSpecificDataPlugAddress::~UndefinedPlugSpecificDataPlugAddress() { } bool UndefinedPlugSpecificDataPlugAddress::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_reserved0, "UndefinedPlugAddress reserved0" ); se.write( m_reserved1, "UndefinedPlugAddress reserved1" ); se.write( m_reserved2, "UndefinedPlugAddress reserved2" ); se.write( m_reserved3, "UndefinedPlugAddress reserved3" ); se.write( m_reserved4, "UndefinedPlugAddress reserved4" ); return true; } bool UndefinedPlugSpecificDataPlugAddress::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_reserved0 ); de.read( &m_reserved1 ); de.read( &m_reserved2 ); de.read( &m_reserved3 ); de.read( &m_reserved4 ); return true; } UndefinedPlugSpecificDataPlugAddress* UndefinedPlugSpecificDataPlugAddress:: clone() const { return new UndefinedPlugSpecificDataPlugAddress( *this ); } //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// PlugAddress::PlugAddress( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode, UnitPlugAddress& unitPlugAddress ) : m_plugDirection( plugDirection ) , m_addressMode( plugAddressMode ) , m_plugAddressData( new UnitPlugAddress( unitPlugAddress ) ) { } PlugAddress::PlugAddress( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode, SubunitPlugAddress& subUnitPlugAddress ) : m_plugDirection( plugDirection ) , m_addressMode( plugAddressMode ) , m_plugAddressData( new SubunitPlugAddress( subUnitPlugAddress ) ) { } PlugAddress::PlugAddress( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode, FunctionBlockPlugAddress& functionBlockPlugAddress ) : m_plugDirection( plugDirection ) , m_addressMode( plugAddressMode ) , m_plugAddressData( new FunctionBlockPlugAddress( functionBlockPlugAddress ) ) { } PlugAddress::PlugAddress() : m_plugDirection( ePD_Undefined ) , m_addressMode( ePAM_Undefined ) , m_plugAddressData( new UndefinedPlugAddress() ) { } PlugAddress::PlugAddress( const PlugAddress& pa ) : m_plugDirection( pa.m_plugDirection ) , m_addressMode( pa.m_addressMode ) , m_plugAddressData( dynamic_cast( pa.m_plugAddressData->clone() ) ) { } PlugAddress::~PlugAddress() { delete m_plugAddressData; m_plugAddressData = 0; } bool PlugAddress::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_plugDirection, "PlugAddress plugDirection" ); se.write( m_addressMode, "PlugAddress addressMode" ); return m_plugAddressData->serialize( se ); } bool PlugAddress::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_plugDirection ); de.read( &m_addressMode ); return m_plugAddressData->deserialize( de ); } PlugAddress* PlugAddress::clone() const { return new PlugAddress( *this ); } const char* plugAddressDirectionStrings[] = { "Input", "Output", "Undefined", }; const char* plugAddressAddressModeStrings[] = { "Unit", "Subunit", "FunctionBlock", "Undefined", }; const char* plugAddressPlugDirectionToString( PlugAddress::EPlugDirection direction ) { if ( direction > PlugAddress::ePD_Output ) { direction = PlugAddress::ePD_Undefined; } return plugAddressDirectionStrings[direction]; } const char* plugAddressAddressModeToString( PlugAddress::EPlugAddressMode mode ) { if ( mode > PlugAddress::ePAM_FunctionBlock ) { mode = PlugAddress::ePAM_FunctionBlock; } return plugAddressAddressModeStrings[mode]; } //////////////////////////////////////////////////////////// PlugAddressSpecificData::PlugAddressSpecificData( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode, UnitPlugSpecificDataPlugAddress& unitPlugAddress ) : m_plugDirection( plugDirection ) , m_addressMode( plugAddressMode ) , m_plugAddressData( new UnitPlugSpecificDataPlugAddress( unitPlugAddress ) ) { } PlugAddressSpecificData::PlugAddressSpecificData( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode, SubunitPlugSpecificDataPlugAddress& subUnitPlugAddress ) : m_plugDirection( plugDirection ) , m_addressMode( plugAddressMode ) , m_plugAddressData( new SubunitPlugSpecificDataPlugAddress( subUnitPlugAddress ) ) { } PlugAddressSpecificData::PlugAddressSpecificData( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode, FunctionBlockPlugSpecificDataPlugAddress& functionBlockPlugAddress ) : m_plugDirection( plugDirection ) , m_addressMode( plugAddressMode ) , m_plugAddressData( new FunctionBlockPlugSpecificDataPlugAddress( functionBlockPlugAddress ) ) { } PlugAddressSpecificData::PlugAddressSpecificData( const PlugAddressSpecificData& pa ) : m_plugDirection( pa.m_plugDirection ) , m_addressMode( pa.m_addressMode ) , m_plugAddressData( dynamic_cast( pa.m_plugAddressData->clone() ) ) { } PlugAddressSpecificData::~PlugAddressSpecificData() { delete m_plugAddressData; m_plugAddressData = 0; } bool PlugAddressSpecificData::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_plugDirection, "PlugAddressSpecificData plugDirection" ); se.write( m_addressMode, "PlugAddressSpecificData addressMode" ); return m_plugAddressData->serialize( se ); } bool PlugAddressSpecificData::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_plugDirection ); de.read( &m_addressMode ); if ( m_plugAddressData ) { delete m_plugAddressData; m_plugAddressData = 0; } switch ( m_addressMode ) { case ePAM_Unit: m_plugAddressData = new UnitPlugSpecificDataPlugAddress( UnitPlugSpecificDataPlugAddress::ePT_PCR, 0xff ); break; case ePAM_Subunit: m_plugAddressData = new SubunitPlugSpecificDataPlugAddress( eST_Reserved, 0xff, 0xff ); break; case ePAM_FunctionBlock: m_plugAddressData = new FunctionBlockPlugSpecificDataPlugAddress( eST_Reserved, 0xff, 0xff, 0xff, 0xff); break; default: m_plugAddressData = new UndefinedPlugSpecificDataPlugAddress(); } return m_plugAddressData->deserialize( de ); } PlugAddressSpecificData* PlugAddressSpecificData::clone() const { return new PlugAddressSpecificData( *this ); } } libffado-2.4.5/src/libavc/general/avc_extended_cmd_generic.h0000644000175000001440000002223414206145246023454 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCEXTENDEDCMDGENERIC_H #define AVCEXTENDEDCMDGENERIC_H #include "avc_generic.h" namespace AVC { //////////////////////////////////////////////////////////// class PlugAddressData : public IBusData { }; //////////////////////////////////////////////////////////// class UnitPlugAddress : public PlugAddressData { public: enum EPlugType { ePT_PCR = 0x00, ePT_ExternalPlug = 0x01, ePT_AsynchronousPlug = 0x02, ePT_Unknown = 0xff, }; UnitPlugAddress( EPlugType plugType, plug_type_t plugId ); virtual ~UnitPlugAddress(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual UnitPlugAddress* clone() const; plug_id_t m_plugType; plug_type_t m_plugId; reserved_t m_reserved; }; //////////////////////////////////////////////////////////// class SubunitPlugAddress : public PlugAddressData { public: SubunitPlugAddress( plug_id_t plugId ); virtual ~SubunitPlugAddress(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual SubunitPlugAddress* clone() const; plug_id_t m_plugId; reserved_t m_reserved0; reserved_t m_reserved1; }; //////////////////////////////////////////////////////////// class FunctionBlockPlugAddress : public PlugAddressData { public: FunctionBlockPlugAddress( function_block_type_t functionBlockType, function_block_id_t functionBlockId, plug_id_t plugId ); virtual ~FunctionBlockPlugAddress(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual FunctionBlockPlugAddress* clone() const; function_block_type_t m_functionBlockType; function_block_id_t m_functionBlockId; plug_id_t m_plugId; }; //////////////////////////////////////////////////////////// class UndefinedPlugAddress : public PlugAddressData { public: UndefinedPlugAddress(); virtual ~UndefinedPlugAddress(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual UndefinedPlugAddress* clone() const; reserved_t m_reserved0; reserved_t m_reserved1; reserved_t m_reserved2; }; //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// class UnitPlugSpecificDataPlugAddress : public PlugAddressData { public: enum EPlugType { ePT_PCR = 0x00, ePT_ExternalPlug = 0x01, ePT_AsynchronousPlug = 0x02, }; UnitPlugSpecificDataPlugAddress( EPlugType plugType, plug_type_t plugId ); virtual ~UnitPlugSpecificDataPlugAddress(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual UnitPlugSpecificDataPlugAddress* clone() const; plug_type_t m_plugType; plug_id_t m_plugId; reserved_t m_reserved0; reserved_t m_reserved1; reserved_t m_reserved2; }; //////////////////////////////////////////////////////////// class SubunitPlugSpecificDataPlugAddress : public PlugAddressData { public: SubunitPlugSpecificDataPlugAddress( ESubunitType subunitType, subunit_id_t subunitId, plug_id_t plugId ); virtual ~SubunitPlugSpecificDataPlugAddress(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual SubunitPlugSpecificDataPlugAddress* clone() const; subunit_type_t m_subunitType; subunit_id_t m_subunitId; plug_id_t m_plugId; reserved_t m_reserved0; reserved_t m_reserved1; }; //////////////////////////////////////////////////////////// class FunctionBlockPlugSpecificDataPlugAddress : public PlugAddressData { public: FunctionBlockPlugSpecificDataPlugAddress( ESubunitType subunitType, subunit_id_t subunitId, function_block_type_t functionBlockType, function_block_id_t functionBlockId, plug_id_t plugId); virtual ~FunctionBlockPlugSpecificDataPlugAddress(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual FunctionBlockPlugSpecificDataPlugAddress* clone() const; subunit_type_t m_subunitType; subunit_id_t m_subunitId; function_block_type_t m_functionBlockType; function_block_id_t m_functionBlockId; plug_id_t m_plugId; }; //////////////////////////////////////////////////////////// class UndefinedPlugSpecificDataPlugAddress : public PlugAddressData { public: UndefinedPlugSpecificDataPlugAddress(); virtual ~UndefinedPlugSpecificDataPlugAddress(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual UndefinedPlugSpecificDataPlugAddress* clone() const; reserved_t m_reserved0; reserved_t m_reserved1; reserved_t m_reserved2; reserved_t m_reserved3; reserved_t m_reserved4; }; //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// class PlugAddress : public IBusData { public: enum EPlugDirection { ePD_Input = 0x00, ePD_Output = 0x01, ePD_Undefined = 0xff, }; enum EPlugAddressMode { ePAM_Unit = 0x00, ePAM_Subunit = 0x01, ePAM_FunctionBlock = 0x02, ePAM_Undefined = 0xff, }; PlugAddress( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode, UnitPlugAddress& unitPlugAddress ); PlugAddress( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode, SubunitPlugAddress& subUnitPlugAddress ); PlugAddress( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode, FunctionBlockPlugAddress& functionBlockPlugAddress ); PlugAddress( ); // undefined plug address PlugAddress( const PlugAddress& pa ); virtual ~PlugAddress(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual PlugAddress* clone() const; plug_direction_t m_plugDirection; plug_address_mode_t m_addressMode; PlugAddressData* m_plugAddressData; }; const char* plugAddressPlugDirectionToString( PlugAddress::EPlugDirection direction ); const char* plugAddressAddressModeToString( PlugAddress::EPlugAddressMode mode ); //////////////////////////////////////////////////////////// class PlugAddressSpecificData : public IBusData { public: enum EPlugDirection { ePD_Input = 0x00, ePD_Output = 0x01, }; enum EPlugAddressMode { ePAM_Unit = 0x00, ePAM_Subunit = 0x01, ePAM_FunctionBlock = 0x02, ePAM_Undefined = 0xff, }; PlugAddressSpecificData( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode, UnitPlugSpecificDataPlugAddress& unitPlugAddress ); PlugAddressSpecificData( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode, SubunitPlugSpecificDataPlugAddress& subUnitPlugAddress ); PlugAddressSpecificData( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode, FunctionBlockPlugSpecificDataPlugAddress& functionBlockPlugAddress ); PlugAddressSpecificData( EPlugDirection plugDirection, EPlugAddressMode plugAddressMode ); PlugAddressSpecificData( const PlugAddressSpecificData& pa ); virtual ~PlugAddressSpecificData(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual PlugAddressSpecificData* clone() const; plug_direction_t m_plugDirection; plug_address_mode_t m_addressMode; PlugAddressData* m_plugAddressData; }; } #endif libffado-2.4.5/src/libavc/general/avc_extended_plug_info.cpp0000644000175000001440000005650114206145246023536 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_extended_plug_info.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "libutil/ByteSwap.h" #include #include #include using namespace std; namespace AVC { ///////////////////////////////////////// ///////////////////////////////////////// ExtendedPlugInfoPlugTypeSpecificData::ExtendedPlugInfoPlugTypeSpecificData( EExtendedPlugInfoPlugType ePlugType ) : IBusData() , m_plugType( ePlugType ) { } ExtendedPlugInfoPlugTypeSpecificData::~ExtendedPlugInfoPlugTypeSpecificData() { } bool ExtendedPlugInfoPlugTypeSpecificData::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_plugType, "ExtendedPlugInfoPlugTypeSpecificData plugType" ); return true; } bool ExtendedPlugInfoPlugTypeSpecificData::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_plugType ); return true; } ExtendedPlugInfoPlugTypeSpecificData* ExtendedPlugInfoPlugTypeSpecificData::clone() const { return new ExtendedPlugInfoPlugTypeSpecificData( *this ); } const char* extendedPlugInfoPlugTypeStrings[] = { "IsoStream", "AsyncStream", "Midi", "Sync", "Analog", "Digital", "Unknown", }; const char* extendedPlugInfoPlugTypeToString( plug_type_t plugType ) { if ( plugType > sizeof( extendedPlugInfoPlugTypeStrings ) ) { return "Unknown"; } else { return extendedPlugInfoPlugTypeStrings[plugType]; } } ///////////////////////////////////////// ///////////////////////////////////////// ExtendedPlugInfoPlugNameSpecificData::ExtendedPlugInfoPlugNameSpecificData() : IBusData() { } ExtendedPlugInfoPlugNameSpecificData::~ExtendedPlugInfoPlugNameSpecificData() { } bool ExtendedPlugInfoPlugNameSpecificData::serialize( Util::Cmd::IOSSerialize& se ) { byte_t length = strlen( m_name.c_str() ); se.write( length, "ExtendedPlugInfoPlugNameSpecificData: string length" ); for ( unsigned int i = 0; i < length; ++i ) { se.write( static_cast( m_name[i] ), "ExtendedPlugInfoPlugNameSpecificData: char" ); } return true; } bool ExtendedPlugInfoPlugNameSpecificData::deserialize( Util::Cmd::IISDeserialize& de ) { byte_t length; de.read( &length ); m_name.clear(); char* name; // note that the pointer returned by de.read is not valid outside this function // but since we assign it to m_name it's not a problem since the contents are copied de.read( &name, length ); m_name = name; return true; } ExtendedPlugInfoPlugNameSpecificData* ExtendedPlugInfoPlugNameSpecificData::clone() const { return new ExtendedPlugInfoPlugNameSpecificData( *this ); } ///////////////////////////////////////// ///////////////////////////////////////// ExtendedPlugInfoPlugNumberOfChannelsSpecificData::ExtendedPlugInfoPlugNumberOfChannelsSpecificData() : IBusData() , m_nrOfChannels( 0 ) { } ExtendedPlugInfoPlugNumberOfChannelsSpecificData::~ExtendedPlugInfoPlugNumberOfChannelsSpecificData() { } bool ExtendedPlugInfoPlugNumberOfChannelsSpecificData::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_nrOfChannels, "ExtendedPlugInfoPlugNumberOfChannelsSpecificData: " "number of channels" ); return true; } bool ExtendedPlugInfoPlugNumberOfChannelsSpecificData::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_nrOfChannels ); return true; } ExtendedPlugInfoPlugNumberOfChannelsSpecificData* ExtendedPlugInfoPlugNumberOfChannelsSpecificData::clone() const { return new ExtendedPlugInfoPlugNumberOfChannelsSpecificData( *this ); } ///////////////////////////////////////// ///////////////////////////////////////// ExtendedPlugInfoPlugChannelPositionSpecificData::ExtendedPlugInfoPlugChannelPositionSpecificData() : IBusData() , m_nrOfClusters( 0 ) { } ExtendedPlugInfoPlugChannelPositionSpecificData::~ExtendedPlugInfoPlugChannelPositionSpecificData() { } bool ExtendedPlugInfoPlugChannelPositionSpecificData::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_nrOfClusters, "ExtendedPlugInfoPlugChannelPositionSpecificData: " "number of clusters" ); for ( ClusterInfoVector::const_iterator it = m_clusterInfos.begin(); it != m_clusterInfos.end(); ++it ) { const ClusterInfo* clusterInfo = &( *it ); se.write( clusterInfo->m_nrOfChannels, "ExtendedPlugInfoPlugChannelPositionSpecificData: " "number of channels" ); for ( ChannelInfoVector::const_iterator cit = clusterInfo->m_channelInfos.begin(); cit != clusterInfo->m_channelInfos.end(); ++cit ) { const ChannelInfo* channelInfo = &( *cit ); se.write( channelInfo->m_streamPosition, "ExtendedPlugInfoPlugChannelPositionSpecificData: " "stream position" ); se.write( channelInfo->m_location, "ExtendedPlugInfoPlugChannelPositionSpecificData: " "location" ); } } return true; } bool ExtendedPlugInfoPlugChannelPositionSpecificData::deserialize( Util::Cmd::IISDeserialize& de ) { m_clusterInfos.clear(); de.read( &m_nrOfClusters ); for ( int i = 0; i < m_nrOfClusters; ++i ) { ClusterInfo clusterInfo; de.read ( &clusterInfo.m_nrOfChannels ); for ( int j = 0; j < clusterInfo.m_nrOfChannels; ++j ) { ChannelInfo channelInfo; de.read( &channelInfo.m_streamPosition ); de.read( &channelInfo.m_location ); clusterInfo.m_channelInfos.push_back( channelInfo ); } m_clusterInfos.push_back( clusterInfo ); } return true; } ExtendedPlugInfoPlugChannelPositionSpecificData* ExtendedPlugInfoPlugChannelPositionSpecificData::clone() const { return new ExtendedPlugInfoPlugChannelPositionSpecificData( *this ); } ///////////////////////////////////////// ///////////////////////////////////////// ExtendedPlugInfoPlugChannelNameSpecificData::ExtendedPlugInfoPlugChannelNameSpecificData() : IBusData() , m_streamPosition( 0 ) , m_stringLength( 0xff ) { } ExtendedPlugInfoPlugChannelNameSpecificData::~ExtendedPlugInfoPlugChannelNameSpecificData() { } bool ExtendedPlugInfoPlugChannelNameSpecificData::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_streamPosition, "ExtendedPlugInfoPlugChannelNameSpecificData: stream position" ); se.write( m_stringLength, "ExtendedPlugInfoPlugChannelNameSpecificData: string length" ); for ( unsigned int i = 0; i < m_plugChannelName.size(); ++i ) { se.write( static_cast( m_plugChannelName[i] ), "ExtendedPlugInfoPlugChannelNameSpecificData: char" ); } return true; } bool ExtendedPlugInfoPlugChannelNameSpecificData::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_streamPosition ); de.read( &m_stringLength ); char* name = new char[m_stringLength+1]; for ( int i = 0; i < m_stringLength; ++i ) { byte_t c; de.read( &c ); // \todo do correct encoding if ( c == '&' ) { c = '+'; } name[i] = c; } name[m_stringLength] = '\0'; m_plugChannelName = name; delete[] name; return true; } ExtendedPlugInfoPlugChannelNameSpecificData* ExtendedPlugInfoPlugChannelNameSpecificData::clone() const { return new ExtendedPlugInfoPlugChannelNameSpecificData( *this ); } ///////////////////////////////////////// ///////////////////////////////////////// ExtendedPlugInfoPlugInputSpecificData::ExtendedPlugInfoPlugInputSpecificData() : IBusData() { UnitPlugSpecificDataPlugAddress unitPlug( UnitPlugSpecificDataPlugAddress::ePT_PCR, 0x00 ); m_plugAddress = new PlugAddressSpecificData( PlugAddressSpecificData::ePD_Output, PlugAddressSpecificData::ePAM_Unit, unitPlug ); } ExtendedPlugInfoPlugInputSpecificData::ExtendedPlugInfoPlugInputSpecificData(const ExtendedPlugInfoPlugInputSpecificData& rhs ) { m_plugAddress = rhs.m_plugAddress->clone(); } ExtendedPlugInfoPlugInputSpecificData::~ExtendedPlugInfoPlugInputSpecificData() { delete m_plugAddress; m_plugAddress = 0; } bool ExtendedPlugInfoPlugInputSpecificData::serialize( Util::Cmd::IOSSerialize& se ) { if ( m_plugAddress ) { return m_plugAddress->serialize( se ); } else { return false; } } bool ExtendedPlugInfoPlugInputSpecificData::deserialize( Util::Cmd::IISDeserialize& de ) { return m_plugAddress->deserialize( de ); } ExtendedPlugInfoPlugInputSpecificData* ExtendedPlugInfoPlugInputSpecificData::clone() const { return new ExtendedPlugInfoPlugInputSpecificData( *this ); } ///////////////////////////////////////// ///////////////////////////////////////// ExtendedPlugInfoPlugOutputSpecificData::ExtendedPlugInfoPlugOutputSpecificData() : IBusData() , m_nrOfOutputPlugs( 0 ) { } ExtendedPlugInfoPlugOutputSpecificData::ExtendedPlugInfoPlugOutputSpecificData( const ExtendedPlugInfoPlugOutputSpecificData& rhs) : IBusData() , m_nrOfOutputPlugs( rhs.m_nrOfOutputPlugs ) { for ( PlugAddressVector::const_iterator it = rhs.m_outputPlugAddresses.begin(); it != rhs.m_outputPlugAddresses.end(); ++it ) { m_outputPlugAddresses.push_back( ( *it )->clone() ); } } ExtendedPlugInfoPlugOutputSpecificData::~ExtendedPlugInfoPlugOutputSpecificData() { for ( PlugAddressVector::iterator it = m_outputPlugAddresses.begin(); it != m_outputPlugAddresses.end(); ++it ) { delete *it; } } bool ExtendedPlugInfoPlugOutputSpecificData::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_nrOfOutputPlugs, "ExtendedPlugInfoPlugOutputSpecificData: number of output plugs" ); for ( PlugAddressVector::const_iterator it = m_outputPlugAddresses.begin(); it != m_outputPlugAddresses.end(); ++it ) { ( *it )->serialize( se ); } return true; } bool ExtendedPlugInfoPlugOutputSpecificData::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_nrOfOutputPlugs ); for ( int i = 0; i < m_nrOfOutputPlugs; ++i ) { UnitPlugSpecificDataPlugAddress unitPlug( UnitPlugSpecificDataPlugAddress::ePT_PCR, 0x00 ); PlugAddressSpecificData* plugAddress = new PlugAddressSpecificData( PlugAddressSpecificData::ePD_Output, PlugAddressSpecificData::ePAM_Unit, unitPlug ); if ( !plugAddress->deserialize( de ) ) { return false; } m_outputPlugAddresses.push_back( plugAddress ); } return true; } ExtendedPlugInfoPlugOutputSpecificData* ExtendedPlugInfoPlugOutputSpecificData::clone() const { return new ExtendedPlugInfoPlugOutputSpecificData( *this ); } ///////////////////////////////////////// ///////////////////////////////////////// ExtendedPlugInfoClusterInfoSpecificData::ExtendedPlugInfoClusterInfoSpecificData() : IBusData() , m_clusterIndex( 0 ) , m_portType( ePT_NoType ) , m_stringLength( 0xff ) { } ExtendedPlugInfoClusterInfoSpecificData::~ExtendedPlugInfoClusterInfoSpecificData() { } bool ExtendedPlugInfoClusterInfoSpecificData::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_clusterIndex, "ExtendedPlugInfoClusterInfoSpecificData: cluster index" ); se.write( m_portType, "ExtendedPlugInfoClusterInfoSpecificData: port type" ); se.write( m_stringLength, "ExtendedPlugInfoClusterInfoSpecificData: string length" ); for ( unsigned int i = 0; i < m_clusterName.length(); ++i ) { se.write( static_cast( m_clusterName[i] ), "ExtendedPlugInfoClusterInfoSpecificData: char" ); } return true; } bool ExtendedPlugInfoClusterInfoSpecificData::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_clusterIndex ); de.read( &m_portType ); de.read( &m_stringLength ); char* name = new char[m_stringLength+1]; for ( int i = 0; i < m_stringLength; ++i ) { byte_t c; de.read( &c ); // \todo do correct encoding if ( c == '&' ) { c = '+'; } name[i] = c; } name[m_stringLength] = '\0'; m_clusterName = name; delete[] name; return true; } ExtendedPlugInfoClusterInfoSpecificData* ExtendedPlugInfoClusterInfoSpecificData::clone() const { return new ExtendedPlugInfoClusterInfoSpecificData( *this ); } const char* extendedPlugInfoPortTypeStrings[] = { "Speaker", "Headphone", "Microphone", "Line", "SPDIF", "ADAT", "TDIF", "MADI", "Analog", "Digital", "MIDI", }; const char* extendedPlugInfoClusterInfoPortTypeToString( port_type_t portType ) { if ( portType > ( ( sizeof( extendedPlugInfoPortTypeStrings ) ) / ( sizeof( extendedPlugInfoPortTypeStrings[0] ) ) ) ) { return "Unknown"; } else { return extendedPlugInfoPortTypeStrings[portType]; } } ///////////////////////////////////////// ///////////////////////////////////////// ///////////////////////////////////////// ///////////////////////////////////////// ExtendedPlugInfoInfoType::ExtendedPlugInfoInfoType(EInfoType eInfoType) : IBusData() , m_infoType( eInfoType ) , m_plugType( 0 ) , m_plugName( 0 ) , m_plugNrOfChns( 0 ) , m_plugChannelPosition( 0 ) , m_plugChannelName( 0 ) , m_plugInput( 0 ) , m_plugOutput( 0 ) , m_plugClusterInfo( 0 ) { } ExtendedPlugInfoInfoType::ExtendedPlugInfoInfoType( const ExtendedPlugInfoInfoType& rhs ) : IBusData() , m_infoType( rhs.m_infoType ) , m_plugType( 0 ) , m_plugName( 0 ) , m_plugNrOfChns( 0 ) , m_plugChannelPosition( 0 ) , m_plugChannelName( 0 ) , m_plugInput( 0 ) , m_plugOutput( 0 ) , m_plugClusterInfo( 0 ) { switch( m_infoType ) { case eIT_PlugType: m_plugType = new ExtendedPlugInfoPlugTypeSpecificData( *rhs.m_plugType ); break; case eIT_PlugName: m_plugName = new ExtendedPlugInfoPlugNameSpecificData( *rhs.m_plugName ); break; case eIT_NoOfChannels: m_plugNrOfChns = new ExtendedPlugInfoPlugNumberOfChannelsSpecificData( *rhs.m_plugNrOfChns ); break; case eIT_ChannelPosition: m_plugChannelPosition = new ExtendedPlugInfoPlugChannelPositionSpecificData( *rhs.m_plugChannelPosition ); break; case eIT_ChannelName: m_plugChannelName = new ExtendedPlugInfoPlugChannelNameSpecificData( *rhs.m_plugChannelName ); break; case eIT_PlugInput: m_plugInput = new ExtendedPlugInfoPlugInputSpecificData( *rhs.m_plugInput ); break; case eIT_PlugOutput: m_plugOutput = new ExtendedPlugInfoPlugOutputSpecificData( *rhs.m_plugOutput ); break; case eIT_ClusterInfo: m_plugClusterInfo = new ExtendedPlugInfoClusterInfoSpecificData( *rhs.m_plugClusterInfo ); break; } } ExtendedPlugInfoInfoType::~ExtendedPlugInfoInfoType() { delete( m_plugType ); delete( m_plugName ); delete( m_plugNrOfChns ); delete( m_plugChannelPosition ); delete( m_plugChannelName ); delete( m_plugInput ); delete( m_plugOutput ); delete( m_plugClusterInfo ); } bool ExtendedPlugInfoInfoType::initialize() { switch ( m_infoType ) { case eIT_PlugType: m_plugType = new ExtendedPlugInfoPlugTypeSpecificData; break; case eIT_PlugName: m_plugName = new ExtendedPlugInfoPlugNameSpecificData; break; case eIT_NoOfChannels: m_plugNrOfChns = new ExtendedPlugInfoPlugNumberOfChannelsSpecificData; break; case eIT_ChannelPosition: m_plugChannelPosition = new ExtendedPlugInfoPlugChannelPositionSpecificData; break; case eIT_ChannelName: m_plugChannelName = new ExtendedPlugInfoPlugChannelNameSpecificData; break; case eIT_PlugInput: m_plugInput = new ExtendedPlugInfoPlugInputSpecificData; break; case eIT_PlugOutput: m_plugOutput = new ExtendedPlugInfoPlugOutputSpecificData; break; case eIT_ClusterInfo: m_plugClusterInfo = new ExtendedPlugInfoClusterInfoSpecificData; break; default: return false; } return true; } bool ExtendedPlugInfoInfoType::serialize( Util::Cmd::IOSSerialize& se ) { // XXX \todo improve Util::Cmd::IOSSerialize::write interface char* buf; asprintf( &buf, "ExtendedPlugInfoInfoType infoType (%s)", extendedPlugInfoInfoTypeToString( m_infoType ) ); se.write( m_infoType, buf ); free(buf); switch ( m_infoType ) { case eIT_PlugType: if ( m_plugType ) { m_plugType->serialize( se ); } break; case eIT_PlugName: if ( m_plugName ) { m_plugName->serialize( se ); } break; case eIT_NoOfChannels: if ( m_plugNrOfChns ) { m_plugNrOfChns->serialize( se ); } break; case eIT_ChannelPosition: if ( m_plugChannelPosition ) { m_plugChannelPosition->serialize( se ); } break; case eIT_ChannelName: if ( m_plugChannelName ) { m_plugChannelName->serialize( se ); } break; case eIT_PlugInput: if ( m_plugInput ) { m_plugInput->serialize( se ); } break; case eIT_PlugOutput: if ( m_plugOutput ) { m_plugOutput->serialize( se ); } break; case eIT_ClusterInfo: if ( m_plugClusterInfo ) { m_plugClusterInfo->serialize( se ); } break; default: return false; } return true; } bool ExtendedPlugInfoInfoType::deserialize( Util::Cmd::IISDeserialize& de ) { bool status = false; de.read( &m_infoType ); switch ( m_infoType ) { case eIT_PlugType: if ( !m_plugType ) { m_plugType = new ExtendedPlugInfoPlugTypeSpecificData; } status = m_plugType->deserialize( de ); break; case eIT_PlugName: if ( !m_plugName ) { m_plugName = new ExtendedPlugInfoPlugNameSpecificData; } status = m_plugName->deserialize( de ); break; case eIT_NoOfChannels: if ( !m_plugNrOfChns ) { m_plugNrOfChns = new ExtendedPlugInfoPlugNumberOfChannelsSpecificData; } status = m_plugNrOfChns->deserialize( de ); break; case eIT_ChannelPosition: if ( !m_plugChannelPosition ) { m_plugChannelPosition = new ExtendedPlugInfoPlugChannelPositionSpecificData; } status = m_plugChannelPosition->deserialize( de ); break; case eIT_ChannelName: if ( !m_plugChannelName ) { m_plugChannelName = new ExtendedPlugInfoPlugChannelNameSpecificData; } status = m_plugChannelName->deserialize( de ); break; case eIT_PlugInput: if ( !m_plugInput ) { m_plugInput = new ExtendedPlugInfoPlugInputSpecificData; } status = m_plugInput->deserialize( de ); break; case eIT_PlugOutput: if ( !m_plugOutput ) { m_plugOutput = new ExtendedPlugInfoPlugOutputSpecificData; } status = m_plugOutput->deserialize( de ); break; case eIT_ClusterInfo: if ( !m_plugClusterInfo ) { m_plugClusterInfo = new ExtendedPlugInfoClusterInfoSpecificData; } status =m_plugClusterInfo->deserialize( de ); break; default: return false; } return status; } ExtendedPlugInfoInfoType* ExtendedPlugInfoInfoType::clone() const { ExtendedPlugInfoInfoType* extPlugInfoInfoType = new ExtendedPlugInfoInfoType( *this ); extPlugInfoInfoType->initialize(); return extPlugInfoInfoType; } const char* extendedPlugInfoInfoTypeStrings[] = { "PlugType", "PlugName", "NoOfChannels", "ChannelPosition", "ChannelName", "PlugInput", "PlugOutput", "ClusterInfo", }; const char* extendedPlugInfoInfoTypeToString( info_type_t infoType ) { if ( infoType > ( ( sizeof( extendedPlugInfoInfoTypeStrings ) ) / ( sizeof( extendedPlugInfoInfoTypeStrings[0] ) ) ) ) { return "Unknown"; } else { return extendedPlugInfoInfoTypeStrings[infoType]; } } ////////////////////////////////////////////// ExtendedPlugInfoCmd::ExtendedPlugInfoCmd( Ieee1394Service& ieee1394service, ESubFunction eSubFunction ) : AVCCommand( ieee1394service, AVC1394_CMD_PLUG_INFO ) { setSubFunction( eSubFunction ); UnitPlugAddress unitPlugAddress( UnitPlugAddress::ePT_PCR, 0x00 ); m_plugAddress = new PlugAddress( PlugAddress::ePD_Output, PlugAddress::ePAM_Unit, unitPlugAddress ); m_infoType = new ExtendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_PlugType ); m_infoType->initialize(); } ExtendedPlugInfoCmd::ExtendedPlugInfoCmd( const ExtendedPlugInfoCmd& rhs ) : AVCCommand( rhs ) { m_subFunction = rhs.m_subFunction; m_plugAddress = new PlugAddress( *rhs.m_plugAddress ); m_infoType = new ExtendedPlugInfoInfoType( *rhs.m_infoType ); } ExtendedPlugInfoCmd::~ExtendedPlugInfoCmd() { delete m_plugAddress; m_plugAddress = 0; delete m_infoType; m_infoType = 0; } bool ExtendedPlugInfoCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool status = false; AVCCommand::serialize( se ); se.write( m_subFunction, "ExtendedPlugInfoCmd subFunction" ); status = m_plugAddress->serialize( se ); status &= m_infoType->serialize( se ); return status; } bool ExtendedPlugInfoCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool status = false; AVCCommand::deserialize( de ); de.read( &m_subFunction ); status = m_plugAddress->deserialize( de ); status &= m_infoType->deserialize( de ); return status; } bool ExtendedPlugInfoCmd::setPlugAddress( const PlugAddress& plugAddress ) { delete m_plugAddress; m_plugAddress = plugAddress.clone(); return true; } bool ExtendedPlugInfoCmd::setSubFunction( ESubFunction subFunction ) { m_subFunction = subFunction; return true; } bool ExtendedPlugInfoCmd::setInfoType( const ExtendedPlugInfoInfoType& infoType ) { delete m_infoType; m_infoType = infoType.clone(); return true; } } libffado-2.4.5/src/libavc/general/avc_extended_plug_info.h0000644000175000001440000002555614206145246023211 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCEXTENDEDPLUGINFO_H #define AVCEXTENDEDPLUGINFO_H #include "avc_extended_cmd_generic.h" #include "avc_generic.h" #include #include #define AVC1394_CMD_PLUG_INFO 0x02 namespace AVC { class ExtendedPlugInfoPlugTypeSpecificData : public IBusData { public: enum EExtendedPlugInfoPlugType { eEPIPT_IsoStream = 0x0, eEPIPT_AsyncStream = 0x1, eEPIPT_Midi = 0x2, eEPIPT_Sync = 0x3, eEPIPT_Analog = 0x4, eEPIPT_Digital = 0x5, eEPIPT_Unknown = 0xff, }; ExtendedPlugInfoPlugTypeSpecificData( EExtendedPlugInfoPlugType ePlugType = eEPIPT_Unknown); virtual ~ExtendedPlugInfoPlugTypeSpecificData(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual ExtendedPlugInfoPlugTypeSpecificData* clone() const; typedef byte_t plug_type_t; plug_type_t m_plugType; }; const char* extendedPlugInfoPlugTypeToString( plug_type_t plugType ); ///////////////////////////////////////// class ExtendedPlugInfoPlugNameSpecificData : public IBusData { public: ExtendedPlugInfoPlugNameSpecificData(); virtual ~ExtendedPlugInfoPlugNameSpecificData(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual ExtendedPlugInfoPlugNameSpecificData* clone() const; std::string m_name; }; ///////////////////////////////////////// class ExtendedPlugInfoPlugNumberOfChannelsSpecificData : public IBusData { public: ExtendedPlugInfoPlugNumberOfChannelsSpecificData(); virtual ~ExtendedPlugInfoPlugNumberOfChannelsSpecificData(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual ExtendedPlugInfoPlugNumberOfChannelsSpecificData* clone() const; nr_of_channels_t m_nrOfChannels; }; ///////////////////////////////////////// class ExtendedPlugInfoPlugChannelPositionSpecificData : public IBusData { public: enum ELocation { eEPI_LeftFront = 0x01, eEPI_RightFront = 0x02, eEPI_Center = 0x03, eEPI_Subwoofer = 0x04, eEPI_LeftSurround = 0x05, eEPI_RightSurround = 0x06, eEPI_LeftOfCenter = 0x07, eEPI_RightOfCenter = 0x08, eEPI_Surround = 0x09, eEPI_SideLeft = 0x0a, eEPI_SideRight = 0x0b, eEPI_Top = 0x0c, eEPI_Bottom = 0x0d, eEPI_LeftFrontEffect = 0x0e, eEPI_RightFrontEffect = 0x0f, eEPI_NoPostion = 0xff, }; struct ChannelInfo { stream_position_t m_streamPosition; stream_position_location_t m_location; }; typedef std::vector ChannelInfoVector; struct ClusterInfo { nr_of_channels_t m_nrOfChannels; ChannelInfoVector m_channelInfos; }; ExtendedPlugInfoPlugChannelPositionSpecificData(); virtual ~ExtendedPlugInfoPlugChannelPositionSpecificData(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual ExtendedPlugInfoPlugChannelPositionSpecificData* clone() const; typedef std::vector ClusterInfoVector; nr_of_clusters_t m_nrOfClusters; ClusterInfoVector m_clusterInfos; }; ///////////////////////////////////////// class ExtendedPlugInfoPlugChannelNameSpecificData : public IBusData { public: ExtendedPlugInfoPlugChannelNameSpecificData(); virtual ~ExtendedPlugInfoPlugChannelNameSpecificData(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual ExtendedPlugInfoPlugChannelNameSpecificData* clone() const; stream_position_t m_streamPosition; string_length_t m_stringLength; std::string m_plugChannelName; }; ///////////////////////////////////////// class ExtendedPlugInfoPlugInputSpecificData : public IBusData { public: ExtendedPlugInfoPlugInputSpecificData(); ExtendedPlugInfoPlugInputSpecificData( const ExtendedPlugInfoPlugInputSpecificData& rhs ); virtual ~ExtendedPlugInfoPlugInputSpecificData(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual ExtendedPlugInfoPlugInputSpecificData* clone() const; PlugAddressSpecificData* m_plugAddress; }; ///////////////////////////////////////// class ExtendedPlugInfoPlugOutputSpecificData : public IBusData { public: ExtendedPlugInfoPlugOutputSpecificData(); ExtendedPlugInfoPlugOutputSpecificData( const ExtendedPlugInfoPlugOutputSpecificData& rhs ); virtual ~ExtendedPlugInfoPlugOutputSpecificData(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual ExtendedPlugInfoPlugOutputSpecificData* clone() const; number_of_output_plugs_t m_nrOfOutputPlugs; typedef std::vector PlugAddressVector; PlugAddressVector m_outputPlugAddresses; }; ///////////////////////////////////////// class ExtendedPlugInfoClusterInfoSpecificData : public IBusData { public: enum EPortType { ePT_Speaker = 0x00, ePT_Headphone = 0x01, ePT_Microphone = 0x02, ePT_Line = 0x03, ePT_SPDIF = 0x04, ePT_ADAT = 0x05, ePT_TDIF = 0x06, ePT_MADI = 0x07, ePT_Analog = 0x08, ePT_Digital = 0x09, ePT_MIDI = 0x0a, ePT_NoType = 0xff, }; ExtendedPlugInfoClusterInfoSpecificData(); virtual ~ExtendedPlugInfoClusterInfoSpecificData(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual ExtendedPlugInfoClusterInfoSpecificData* clone() const; cluster_index_t m_clusterIndex; port_type_t m_portType; string_length_t m_stringLength; std::string m_clusterName; }; const char* extendedPlugInfoClusterInfoPortTypeToString( port_type_t portType ); ///////////////////////////////////////// #define AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_TYPE 0x00 #define AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_NAME 0x01 #define AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_NO_OF_CHANNELS 0x02 #define AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_CHANNEL_POSITION 0x03 #define AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_CHANNEL_NAME 0x04 #define AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_INPUT 0x05 #define AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_OUTPUT 0x06 #define AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_CLUSTER_INFO 0x07 class ExtendedPlugInfoInfoType : public IBusData { public: enum EInfoType { eIT_PlugType = AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_TYPE, eIT_PlugName = AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_NAME, eIT_NoOfChannels = AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_NO_OF_CHANNELS, eIT_ChannelPosition = AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_CHANNEL_POSITION, eIT_ChannelName = AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_CHANNEL_NAME, eIT_PlugInput = AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_INPUT, eIT_PlugOutput = AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_OUTPUT, eIT_ClusterInfo = AVC1394_EXTENDED_PLUG_INFO_INFO_TYPE_PLUG_CLUSTER_INFO, }; ExtendedPlugInfoInfoType(EInfoType eInfoType); ExtendedPlugInfoInfoType( const ExtendedPlugInfoInfoType& rhs ); virtual ~ExtendedPlugInfoInfoType(); bool initialize(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual ExtendedPlugInfoInfoType* clone() const; info_type_t m_infoType; ExtendedPlugInfoPlugTypeSpecificData* m_plugType; ExtendedPlugInfoPlugNameSpecificData* m_plugName; ExtendedPlugInfoPlugNumberOfChannelsSpecificData* m_plugNrOfChns; ExtendedPlugInfoPlugChannelPositionSpecificData* m_plugChannelPosition; ExtendedPlugInfoPlugChannelNameSpecificData* m_plugChannelName; ExtendedPlugInfoPlugInputSpecificData* m_plugInput; ExtendedPlugInfoPlugOutputSpecificData* m_plugOutput; ExtendedPlugInfoClusterInfoSpecificData* m_plugClusterInfo; }; const char* extendedPlugInfoInfoTypeToString( info_type_t infoType ); ///////////////////////////////////////////////////////// #define AVC1394_PLUG_INFO_SUBFUNCTION_EXTENDED_PLUG_INFO_CMD 0xC0 #define AVC1394_PLUG_INFO_SUBFUNCTION_SERIAL_BUS_NOT_USED 0xFF class ExtendedPlugInfoCmd: public AVCCommand { public: enum ESubFunction { eSF_ExtendedPlugInfoCmd = AVC1394_PLUG_INFO_SUBFUNCTION_EXTENDED_PLUG_INFO_CMD, eSF_NotUsed = AVC1394_PLUG_INFO_SUBFUNCTION_SERIAL_BUS_NOT_USED, }; ExtendedPlugInfoCmd( Ieee1394Service& ieee1394service, ESubFunction eSubFunction = eSF_ExtendedPlugInfoCmd ); ExtendedPlugInfoCmd( const ExtendedPlugInfoCmd& rhs ); virtual ~ExtendedPlugInfoCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); bool setPlugAddress( const PlugAddress& plugAddress ); bool setSubFunction( ESubFunction subFunction ); subfunction_t getSubFunction( ) {return m_subFunction;}; bool setInfoType( const ExtendedPlugInfoInfoType& infoType ); ExtendedPlugInfoInfoType* getInfoType() { return m_infoType; } virtual const char* getCmdName() const { return "ExtendedPlugInfoCmd"; } protected: subfunction_t m_subFunction; PlugAddress* m_plugAddress; ExtendedPlugInfoInfoType* m_infoType; }; } #endif libffado-2.4.5/src/libavc/general/avc_extended_subunit_info.cpp0000644000175000001440000001121314206145246024247 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_extended_subunit_info.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "libutil/ByteSwap.h" #define NR_OF_PAGE_DATA 5 #define SIZE_OF_PAGE_ENTRY 5 namespace AVC { ExtendedSubunitInfoPageData::ExtendedSubunitInfoPageData() : IBusData() , m_functionBlockType( 0xff ) , m_functionBlockId( 0xff ) , m_functionBlockSpecialPupose( eSP_NoSpecialPupose ) , m_noOfInputPlugs( 0xff ) , m_noOfOutputPlugs( 0xff ) { } ExtendedSubunitInfoPageData::~ExtendedSubunitInfoPageData() { } bool ExtendedSubunitInfoPageData::serialize( Util::Cmd::IOSSerialize& se ) { se.write( m_functionBlockType, "ExtendedSubunitInfoPageData: function block type" ); se.write( m_functionBlockId, "ExtendedSubunitInfoPageData: function block id" ); se.write( m_functionBlockSpecialPupose, "ExtendedSubunitInfoPageData: function block special purpose" ); se.write( m_noOfInputPlugs, "ExtendedSubunitInfoPageData: number of input plugs" ); se.write( m_noOfOutputPlugs, "ExtendedSubunitInfoPageData: number of output plugs" ); return true; } bool ExtendedSubunitInfoPageData::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_functionBlockType ); de.read( &m_functionBlockId ); de.read( &m_functionBlockSpecialPupose ); de.read( &m_noOfInputPlugs ); de.read( &m_noOfOutputPlugs ); return true; } ExtendedSubunitInfoPageData* ExtendedSubunitInfoPageData::clone() const { return new ExtendedSubunitInfoPageData( *this ); } ////////////////////////////////////////////// ExtendedSubunitInfoCmd::ExtendedSubunitInfoCmd( Ieee1394Service& ieee1394service ) : AVCCommand( ieee1394service, AVC1394_CMD_SUBUNIT_INFO ) , m_page( 0xff ) , m_fbType( eFBT_AllFunctinBlockType ) { } ExtendedSubunitInfoCmd::ExtendedSubunitInfoCmd( const ExtendedSubunitInfoCmd& rhs ) : AVCCommand( rhs ) , m_page( rhs.m_page ) , m_fbType( rhs.m_fbType ) { for ( ExtendedSubunitInfoPageDataVector::const_iterator it = rhs.m_infoPageDatas.begin(); it != rhs.m_infoPageDatas.end(); ++it ) { m_infoPageDatas.push_back( ( *it )->clone() ); } } ExtendedSubunitInfoCmd::~ExtendedSubunitInfoCmd() { for ( ExtendedSubunitInfoPageDataVector::iterator it = m_infoPageDatas.begin(); it != m_infoPageDatas.end(); ++it ) { delete *it; } } bool ExtendedSubunitInfoCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool status = false; status = AVCCommand::serialize( se ); status &= se.write( m_page, "ExtendedSubunitInfoCmd: page" ); status &= se.write( m_fbType, "ExtendedSubunitInfoCmd: function block type" ); for ( ExtendedSubunitInfoPageDataVector::const_iterator it = m_infoPageDatas.begin(); it != m_infoPageDatas.end(); ++it ) { status &= ( *it )->serialize( se ); } int startIndex = m_infoPageDatas.size() * SIZE_OF_PAGE_ENTRY; int endIndex = SIZE_OF_PAGE_ENTRY * NR_OF_PAGE_DATA; for ( int i = startIndex; i < endIndex; ++i ) { byte_t dummy = 0xff; se.write( dummy, "ExtendedSubunitInfoCmd: space fill" ); } return status; } bool ExtendedSubunitInfoCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool status = false; status = AVCCommand::deserialize( de ); status &= de.read( &m_page ); status &= de.read( &m_fbType ); for ( int i = 0; i < 5; ++i ) { byte_t next; de.peek( &next ); if ( next != 0xff ) { ExtendedSubunitInfoPageData* infoPageData = new ExtendedSubunitInfoPageData(); if ( !infoPageData->deserialize( de ) ) { return false; } m_infoPageDatas.push_back( infoPageData ); } else { return status; } } return status; } } libffado-2.4.5/src/libavc/general/avc_extended_subunit_info.h0000644000175000001440000000645414206145246023727 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCEXTENDEDSUBUNITINFO_H #define AVCEXTENDEDSUBUNITINFO_H #include "avc_generic.h" #include #include #define AVC1394_CMD_SUBUNIT_INFO 0x31 namespace AVC { class ExtendedSubunitInfoPageData: public IBusData { public: enum ESpecialPurpose { eSP_InputGain = 0x00, eSP_OutputVolume = 0x01, eSP_NoSpecialPupose = 0xff, }; ExtendedSubunitInfoPageData(); virtual ~ExtendedSubunitInfoPageData(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual ExtendedSubunitInfoPageData* clone() const; function_block_type_t m_functionBlockType; function_block_id_t m_functionBlockId; function_block_special_purpose_t m_functionBlockSpecialPupose; no_of_input_plugs_t m_noOfInputPlugs; no_of_output_plugs_t m_noOfOutputPlugs; }; typedef std::vector ExtendedSubunitInfoPageDataVector; class ExtendedSubunitInfoCmd: public AVCCommand { public: enum EFunctionBlockType { eFBT_AllFunctinBlockType = 0xff, eFBT_AudioSubunitSelector = 0x80, eFBT_AudioSubunitFeature = 0x81, eFBT_AudioSubunitProcessing = 0x82, eFBT_AudioSubunitCodec = 0x83, }; enum EProcessingType { ePT_Unknown = 0x00, ePT_Mixer = 0x01, ePT_Generic = 0x02, ePT_UpDown = 0x03, ePT_DolbyProLogic = 0x04, ePT_3DStereoExtender = 0x05, ePT_Reverberation = 0x06, ePT_Chorus = 0x07, ePT_DynamicRangeCompression = 0x08, ePT_EnhancedMixer = 0x82, ePT_Reserved = 0xff, }; enum ECodecType { eCT_AC3Decoder = 0x01, eCT_MPEGDecoder = 0x02, eCT_DTSDecoder = 0x03, eCT_Reserved = 0xff, }; ExtendedSubunitInfoCmd( Ieee1394Service& ieee1394service ); ExtendedSubunitInfoCmd( const ExtendedSubunitInfoCmd& rhs ); virtual ~ExtendedSubunitInfoCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "ExtendedSubunitInfoCmd"; } page_t m_page; function_block_type_t m_fbType; ExtendedSubunitInfoPageDataVector m_infoPageDatas; }; } #endif libffado-2.4.5/src/libavc/general/avc_generic.cpp0000644000175000001440000002163114206145246021304 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_generic.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "debugmodule/debugmodule.h" #include "libutil/Time.h" #include "libutil/ByteSwap.h" #include #include #define DEBUG_EXTRA_VERBOSE 5 namespace AVC { IMPL_DEBUG_MODULE( AVCCommand, AVCCommand, DEBUG_LEVEL_NORMAL ); IMPL_DEBUG_MODULE( IBusData, IBusData, DEBUG_LEVEL_VERBOSE ); AVCCommand::AVCCommand( Ieee1394Service& ieee1394service, opcode_t opcode ) : m_p1394Service( &ieee1394service ) , m_nodeId( 0 ) , m_ctype( eCT_Unknown ) , m_subunit( 0xff ) , m_opcode( opcode ) , m_eResponse( eR_Unknown ) { } bool AVCCommand::serialize( Util::Cmd::IOSSerialize& se ) { // XXX \todo improve Util::Cmd::IOSSerialize::write interface char* buf; asprintf( &buf, "AVCCommand ctype ('%s')", responseToString( static_cast( m_ctype ) ) ); se.write( m_ctype, buf ); free( buf ); asprintf( &buf, "AVCCommand subunit (subunit_type = %d, subunit_id = %d)", getSubunitType(), getSubunitId() ); se.write( m_subunit, buf ); free( buf ); se.write( m_opcode, "AVCCommand opcode" ); return true; } bool AVCCommand::deserialize( Util::Cmd::IISDeserialize& de ) { de.read( &m_ctype ); de.read( &m_subunit ); de.read( &m_opcode ); return true; } bool AVCCommand::setCommandType( ECommandType commandType ) { m_ctype = commandType; m_commandType = commandType; return true; } AVCCommand::ECommandType AVCCommand::getCommandType() { return m_commandType; } AVCCommand::EResponse AVCCommand::getResponse() { return m_eResponse; } bool AVCCommand::setSubunitType(ESubunitType subunitType) { byte_t subT = subunitType; m_subunit = ( subT << 3 ) | ( m_subunit & 0x7 ); return true; } bool AVCCommand::setNodeId( fb_nodeid_t nodeId ) { m_nodeId = nodeId; return true; } bool AVCCommand::setSubunitId(subunit_id_t subunitId) { m_subunit = ( subunitId & 0x7 ) | ( m_subunit & 0xf8 ); return true; } ESubunitType AVCCommand::getSubunitType() { return static_cast( ( m_subunit >> 3 ) ); } subunit_id_t AVCCommand::getSubunitId() { return m_subunit & 0x7; } bool AVCCommand::setVerbose( int verboseLevel ) { setDebugLevel(verboseLevel); return true; } int AVCCommand::getVerboseLevel() { return getDebugLevel(); } void AVCCommand::showFcpFrame( const unsigned char* buf, unsigned short frameSize ) const { // use an intermediate buffer to avoid a load of very small print's that cause the // message ringbuffer to overflow char msg[DEBUG_MAX_MESSAGE_LENGTH]; int chars_written=0; for ( int i = 0; i < frameSize; ++i ) { if ( ( i % 16 ) == 0 ) { if ( i > 0 ) { debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, "%s\n", msg); chars_written=0; } chars_written+=snprintf(msg+chars_written,DEBUG_MAX_MESSAGE_LENGTH-chars_written," %3d:\t", i );; } else if ( ( i % 4 ) == 0 ) { chars_written+=snprintf(msg+chars_written,DEBUG_MAX_MESSAGE_LENGTH-chars_written," "); } chars_written+=snprintf(msg+chars_written,DEBUG_MAX_MESSAGE_LENGTH-chars_written, "%02x ", buf[i] ); } if (chars_written != 0) { debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, "%s\n", msg ); } else { debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, "\n" ); } } bool AVCCommand::fire() { memset( &m_fcpFrame, 0x0, sizeof( m_fcpFrame ) ); Util::Cmd::BufferSerialize se( m_fcpFrame, sizeof( m_fcpFrame ) ); if ( !serialize( se ) ) { debugFatal( "fire: Could not serialize\n" ); return false; } unsigned short fcpFrameSize = se.getNrOfProducesBytes(); if (getDebugLevel() >= DEBUG_LEVEL_VERY_VERBOSE) { debugOutputShort( DEBUG_LEVEL_VERY_VERBOSE, "%s:\n", getCmdName() ); debugOutputShort( DEBUG_LEVEL_VERY_VERBOSE, " Request:\n"); showFcpFrame( m_fcpFrame, fcpFrameSize ); Util::Cmd::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_writtentransactionBlock( m_nodeId, (quadlet_t*)m_fcpFrame, ( fcpFrameSize+3 ) / 4, &resp_len ); if ( resp ) { resp_len *= 4; unsigned char* buf = ( unsigned char* ) resp; m_eResponse = ( EResponse )( *buf ); switch ( m_eResponse ) { case eR_Accepted: case eR_Implemented: case eR_Rejected: case eR_NotImplemented: { Util::Cmd::BufferDeserialize de( buf, resp_len ); result = deserialize( de ); debugOutputShort( DEBUG_LEVEL_VERY_VERBOSE," Response:\n"); showFcpFrame( buf, de.getNrOfConsumedBytes() ); Util::Cmd::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_writtentransactionBlockClose(); } else { debugOutput( DEBUG_LEVEL_VERBOSE, "no response\n" ); result = false; m_p1394Service->transactionBlockClose(); } return result; } const char* subunitTypeStrings[] = { "Monitor", "Audio", "Printer", "Disc recorder", "Tape recorder/VCR", "Tuner", "CA", "Video camera", "unknown", "Panel", "Bulletin board", "Camera storage", "Music", }; const char* subunitTypeToString( subunit_type_t subunitType ) { if ( subunitType == eST_Unit ) { return "Unit"; } if ( subunitType > ( int ) ( sizeof( subunitTypeStrings ) / sizeof( subunitTypeStrings[0] ) ) ) { return "unknown"; } else { return subunitTypeStrings[subunitType]; } } const char* responseToStrings[] = { "control", "status", "specific inquiry", "notify", "general inquiry", "reserved for future specification", "reserved for future specification", "reserved for future specification", "not implemented", "accepted", "rejected", "in transition", "implemented/stable", "changed" "reserved for future specification", "interim", }; const char* responseToString( AVCCommand::EResponse eResponse ) { if ( eResponse > ( int )( sizeof( responseToStrings ) / sizeof( responseToStrings[0] ) ) ) { return "unknown"; } return responseToStrings[eResponse]; } } libffado-2.4.5/src/libavc/general/avc_generic.h0000644000175000001440000000677114206145246020761 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCGENERIC_H #define AVCGENERIC_H #include "../avc_definitions.h" #include "debugmodule/debugmodule.h" #include "fbtypes.h" class Ieee1394Service; namespace Util { namespace Cmd { class IOSSerialize; class IISDeserialize; } }; namespace AVC { const int fcpFrameMaxLength = 512; typedef unsigned char fcp_frame_t[fcpFrameMaxLength]; enum EAVCDiscoveryMode { eDM_BeBoB = 0x00, eDM_GenericAVC = 0x01, eDM_Invalid = 0xFF, }; class IBusData { public: IBusData() {} virtual ~IBusData() {} virtual bool serialize( Util::Cmd::IOSSerialize& se ) = 0; virtual bool deserialize( Util::Cmd::IISDeserialize& de ) = 0; virtual IBusData* clone() const = 0; protected: DECLARE_DEBUG_MODULE; }; class AVCCommand { public: enum EResponse { eR_Unknown = 0xff, eR_NotImplemented = 0x08, eR_Accepted = 0x09, eR_Rejected = 0x0A, eR_InTransition = 0x0B, eR_Implemented = 0x0C, eR_Changed = 0x0D, eR_Interim = 0x0F, }; enum ECommandType { eCT_Control = 0x00, eCT_Status = 0x01, eCT_SpecificInquiry = 0x02, eCT_Notify = 0x03, eCT_GeneralInquiry = 0x04, eCT_Unknown = 0xff, }; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual bool setCommandType( ECommandType commandType ); virtual bool fire(); EResponse getResponse(); bool setNodeId( fb_nodeid_t nodeId ); bool setSubunitType( ESubunitType subunitType ); bool setSubunitId( subunit_id_t subunitId ); ESubunitType getSubunitType(); subunit_id_t getSubunitId(); bool setVerbose( int verboseLevel ); bool setVerboseLevel( int verboseLevel ) { return setVerbose(verboseLevel);}; int getVerboseLevel(); virtual const char* getCmdName() const = 0; protected: void showFcpFrame( const unsigned char* buf, unsigned short frameSize ) const; protected: AVCCommand( Ieee1394Service& ieee1394service, opcode_t opcode ); virtual ~AVCCommand() {} ECommandType getCommandType(); Ieee1394Service* m_p1394Service; fb_nodeid_t m_nodeId; fcp_frame_t m_fcpFrame; private: ctype_t m_ctype; subunit_t m_subunit; opcode_t m_opcode; EResponse m_eResponse; ECommandType m_commandType; protected: DECLARE_DEBUG_MODULE; }; const char* subunitTypeToString( subunit_type_t subunitType ); const char* responseToString( AVCCommand::EResponse eResponse ); } #endif // AVCGENERIC_H libffado-2.4.5/src/libavc/general/avc_plug.cpp0000644000175000001440000024736714206145246020657 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_plug.h" #include "avc_unit.h" #include "avc_signal_format.h" #include "avc_extended_plug_info.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/cmd_serialize.h" #include #include namespace AVC { IMPL_DEBUG_MODULE( Plug, Plug, DEBUG_LEVEL_NORMAL ); IMPL_DEBUG_MODULE( PlugManager, PlugManager, DEBUG_LEVEL_NORMAL ); Plug::Plug( Unit* unit, Subunit* subunit, function_block_type_t functionBlockType, function_block_id_t functionBlockId, EPlugAddressType plugAddressType, EPlugDirection plugDirection, plug_id_t plugId ) : m_unit( unit ) , m_subunit( subunit ) , m_functionBlockType( functionBlockType ) , m_functionBlockId( functionBlockId ) , m_addressType( plugAddressType ) , m_direction( plugDirection ) , m_id( plugId ) , m_infoPlugType( eAPT_Unknown ) , m_nrOfChannels( 0 ) , m_globalId( unit->getPlugManager().requestNewGlobalId() ) { debugOutput( DEBUG_LEVEL_VERBOSE, "nodeId = %d, subunitType = %d, " "subunitId = %d, functionBlockType = %d, " "functionBlockId = %d, addressType = %d, " "direction = %d, id = %d\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_functionBlockType, m_functionBlockId, m_addressType, m_direction, m_id ); } Plug::Plug( Unit* unit, Subunit* subunit, function_block_type_t functionBlockType, function_block_id_t functionBlockId, EPlugAddressType plugAddressType, EPlugDirection plugDirection, plug_id_t plugId, int globalId ) : m_unit( unit ) , m_subunit( subunit ) , m_functionBlockType( functionBlockType ) , m_functionBlockId( functionBlockId ) , m_addressType( plugAddressType ) , m_direction( plugDirection ) , m_id( plugId ) , m_infoPlugType( eAPT_Unknown ) , m_nrOfChannels( 0 ) { if(globalId < 0) { m_globalId = unit->getPlugManager().requestNewGlobalId(); } else { m_globalId = globalId; } debugOutput( DEBUG_LEVEL_VERBOSE, "nodeId = %d, subunitType = %d, " "subunitId = %d, functionBlockType = %d, " "functionBlockId = %d, addressType = %d, " "direction = %d, id = %d\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_functionBlockType, m_functionBlockId, m_addressType, m_direction, m_id ); } Plug::Plug( const Plug& rhs ) : m_unit ( rhs.m_unit ) , m_subunit ( rhs.m_subunit ) , m_subunitType ( rhs.m_subunitType ) , m_subunitId ( rhs.m_subunitId ) , m_functionBlockType( rhs.m_functionBlockType ) , m_functionBlockId( rhs.m_functionBlockId ) , m_addressType( rhs.m_addressType ) , m_direction( rhs.m_direction ) , m_id( rhs.m_id ) , m_infoPlugType( rhs.m_infoPlugType ) , m_nrOfChannels( rhs.m_nrOfChannels ) , m_name( rhs.m_name ) , m_clusterInfos( rhs.m_clusterInfos ) , m_formatInfos( rhs.m_formatInfos ) , m_globalId( rhs.m_globalId ) { if ( getDebugLevel() ) { setDebugLevel( DEBUG_LEVEL_VERBOSE ); } } Plug::Plug() : m_unit( NULL ) , m_subunit( NULL ) , m_functionBlockType( 0 ) , m_functionBlockId( 0 ) , m_addressType( eAPA_Undefined ) , m_direction( eAPD_Unknown ) , m_id( 0 ) , m_infoPlugType( eAPT_Unknown ) , m_nrOfChannels( 0 ) , m_globalId( 0 ) { } Plug::~Plug() { m_unit->getPlugManager().remPlug( *this ); } void Plug::setVerboseLevel(int l) { setDebugLevel(l); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Setting verbose level to %d...\n", l ); } ESubunitType Plug::getSubunitType() const { return (m_subunit==NULL?eST_Unit:m_subunit->getSubunitType()); } subunit_id_t Plug::getSubunitId() const { return (m_subunit==NULL?0xFF:m_subunit->getSubunitId()); } bool Plug::discover() { if ( !initFromDescriptor() ) { debugOutput(DEBUG_LEVEL_NORMAL, "discover: Could not init plug from descriptor (%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); // return false; } if ( !discoverPlugType() ) { debugOutput(DEBUG_LEVEL_NORMAL, "discover: Could not discover plug type (%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverName() ) { debugOutput(DEBUG_LEVEL_NORMAL, "Could not discover name (%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverNoOfChannels() ) { debugError( "Could not discover number of channels " "(%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverChannelPosition() ) { debugOutput(DEBUG_LEVEL_NORMAL, "Could not discover channel positions " "(%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverChannelName() ) { debugOutput(DEBUG_LEVEL_NORMAL, "Could not discover channel name " "(%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverClusterInfo() ) { debugOutput(DEBUG_LEVEL_NORMAL, "Could not discover cluster info " "(%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); return false; } if ( !discoverStreamFormat() ) { debugOutput(DEBUG_LEVEL_NORMAL, "Could not discover stream format " "(%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); // return false; } // adi@2011-1-14: short-circuit left-to-right evaluation, so only try to // discover the supported stream formats when not on a sync channel. // According to Holger Dehnhardt, this is required to make his Mackie // Onyx work. if ( (m_infoPlugType != AVC::Plug::eAPT_Sync) && !discoverSupportedStreamFormats() ) { debugOutput(DEBUG_LEVEL_NORMAL, "Could not discover supported stream formats " "(%d,%d,%d,%d,%d)\n", m_unit->getConfigRom().getNodeId(), getSubunitType(), getSubunitId(), m_direction, m_id ); // return false; } return m_unit->getPlugManager().addPlug( *this ); } bool Plug::initFromDescriptor() { if(getSubunitType()==eST_Unit) { debugOutput(DEBUG_LEVEL_VERBOSE, "Not loading unit plug from descriptor.\n"); return true; } else { return m_subunit->initPlugFromDescriptor(*this); } } bool Plug::discoverConnections() { return discoverConnectionsInput() && discoverConnectionsOutput(); } bool Plug::discoverPlugType() { return true; } bool Plug::discoverName() { // name already set if (m_name != "") return true; m_name = plugAddressTypeToString(getPlugAddressType()); m_name += " "; m_name += plugTypeToString(getPlugType()); m_name += " "; m_name += plugDirectionToString(getPlugDirection()); return true; } bool Plug::discoverNoOfChannels() { if (m_nrOfChannels == 0) { // not discovered yet, get from ext stream format ExtendedStreamFormatCmd extStreamFormatCmd = setPlugAddrToStreamFormatCmd( ExtendedStreamFormatCmd::eSF_ExtendedStreamFormatInformationCommand ); extStreamFormatCmd.setVerbose( getDebugLevel() ); if ( !extStreamFormatCmd.fire() ) { debugError( "stream format command failed\n" ); return false; } if ( ( extStreamFormatCmd.getStatus() == ExtendedStreamFormatCmd::eS_NoStreamFormat ) || ( extStreamFormatCmd.getStatus() == ExtendedStreamFormatCmd::eS_NotUsed ) ) { debugOutput( DEBUG_LEVEL_VERBOSE, "No stream format information available\n" ); return true; } if ( !extStreamFormatCmd.getFormatInformation() ) { debugOutput(DEBUG_LEVEL_NORMAL, "No stream format information for plug found -> skip\n" ); return true; } if ( extStreamFormatCmd.getFormatInformation()->m_root != FormatInformation::eFHR_AudioMusic ) { debugOutput(DEBUG_LEVEL_NORMAL, "Format hierarchy root is not Audio&Music -> skip\n" ); return true; } FormatInformation* formatInfo = extStreamFormatCmd.getFormatInformation(); FormatInformationStreamsCompound* compoundStream = dynamic_cast< FormatInformationStreamsCompound* > ( formatInfo->m_streams ); if ( compoundStream ) { unsigned int nb_channels = 0; for ( int i = 1; i <= compoundStream->m_numberOfStreamFormatInfos; ++i ) { StreamFormatInfo* streamFormatInfo = compoundStream->m_streamFormatInfos[ i - 1 ]; debugOutput( DEBUG_LEVEL_VERBOSE, "number of channels = %d, stream format = %d\n", streamFormatInfo->m_numberOfChannels, streamFormatInfo->m_streamFormat ); // FIXME: might not be correct to sum all nb_channels += streamFormatInfo->m_numberOfChannels; } m_nrOfChannels = nb_channels; } return true; } else { // already got the nb channels from somewhere else return true; } } bool Plug::discoverChannelPosition() { return true; } bool Plug::discoverChannelName() { return true; } bool Plug::discoverClusterInfo() { // if there are no cluster info's, we'll have to come up with some if(m_clusterInfos.size() == 0) { debugOutput( DEBUG_LEVEL_VERBOSE, "fixing up cluster infos\n"); // we figure out how many channels we have, and build one cluster struct ClusterInfo c; c.m_index = 1; c.m_portType = 0; c.m_name = "Unknown"; c.m_buildSource = -1; // Flag this as a temp build c.m_nrOfChannels = m_nrOfChannels; for(int i=0; i skip\n" ); return true; } if ( extStreamFormatCmd.getFormatInformation()->m_root != FormatInformation::eFHR_AudioMusic ) { debugOutput(DEBUG_LEVEL_NORMAL, "Format hierarchy root is not Audio&Music -> skip\n" ); return true; } FormatInformation* formatInfo = extStreamFormatCmd.getFormatInformation(); FormatInformationStreamsCompound* compoundStream = dynamic_cast< FormatInformationStreamsCompound* > ( formatInfo->m_streams ); if ( compoundStream ) { m_samplingFrequency = compoundStream->m_samplingFrequency; debugOutput( DEBUG_LEVEL_VERBOSE, "%s plug %d uses " "sampling frequency %d, nr of stream infos = %d\n", getName(), m_id, m_samplingFrequency, compoundStream->m_numberOfStreamFormatInfos ); for ( int i = 1; i <= compoundStream->m_numberOfStreamFormatInfos; ++i ) { ClusterInfo* clusterInfo = const_cast( getClusterInfoByIndex( i ) ); if ( !clusterInfo ) { debugOutput(DEBUG_LEVEL_NORMAL, "No matching cluster " "info found for index %d\n", i ); //return false; } StreamFormatInfo* streamFormatInfo = compoundStream->m_streamFormatInfos[ i - 1 ]; debugOutput( DEBUG_LEVEL_VERBOSE, "number of channels = %d, stream format = %d\n", streamFormatInfo->m_numberOfChannels, streamFormatInfo->m_streamFormat ); if ( clusterInfo ) { int nrOfChannels = clusterInfo->m_nrOfChannels; if ( streamFormatInfo->m_streamFormat == FormatInformation::eFHL2_AM824_MIDI_CONFORMANT ) { // 8 logical midi channels fit into 1 channel nrOfChannels = ( ( nrOfChannels + 7 ) / 8 ); } // sanity check if ( nrOfChannels != streamFormatInfo->m_numberOfChannels ) { debugOutput(DEBUG_LEVEL_NORMAL, "Number of channels " "mismatch: '%s' plug discovering reported " "%d channels for cluster '%s', while stream " "format reported %d\n", getName(), nrOfChannels, clusterInfo->m_name.c_str(), streamFormatInfo->m_numberOfChannels); } clusterInfo->m_streamFormat = streamFormatInfo->m_streamFormat; debugOutput( DEBUG_LEVEL_VERBOSE, "%s plug %d cluster info %d ('%s'): " "stream format %d\n", getName(), m_id, i, clusterInfo->m_name.c_str(), clusterInfo->m_streamFormat ); } } } FormatInformationStreamsSync* syncStream = dynamic_cast< FormatInformationStreamsSync* > ( formatInfo->m_streams ); if ( syncStream ) { m_samplingFrequency = syncStream->m_samplingFrequency; debugOutput( DEBUG_LEVEL_VERBOSE, "%s plug %d is sync stream with sampling frequency %d\n", getName(), m_id, m_samplingFrequency ); } if ( !compoundStream && !syncStream ) { debugError( "Unsupported stream format\n" ); return false; } return true; } bool Plug::discoverSupportedStreamFormats() { ExtendedStreamFormatCmd extStreamFormatCmd = setPlugAddrToStreamFormatCmd( ExtendedStreamFormatCmd::eSF_ExtendedStreamFormatInformationCommandList); extStreamFormatCmd.setVerbose( getDebugLevel() ); int i = 0; bool cmdSuccess = false; do { extStreamFormatCmd.setIndexInStreamFormat( i ); extStreamFormatCmd.setCommandType( AVCCommand::eCT_Status ); cmdSuccess = extStreamFormatCmd.fire(); if ( cmdSuccess && ( extStreamFormatCmd.getResponse() == AVCCommand::eR_Implemented ) ) { FormatInfo formatInfo; formatInfo.m_index = i; bool formatInfoIsValid = true; FormatInformationStreamsSync* syncStream = dynamic_cast< FormatInformationStreamsSync* > ( extStreamFormatCmd.getFormatInformation()->m_streams ); if ( syncStream ) { formatInfo.m_samplingFrequency = syncStream->m_samplingFrequency; formatInfo.m_isSyncStream = true ; } FormatInformationStreamsCompound* compoundStream = dynamic_cast< FormatInformationStreamsCompound* > ( extStreamFormatCmd.getFormatInformation()->m_streams ); if ( compoundStream ) { formatInfo.m_samplingFrequency = compoundStream->m_samplingFrequency; formatInfo.m_isSyncStream = false; for ( int j = 0; j < compoundStream->m_numberOfStreamFormatInfos; ++j ) { switch ( compoundStream->m_streamFormatInfos[j]->m_streamFormat ) { case AVC1394_STREAM_FORMAT_AM824_IEC60958_3: formatInfo.m_audioChannels += compoundStream->m_streamFormatInfos[j]->m_numberOfChannels; break; case AVC1394_STREAM_FORMAT_AM824_MULTI_BIT_LINEAR_AUDIO_RAW: formatInfo.m_audioChannels += compoundStream->m_streamFormatInfos[j]->m_numberOfChannels; break; case AVC1394_STREAM_FORMAT_AM824_MIDI_CONFORMANT: formatInfo.m_midiChannels += compoundStream->m_streamFormatInfos[j]->m_numberOfChannels; break; default: formatInfoIsValid = false; debugWarning("unknown stream format (0x%02x) for channel " "(%d)\n", compoundStream->m_streamFormatInfos[j]->m_streamFormat, j ); } } } if ( formatInfoIsValid ) { flushDebugOutput(); debugOutput( DEBUG_LEVEL_VERBOSE, "[%s:%d] formatInfo[%d].m_samplingFrequency " "= %d\n", getName(), m_id, i, formatInfo.m_samplingFrequency ); debugOutput( DEBUG_LEVEL_VERBOSE, "[%s:%d] formatInfo[%d].m_isSyncStream = %d\n", getName(), m_id, i, formatInfo.m_isSyncStream ); debugOutput( DEBUG_LEVEL_VERBOSE, "[%s:%d] formatInfo[%d].m_audioChannels = %d\n", getName(), m_id, i, formatInfo.m_audioChannels ); debugOutput( DEBUG_LEVEL_VERBOSE, "[%s:%d] formatInfo[%d].m_midiChannels = %d\n", getName(), m_id, i, formatInfo.m_midiChannels ); m_formatInfos.push_back( formatInfo ); flushDebugOutput(); } } ++i; } while ( cmdSuccess && ( extStreamFormatCmd.getResponse() == ExtendedStreamFormatCmd::eR_Implemented ) ); return true; } bool Plug::discoverConnectionsInput() { debugOutput( DEBUG_LEVEL_VERBOSE, "Discovering incoming connections...\n"); m_inputConnections.clear(); int sourcePlugGlobalId=getSignalSource(); if(sourcePlugGlobalId >= 0) { Plug *p=m_unit->getPlugManager().getPlug(sourcePlugGlobalId); if (p==NULL) { debugError( "Plug with global id %d not found\n",sourcePlugGlobalId ); return false; } // connected to another plug debugOutput( DEBUG_LEVEL_VERBOSE, "Plug '%s' gets signal from '%s'...\n", getName(), p->getName() ); if ( p ) { debugOutput( DEBUG_LEVEL_VERBOSE, "'(%d) %s' has a connection to '(%d) %s'\n", getGlobalId(), getName(), p->getGlobalId(), p->getName() ); addPlugConnection( m_inputConnections, *p ); } else { debugError( "no corresponding plug found for '(%d) %s'\n", getGlobalId(), getName() ); return false; } } return true; } bool Plug::discoverConnectionsOutput() { return true; } bool Plug::inquireConnnection( Plug& plug ) { SignalSourceCmd signalSourceCmd = setSrcPlugAddrToSignalCmd(); setDestPlugAddrToSignalCmd( signalSourceCmd, plug ); signalSourceCmd.setCommandType( AVCCommand::eCT_SpecificInquiry ); signalSourceCmd.setVerbose( getDebugLevel() ); if ( !signalSourceCmd.fire() ) { debugError( "Could not inquire connection between '%s' and '%s'\n", getName(), plug.getName() ); return false; } if ( signalSourceCmd.getResponse() == AVCCommand::eR_Implemented ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Connection possible between '%s' and '%s'\n", getName(), plug.getName() ); return true; } debugOutput( DEBUG_LEVEL_VERBOSE, "Connection not possible between '%s' and '%s'\n", getName(), plug.getName() ); return false; } int Plug::getSignalSource() { if((getPlugAddressType() == eAPA_PCR) || (getPlugAddressType() == eAPA_ExternalPlug)) { if (getPlugDirection() != eAPD_Output) { debugOutput(DEBUG_LEVEL_VERBOSE, "Signal Source command not valid for non-output unit plugs...\n"); return -1; } } if(getPlugAddressType() == eAPA_SubunitPlug) { if (getPlugDirection() != eAPD_Input) { debugOutput(DEBUG_LEVEL_VERBOSE, "Signal Source command not valid for non-input subunit plugs...\n"); return -1; } } SignalSourceCmd signalSourceCmd( m_unit->get1394Service() ); signalSourceCmd.setNodeId( m_unit->getConfigRom().getNodeId() ); signalSourceCmd.setSubunitType( eST_Unit ); signalSourceCmd.setSubunitId( 0xff ); SignalSubunitAddress signalSubunitAddr; signalSubunitAddr.m_subunitType = 0xFF; signalSubunitAddr.m_subunitId = 0xFF; signalSubunitAddr.m_plugId = 0xFE; signalSourceCmd.setSignalSource( signalSubunitAddr ); setDestPlugAddrToSignalCmd( signalSourceCmd, *this ); signalSourceCmd.setCommandType( AVCCommand::eCT_Status ); signalSourceCmd.setVerbose( getDebugLevel() ); if ( !signalSourceCmd.fire() ) { debugError( "Could not get signal source for '%s'\n", getName() ); return -1; } if ( signalSourceCmd.getResponse() == AVCCommand::eR_Implemented ) { SignalAddress* src=signalSourceCmd.getSignalSource(); Plug* p=NULL; if(dynamic_cast(src)) { SignalUnitAddress *usrc=dynamic_cast(src); if(usrc->m_plugId == 0xFE) { debugOutput(DEBUG_LEVEL_VERBOSE, "No/Invalid connection...\n"); return -1; } else if (usrc->m_plugId & 0x80) { p=m_unit->getPlugManager().getPlug( eST_Unit, 0xFF, 0xFF, 0xFF, eAPA_ExternalPlug, eAPD_Input, usrc->m_plugId & 0x7F ); } else { p=m_unit->getPlugManager().getPlug( eST_Unit, 0xFF, 0xFF, 0xFF, eAPA_PCR, eAPD_Input, usrc->m_plugId & 0x7F ); } } else if (dynamic_cast(src)) { SignalSubunitAddress *susrc=dynamic_cast(src); if(susrc->m_plugId == 0xFE) { debugOutput(DEBUG_LEVEL_VERBOSE, "No/Invalid connection...\n"); return -1; } else { p=m_unit->getPlugManager().getPlug( byteToSubunitType(susrc->m_subunitType), susrc->m_subunitId, 0xFF, 0xFF, eAPA_SubunitPlug, eAPD_Output, susrc->m_plugId); } } else return -1; if (p==NULL) { debugError("reported signal source plug not found for '%s'\n", getName()); return -1; } return p->getGlobalId(); } return -1; } bool Plug::setConnection( Plug& plug ) { SignalSourceCmd signalSourceCmd = setSrcPlugAddrToSignalCmd(); setDestPlugAddrToSignalCmd( signalSourceCmd, plug ); signalSourceCmd.setCommandType( AVCCommand::eCT_Control ); signalSourceCmd.setVerbose( getDebugLevel() ); if ( !signalSourceCmd.fire() ) { debugError( "Could not set connection between '%s' and '%s'\n", getName(), plug.getName() ); return false; } if ( signalSourceCmd.getResponse() == AVCCommand::eR_Accepted ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Could set connection between '%s' and '%s'\n", getName(), plug.getName() ); return true; } debugOutput( DEBUG_LEVEL_VERBOSE, "Could not set connection between '%s' and '%s'\n", getName(), plug.getName() ); return false; } bool Plug::propagateFromConnectedPlug( ) { if (getDirection() == eAPD_Output) { if (getInputConnections().size()==0) { debugOutput(DEBUG_LEVEL_NORMAL, "No input connections to propagate from, skipping.\n"); return true; } if (getInputConnections().size()>1) { debugOutput(DEBUG_LEVEL_NORMAL, "Too many input connections to propagate from, using first one.\n"); } Plug* p = *(getInputConnections().begin()); return propagateFromPlug( p ); } else if (getDirection() == eAPD_Input) { if (getOutputConnections().size()==0) { debugOutput(DEBUG_LEVEL_NORMAL, "No output connections to propagate from, skipping.\n"); return true; } if (getOutputConnections().size()>1) { debugOutput(DEBUG_LEVEL_NORMAL, "Too many output connections to propagate from, using first one.\n"); } Plug* p = *(getOutputConnections().begin()); return propagateFromPlug( p ); } else { debugError("plug with undefined direction\n"); return false; } } bool Plug::propagateFromPlug( Plug *p ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Propagating info from plug '%s' to plug '%s'\n", p->getName(), getName() ); if (m_clusterInfos.size()==0 || m_clusterInfos[0].m_buildSource == -1) { m_clusterInfos=p->m_clusterInfos; if (m_clusterInfos.size() > 0) { m_clusterInfos[0].m_buildSource = 0; // No longer a temp instance } } m_nrOfChannels=p->m_nrOfChannels; return true; } int Plug::getNrOfStreams() const { int nrOfChannels = 0; for ( ClusterInfoVector::const_iterator it = m_clusterInfos.begin(); it != m_clusterInfos.end(); ++it ) { const ClusterInfo* clusterInfo = &( *it ); nrOfChannels += clusterInfo->m_nrOfChannels; } return nrOfChannels; } int Plug::getNrOfChannels() const { return m_nrOfChannels; } bool Plug::setNrOfChannels(int i) { m_nrOfChannels=i; return true; } int Plug::getSampleRate() const { if(getPlugAddressType()==eAPA_PCR) { if(getPlugDirection()==eAPD_Input) { InputPlugSignalFormatCmd cmd( m_unit->get1394Service() ); cmd.m_form=0xFF; cmd.m_eoh=0xFF; cmd.m_fmt=0xFF; cmd.m_plug=getPlugId(); cmd.setNodeId( m_unit->getConfigRom().getNodeId() ); cmd.setSubunitType( eST_Unit ); cmd.setSubunitId( 0xff ); cmd.setCommandType( AVCCommand::eCT_Status ); if ( !cmd.fire() ) { debugError( "input plug signal format command failed\n" ); return 0; } if (cmd.m_fmt != 0x10 ) { debugWarning("Incorrect FMT response received: 0x%02X\n",cmd.m_fmt); } return fdfSfcToSampleRate(cmd.m_fdf[0]); } else if (getPlugDirection()==eAPD_Output) { OutputPlugSignalFormatCmd cmd( m_unit->get1394Service() ); cmd.m_form=0xFF; cmd.m_eoh=0xFF; cmd.m_fmt=0xFF; cmd.m_plug=getPlugId(); cmd.setNodeId( m_unit->getConfigRom().getNodeId() ); cmd.setSubunitType( eST_Unit ); cmd.setSubunitId( 0xff ); cmd.setCommandType( AVCCommand::eCT_Status ); if ( !cmd.fire() ) { debugError( "output plug signal format command failed\n" ); return 0; } if (cmd.m_fmt != 0x10 ) { debugWarning("Incorrect FMT response received: 0x%02X\n",cmd.m_fmt); } return fdfSfcToSampleRate(cmd.m_fdf[0]); } else { debugError("PCR plug with undefined direction.\n"); return 0; } } // fallback return convertESamplingFrequency( static_cast( m_samplingFrequency ) ); } bool Plug::setSampleRate( int rate ) { // apple style if(getPlugAddressType()==eAPA_PCR) { if(getPlugDirection()==eAPD_Input) { InputPlugSignalFormatCmd cmd( m_unit->get1394Service() ); cmd.m_eoh=1; cmd.m_form=0; cmd.m_fmt=0x10; cmd.m_plug=getPlugId(); cmd.m_fdf[0]=sampleRateToFdfSfc(rate); cmd.m_fdf[1]=0xFF; cmd.m_fdf[2]=0xFF; cmd.setNodeId( m_unit->getConfigRom().getNodeId() ); cmd.setSubunitType( eST_Unit ); cmd.setSubunitId( 0xff ); cmd.setCommandType( AVCCommand::eCT_Control ); if ( !cmd.fire() ) { debugError( "input plug signal format command failed\n" ); return false; } if ( cmd.getResponse() == AVCCommand::eR_Accepted ) { return true; } debugWarning( "output plug signal format command not accepted\n" ); } else if (getPlugDirection()==eAPD_Output) { OutputPlugSignalFormatCmd cmd( m_unit->get1394Service() ); cmd.m_eoh=1; cmd.m_form=0; cmd.m_fmt=0x10; cmd.m_plug=getPlugId(); cmd.m_fdf[0]=sampleRateToFdfSfc(rate); cmd.m_fdf[1]=0xFF; cmd.m_fdf[2]=0xFF; cmd.setNodeId( m_unit->getConfigRom().getNodeId() ); cmd.setSubunitType( eST_Unit ); cmd.setSubunitId( 0xff ); cmd.setCommandType( AVCCommand::eCT_Control ); if ( !cmd.fire() ) { debugError( "output plug signal format command failed\n" ); return false; } if ( cmd.getResponse() == AVCCommand::eR_Accepted ) { return true; } debugWarning( "output plug signal format command not accepted\n" ); } else { debugError("PCR plug with undefined direction.\n"); return false; } } // fallback: BeBoB style ESamplingFrequency samplingFrequency = parseSampleRate(rate); ExtendedStreamFormatCmd extStreamFormatCmd( m_unit->get1394Service(), ExtendedStreamFormatCmd::eSF_ExtendedStreamFormatInformationCommandList ); UnitPlugAddress unitPlugAddress( UnitPlugAddress::ePT_PCR, getPlugId() ); extStreamFormatCmd.setPlugAddress( PlugAddress( Plug::convertPlugDirection(getPlugDirection() ), PlugAddress::ePAM_Unit, unitPlugAddress ) ); extStreamFormatCmd.setNodeId( m_unit->getConfigRom().getNodeId() ); extStreamFormatCmd.setCommandType( AVCCommand::eCT_Status ); int i = 0; bool cmdSuccess = false; bool correctFormatFound = false; do { extStreamFormatCmd.setIndexInStreamFormat( i ); extStreamFormatCmd.setCommandType( AVCCommand::eCT_Status ); extStreamFormatCmd.setVerbose( getDebugLevel() ); cmdSuccess = extStreamFormatCmd.fire(); if ( cmdSuccess && ( extStreamFormatCmd.getResponse() == AVCCommand::eR_Implemented ) ) { ESamplingFrequency foundFreq = eSF_DontCare; FormatInformation* formatInfo = extStreamFormatCmd.getFormatInformation(); FormatInformationStreamsCompound* compoundStream = dynamic_cast< FormatInformationStreamsCompound* > ( formatInfo->m_streams ); if ( compoundStream ) { foundFreq = static_cast< ESamplingFrequency >( compoundStream->m_samplingFrequency ); } FormatInformationStreamsSync* syncStream = dynamic_cast< FormatInformationStreamsSync* > ( formatInfo->m_streams ); if ( syncStream ) { foundFreq = static_cast< ESamplingFrequency >( syncStream->m_samplingFrequency ); } if ( foundFreq == samplingFrequency ) { correctFormatFound = true; break; } } ++i; } while ( cmdSuccess && ( extStreamFormatCmd.getResponse() == ExtendedStreamFormatCmd::eR_Implemented ) ); if ( !cmdSuccess ) { debugError( "setSampleRatePlug: Failed to retrieve format info\n" ); return false; } if ( !correctFormatFound ) { debugError( "setSampleRatePlug: %s plug %d does not support " "sample rate %d\n", getName(), getPlugId(), convertESamplingFrequency( samplingFrequency ) ); return false; } extStreamFormatCmd.setSubFunction( ExtendedStreamFormatCmd::eSF_ExtendedStreamFormatInformationCommand ); extStreamFormatCmd.setCommandType( AVCCommand::eCT_Control ); extStreamFormatCmd.setVerbose( getDebugLevel() ); if ( !extStreamFormatCmd.fire() ) { debugError( "setSampleRate: Could not set sample rate %d " "to %s plug %d\n", convertESamplingFrequency( samplingFrequency ), getName(), getPlugId() ); return false; } return true; } bool Plug::supportsSampleRate( int rate ) { // fallback: BeBoB style ESamplingFrequency samplingFrequency = parseSampleRate(rate); ExtendedStreamFormatCmd extStreamFormatCmd( m_unit->get1394Service(), ExtendedStreamFormatCmd::eSF_ExtendedStreamFormatInformationCommandList ); UnitPlugAddress unitPlugAddress( UnitPlugAddress::ePT_PCR, getPlugId() ); extStreamFormatCmd.setPlugAddress( PlugAddress( Plug::convertPlugDirection(getPlugDirection() ), PlugAddress::ePAM_Unit, unitPlugAddress ) ); extStreamFormatCmd.setNodeId( m_unit->getConfigRom().getNodeId() ); extStreamFormatCmd.setCommandType( AVCCommand::eCT_Status ); int i = 0; bool cmdSuccess = false; bool correctFormatFound = false; do { extStreamFormatCmd.setIndexInStreamFormat( i ); extStreamFormatCmd.setCommandType( AVCCommand::eCT_Status ); extStreamFormatCmd.setVerbose( getDebugLevel() ); cmdSuccess = extStreamFormatCmd.fire(); if ( cmdSuccess && ( extStreamFormatCmd.getResponse() == AVCCommand::eR_Implemented ) ) { ESamplingFrequency foundFreq = eSF_DontCare; FormatInformation* formatInfo = extStreamFormatCmd.getFormatInformation(); FormatInformationStreamsCompound* compoundStream = dynamic_cast< FormatInformationStreamsCompound* > ( formatInfo->m_streams ); if ( compoundStream ) { foundFreq = static_cast< ESamplingFrequency >( compoundStream->m_samplingFrequency ); } FormatInformationStreamsSync* syncStream = dynamic_cast< FormatInformationStreamsSync* > ( formatInfo->m_streams ); if ( syncStream ) { foundFreq = static_cast< ESamplingFrequency >( syncStream->m_samplingFrequency ); } if ( foundFreq == samplingFrequency ) { correctFormatFound = true; break; } } ++i; } while ( cmdSuccess && ( extStreamFormatCmd.getResponse() == ExtendedStreamFormatCmd::eR_Implemented ) ); if ( !cmdSuccess ) { debugError( "setSampleRatePlug: Failed to retrieve format info\n" ); return false; } if ( !correctFormatFound ) { debugOutput(DEBUG_LEVEL_VERBOSE, "setSampleRatePlug: %s plug %d does not support sample rate %d\n", getName(), getPlugId(), convertESamplingFrequency( samplingFrequency ) ); return false; } return true; } bool Plug::discoverConnectionsFromSpecificData( EPlugDirection discoverDirection, PlugAddressSpecificData* plugAddress, PlugVector& connections ) { UnitPlugSpecificDataPlugAddress* pUnitPlugAddress = dynamic_cast ( plugAddress->m_plugAddressData ); SubunitPlugSpecificDataPlugAddress* pSubunitPlugAddress = dynamic_cast ( plugAddress->m_plugAddressData ); FunctionBlockPlugSpecificDataPlugAddress* pFunctionBlockPlugAddress = dynamic_cast ( plugAddress->m_plugAddressData ); Plug* plug = getPlugDefinedBySpecificData( pUnitPlugAddress, pSubunitPlugAddress, pFunctionBlockPlugAddress ); if ( plug ) { debugOutput( DEBUG_LEVEL_VERBOSE, "'(%d) %s' has a connection to '(%d) %s'\n", getGlobalId(), getName(), plug->getGlobalId(), plug->getName() ); addPlugConnection( connections, *plug ); } else { debugError( "no corresponding plug found for '(%d) %s'\n", getGlobalId(), getName() ); return false; } return true; } bool Plug::addPlugConnection( PlugVector& connections, Plug& plug ) { for ( PlugVector::iterator it = connections.begin(); it != connections.end(); ++it ) { Plug* cPlug = *it; if ( cPlug == &plug ) { debugOutput( DEBUG_LEVEL_VERBOSE, "plug '%s' already in connection list\n", plug.getName() ); return true; } } connections.push_back( &plug ); return true; } SignalSourceCmd Plug::setSrcPlugAddrToSignalCmd() { SignalSourceCmd signalSourceCmd( m_unit->get1394Service() ); switch( getSubunitType() ) { case eST_Unit: { SignalUnitAddress signalUnitAddr; if ( getPlugAddressType() == eAPA_ExternalPlug ) { signalUnitAddr.m_plugId = m_id + 0x80; } else { signalUnitAddr.m_plugId = m_id; } signalSourceCmd.setSignalSource( signalUnitAddr ); } break; case eST_Music: case eST_Audio: { SignalSubunitAddress signalSubunitAddr; signalSubunitAddr.m_subunitType = getSubunitType(); signalSubunitAddr.m_subunitId = getSubunitId(); signalSubunitAddr.m_plugId = m_id; signalSourceCmd.setSignalSource( signalSubunitAddr ); } break; default: debugError( "Unknown subunit type\n" ); } signalSourceCmd.setNodeId( m_unit->getConfigRom().getNodeId() ); signalSourceCmd.setSubunitType( eST_Unit ); signalSourceCmd.setSubunitId( 0xff ); return signalSourceCmd; } void Plug::setDestPlugAddrToSignalCmd(SignalSourceCmd& signalSourceCmd, Plug& plug) { switch( plug.getSubunitType() ) { case eST_Unit: { SignalUnitAddress signalUnitAddr; if ( plug.getPlugAddressType() == eAPA_ExternalPlug ) { signalUnitAddr.m_plugId = plug.m_id + 0x80; } else { signalUnitAddr.m_plugId = plug.m_id; } signalSourceCmd.setSignalDestination( signalUnitAddr ); } break; case eST_Music: case eST_Audio: { SignalSubunitAddress signalSubunitAddr; signalSubunitAddr.m_subunitType = plug.getSubunitType(); signalSubunitAddr.m_subunitId = plug.getSubunitId(); signalSubunitAddr.m_plugId = plug.m_id; signalSourceCmd.setSignalDestination( signalSubunitAddr ); } break; default: debugError( "Unknown subunit type\n" ); } } void Plug::debugOutputClusterInfos( int debugLevel ) { for ( ClusterInfoVector::const_iterator it = m_clusterInfos.begin(); it != m_clusterInfos.end(); ++it ) { const ClusterInfo* clusterInfo = &( *it ); debugOutput( debugLevel, "number of channels: %d\n", clusterInfo->m_nrOfChannels ); for ( ChannelInfoVector::const_iterator cit = clusterInfo->m_channelInfos.begin(); cit != clusterInfo->m_channelInfos.end(); ++cit ) { const ChannelInfo* channelInfo = &( *cit ); // Avoid unused variable warning when debug is disabled static_cast(channelInfo); debugOutput( debugLevel, "stream position: %d\n", channelInfo->m_streamPosition ); debugOutput( debugLevel, "location: %d\n", channelInfo->m_location ); } } } const Plug::ClusterInfo* Plug::getClusterInfoByIndex(int index) const { for ( Plug::ClusterInfoVector::const_iterator clit = m_clusterInfos.begin(); clit != m_clusterInfos.end(); ++clit ) { const ClusterInfo* info = &*clit; if ( info->m_index == index ) { return info; } } return 0; } PlugAddress::EPlugDirection Plug::convertPlugDirection( EPlugDirection direction ) { PlugAddress::EPlugDirection dir; switch ( direction ) { case Plug::eAPD_Input: dir = PlugAddress::ePD_Input; break; case Plug::eAPD_Output: dir = PlugAddress::ePD_Output; break; default: dir = PlugAddress::ePD_Undefined; } return dir; } std::string Plug::plugAddressTypeToString(enum EPlugAddressType t) { switch (t) { case eAPA_PCR: return string("PCR"); case eAPA_ExternalPlug: return string("External"); case eAPA_AsynchronousPlug: return string("Async"); case eAPA_SubunitPlug: return string("Subunit"); case eAPA_FunctionBlockPlug: return string("Function Block"); default: case eAPA_Undefined: return string("Undefined"); } } std::string Plug::plugTypeToString(enum EPlugType t) { switch (t) { case eAPT_IsoStream: return string("IsoStream"); case eAPT_AsyncStream: return string("AsyncStream"); case eAPT_Midi: return string("MIDI"); case eAPT_Sync: return string("Sync"); case eAPT_Analog: return string("Analog"); case eAPT_Digital: return string("Digital"); default: case eAPT_Unknown: return string("Unknown"); } } std::string Plug::plugDirectionToString(enum EPlugDirection t) { switch (t) { case eAPD_Input: return string("Input"); case eAPD_Output: return string("Output"); case eAPD_Unknown: return string("Unknown"); } return string("ERROR"); } void Plug::showPlug() const { #ifdef DEBUG debugOutput( DEBUG_LEVEL_VERBOSE, "\tName = %s\n", getName() ); debugOutput( DEBUG_LEVEL_VERBOSE, "\tType = %s\n", extendedPlugInfoPlugTypeToString( getPlugType() ) ); debugOutput( DEBUG_LEVEL_VERBOSE, "\tAddress Type = %s\n", avPlugAddressTypeToString( getPlugAddressType() ) ); debugOutput( DEBUG_LEVEL_VERBOSE, "\tId = %d\n", getPlugId() ); debugOutput( DEBUG_LEVEL_VERBOSE, "\tSubunitType = %d\n", getSubunitType() ); debugOutput( DEBUG_LEVEL_VERBOSE, "\tSubunitId = %d\n", getSubunitId() ); debugOutput( DEBUG_LEVEL_VERBOSE, "\tDirection = %s\n", avPlugDirectionToString( getPlugDirection() ) ); debugOutput( DEBUG_LEVEL_VERBOSE, "\tSampling Rate = %d\n", getSampleRate() ); debugOutput( DEBUG_LEVEL_VERBOSE, "\tNumber of Channels = %d\n", getNrOfChannels() ); debugOutput( DEBUG_LEVEL_VERBOSE, "\tNumber of Streams = %d\n", getNrOfStreams() ); debugOutput( DEBUG_LEVEL_VERBOSE, "\tIncoming connections from: "); for ( PlugVector::const_iterator it = m_inputConnections.begin(); it != m_inputConnections.end(); ++it ) { debugOutputShort(DEBUG_LEVEL_VERBOSE, "%s, ", (*it)->getName()); } debugOutputShort(DEBUG_LEVEL_VERBOSE, "\n"); debugOutput( DEBUG_LEVEL_VERBOSE, "\tOutgoing connections to: "); for ( PlugVector::const_iterator it = m_outputConnections.begin(); it != m_outputConnections.end(); ++it ) { debugOutputShort(DEBUG_LEVEL_VERBOSE, "%s, ", (*it)->getName()); } debugOutputShort(DEBUG_LEVEL_VERBOSE, "\n"); debugOutput( DEBUG_LEVEL_VERBOSE, "\tChannel info:\n"); unsigned int i=0; for ( Plug::ClusterInfoVector::const_iterator it = m_clusterInfos.begin(); it != m_clusterInfos.end(); ++it ) { const Plug::ClusterInfo* clusterInfo = &( *it ); debugOutput(DEBUG_LEVEL_VERBOSE, " Cluster %s (idx=%2d, type=0x%02X, ch=%2d, format=0x%02X)\n", clusterInfo->m_name.c_str(), i, clusterInfo->m_portType, clusterInfo->m_nrOfChannels, clusterInfo->m_streamFormat); Plug::ChannelInfoVector channelInfos = clusterInfo->m_channelInfos; for ( Plug::ChannelInfoVector::const_iterator it = channelInfos.begin(); it != channelInfos.end(); ++it ) { const Plug::ChannelInfo* channelInfo = &( *it ); debugOutput(DEBUG_LEVEL_VERBOSE, " Channel %s (pos=0x%02X, loc=0x%02X)\n", channelInfo->m_name.c_str(), channelInfo->m_streamPosition, channelInfo->m_location); } i++; } flushDebugOutput(); #endif } Plug* Plug::getPlugDefinedBySpecificData( UnitPlugSpecificDataPlugAddress* pUnitPlugAddress, SubunitPlugSpecificDataPlugAddress* pSubunitPlugAddress, FunctionBlockPlugSpecificDataPlugAddress* pFunctionBlockPlugAddress ) { subunit_type_t subunitType = 0xff; subunit_id_t subunitId = 0xff; function_block_type_t functionBlockType = 0xff; function_block_id_t functionBlockId = 0xff; EPlugAddressType addressType = eAPA_Undefined; EPlugDirection direction = eAPD_Unknown; plug_id_t plugId = 0xff; if ( !pUnitPlugAddress && !pSubunitPlugAddress && !pFunctionBlockPlugAddress ) { debugError( "No correct specific data found\n" ); return 0; } if ( pUnitPlugAddress ) { subunitType = eST_Unit; switch ( pUnitPlugAddress->m_plugType ) { case UnitPlugSpecificDataPlugAddress::ePT_PCR: addressType = eAPA_PCR; break; case UnitPlugSpecificDataPlugAddress::ePT_ExternalPlug: addressType = eAPA_ExternalPlug; break; case UnitPlugSpecificDataPlugAddress::ePT_AsynchronousPlug: addressType = eAPA_AsynchronousPlug; break; } // unit plug have only connections to subunits if ( getPlugAddressType() == eAPA_SubunitPlug ) { direction = getDirection(); } else { debugError( "Function block has connection from/to unknown " "plug type\n" ); direction = eAPD_Unknown; } plugId = pUnitPlugAddress->m_plugId; debugOutput( DEBUG_LEVEL_VERBOSE, "'(%d) %s': Remote plug is a unit plug " "(%s, %s, %d)\n", getGlobalId(), getName(), avPlugAddressTypeToString( addressType ), avPlugDirectionToString( direction ), plugId ); } if ( pSubunitPlugAddress ) { subunitType = pSubunitPlugAddress->m_subunitType; subunitId = pSubunitPlugAddress->m_subunitId; addressType = eAPA_SubunitPlug; if ( getPlugAddressType() == eAPA_FunctionBlockPlug ) { direction = getDirection(); } else if ( getPlugAddressType() == eAPA_SubunitPlug ) { direction = toggleDirection( getDirection() ); } else { // unit direction = getDirection(); } plugId = pSubunitPlugAddress->m_plugId; debugOutput( DEBUG_LEVEL_VERBOSE, "'(%d) %s': Remote plug is a subunit plug " "(%d, %d, %s, %d)\n", getGlobalId(), getName(), subunitType, subunitId, avPlugDirectionToString( direction ), plugId ); } if ( pFunctionBlockPlugAddress ) { subunitType = pFunctionBlockPlugAddress->m_subunitType; subunitId = pFunctionBlockPlugAddress->m_subunitId; functionBlockType = pFunctionBlockPlugAddress->m_functionBlockType; functionBlockId = pFunctionBlockPlugAddress->m_functionBlockId; addressType = eAPA_FunctionBlockPlug; if ( getPlugAddressType() == eAPA_FunctionBlockPlug ) { direction = toggleDirection( getDirection() ); } else if ( getPlugAddressType() == eAPA_SubunitPlug ){ direction = getDirection(); } else { debugError( "Function block has connection from/to unknown " "plug type\n" ); direction = eAPD_Unknown; } plugId = pFunctionBlockPlugAddress->m_plugId; debugOutput( DEBUG_LEVEL_VERBOSE, "'(%d) %s': Remote plug is a functionblock plug " "(%d, %d, %d, %d, %s, %d)\n", getGlobalId(), getName(), subunitType, subunitId, functionBlockType, functionBlockId, avPlugDirectionToString( direction ), plugId ); } ESubunitType enumSubunitType = static_cast( subunitType ); return m_unit->getPlugManager().getPlug( enumSubunitType, subunitId, functionBlockType, functionBlockId, addressType, direction, plugId ); } Plug::EPlugDirection Plug::toggleDirection( EPlugDirection direction ) const { EPlugDirection newDirection; switch ( direction ) { case eAPD_Output: newDirection = eAPD_Input; break; case eAPD_Input: newDirection = eAPD_Output; break; default: newDirection = direction; } return newDirection; } bool Plug::serializeChannelInfos( std::string basePath, Util::IOSerialize& ser, const ClusterInfo& clusterInfo ) const { bool result = true; int i = 0; for ( ChannelInfoVector::const_iterator it = clusterInfo.m_channelInfos.begin(); it != clusterInfo.m_channelInfos.end(); ++it ) { const ChannelInfo& info = *it; std::ostringstream strstrm; strstrm << basePath << i; result &= ser.write( strstrm.str() + "/m_streamPosition", info.m_streamPosition ); result &= ser.write( strstrm.str() + "/m_location", info.m_location ); result &= ser.write( strstrm.str() + "/m_name", info.m_name ); i++; } return result; } bool Plug::deserializeChannelInfos( std::string basePath, Util::IODeserialize& deser, ClusterInfo& clusterInfo ) { int i = 0; bool bFinished = false; bool result = true; do { std::ostringstream strstrm; strstrm << basePath << i; // check for one element to exist. when one exist the other elements // must also be there. otherwise just return (last) result. if ( deser.isExisting( strstrm.str() + "/m_streamPosition" ) ) { ChannelInfo info; result &= deser.read( strstrm.str() + "/m_streamPosition", info.m_streamPosition ); result &= deser.read( strstrm.str() + "/m_location", info.m_location ); result &= deser.read( strstrm.str() + "/m_name", info.m_name ); if ( result ) { clusterInfo.m_channelInfos.push_back( info ); i++; } else { bFinished = true; } } else { bFinished = true; } } while ( !bFinished ); return result; } bool Plug::serializeClusterInfos( std::string basePath, Util::IOSerialize& ser ) const { bool result = true; int i = 0; for ( ClusterInfoVector::const_iterator it = m_clusterInfos.begin(); it != m_clusterInfos.end(); ++it ) { const ClusterInfo& info = *it; std::ostringstream strstrm; strstrm << basePath << i; result &= ser.write( strstrm.str() + "/m_index", info.m_index ); result &= ser.write( strstrm.str() + "/m_portType", info.m_portType ); result &= ser.write( strstrm.str() + "/m_name", info.m_name ); result &= ser.write( strstrm.str() + "/m_nrOfChannels", info.m_nrOfChannels ); result &= serializeChannelInfos( strstrm.str() + "/m_channelInfo", ser, info ); result &= ser.write( strstrm.str() + "/m_streamFormat", info.m_streamFormat ); i++; } return result; } bool Plug::deserializeClusterInfos( std::string basePath, Util::IODeserialize& deser ) { int i = 0; bool bFinished = false; bool result = true; do { std::ostringstream strstrm; strstrm << basePath << i; // check for one element to exist. when one exist the other elements // must also be there. otherwise just return (last) result. if ( deser.isExisting( strstrm.str() + "/m_index" ) ) { ClusterInfo info; result &= deser.read( strstrm.str() + "/m_index", info.m_index ); result &= deser.read( strstrm.str() + "/m_portType", info.m_portType ); result &= deser.read( strstrm.str() + "/m_name", info.m_name ); result &= deser.read( strstrm.str() + "/m_nrOfChannels", info.m_nrOfChannels ); result &= deserializeChannelInfos( strstrm.str() + "/m_channelInfo", deser, info ); result &= deser.read( strstrm.str() + "/m_streamFormat", info.m_streamFormat ); if ( result ) { m_clusterInfos.push_back( info ); i++; } else { bFinished = true; } } else { bFinished = true; } } while ( !bFinished ); return result; } bool Plug::serializeFormatInfos( std::string basePath, Util::IOSerialize& ser ) const { bool result = true; int i = 0; for ( FormatInfoVector::const_iterator it = m_formatInfos.begin(); it != m_formatInfos.end(); ++it ) { const FormatInfo& info = *it; std::ostringstream strstrm; strstrm << basePath << i; result &= ser.write( strstrm.str() + "/m_samplingFrequency", info.m_samplingFrequency ); result &= ser.write( strstrm.str() + "/m_isSyncStream", info.m_isSyncStream ); result &= ser.write( strstrm.str() + "/m_audioChannels", info.m_audioChannels ); result &= ser.write( strstrm.str() + "/m_midiChannels", info.m_midiChannels ); result &= ser.write( strstrm.str() + "/m_index", info.m_index ); i++; } return result; } bool Plug::deserializeFormatInfos( std::string basePath, Util::IODeserialize& deser ) { int i = 0; bool bFinished = false; bool result = true; do { std::ostringstream strstrm; strstrm << basePath << i; // check for one element to exist. when one exist the other elements // must also be there. otherwise just return (last) result. if ( deser.isExisting( strstrm.str() + "/m_samplingFrequency" ) ) { FormatInfo info; result &= deser.read( strstrm.str() + "/m_samplingFrequency", info.m_samplingFrequency ); result &= deser.read( strstrm.str() + "/m_isSyncStream", info.m_isSyncStream ); result &= deser.read( strstrm.str() + "/m_audioChannels", info.m_audioChannels ); result &= deser.read( strstrm.str() + "/m_midiChannels", info.m_midiChannels ); result &= deser.read( strstrm.str() + "/m_index", info.m_index ); if ( result ) { m_formatInfos.push_back( info ); i++; } else { bFinished = true; } } else { bFinished = true; } } while ( !bFinished ); return result; } bool Plug::serialize( std::string basePath, Util::IOSerialize& ser ) const { bool result=true; result &= ser.write( basePath + "m_subunitType", getSubunitType()); result &= ser.write( basePath + "m_subunitId", getSubunitId()); result &= ser.write( basePath + "m_functionBlockType", m_functionBlockType); result &= ser.write( basePath + "m_functionBlockId", m_functionBlockId); result &= ser.write( basePath + "m_addressType", m_addressType ); result &= ser.write( basePath + "m_direction", m_direction); result &= ser.write( basePath + "m_id", m_id); result &= ser.write( basePath + "m_infoPlugType", m_infoPlugType); result &= ser.write( basePath + "m_nrOfChannels", m_nrOfChannels); result &= ser.write( basePath + "m_name", m_name); result &= serializeClusterInfos( basePath + "m_clusterInfos", ser ); result &= ser.write( basePath + "m_samplingFrequency", m_samplingFrequency); result &= serializeFormatInfos( basePath + "m_formatInfo", ser ); result &= serializePlugVector( basePath + "m_inputConnections", ser, m_inputConnections ); result &= serializePlugVector( basePath + "m_outputConnections", ser, m_outputConnections ); result &= ser.write( basePath + "m_globalId", m_globalId); return result; } Plug* Plug::deserialize( std::string basePath, Util::IODeserialize& deser, Unit& unit, PlugManager& plugManager ) { ESubunitType subunitType; subunit_t subunitId; function_block_type_t functionBlockType; function_block_id_t functionBlockId; EPlugAddressType addressType; EPlugDirection direction; plug_id_t id; int globalId; if ( !deser.isExisting( basePath + "m_subunitType" ) ) { return 0; } bool result=true; result = deser.read( basePath + "m_subunitType", subunitType ); result &= deser.read( basePath + "m_subunitId", subunitId ); Subunit* subunit = unit.getSubunit( subunitType, subunitId ); result &= deser.read( basePath + "m_functionBlockType", functionBlockType ); result &= deser.read( basePath + "m_functionBlockId", functionBlockId ); result &= deser.read( basePath + "m_addressType", addressType ); result &= deser.read( basePath + "m_direction", direction ); result &= deser.read( basePath + "m_id", id ); result &= deser.read( basePath + "m_globalId", globalId ); Plug* pPlug = unit.createPlug( &unit, subunit, functionBlockType, functionBlockId, addressType, direction, id, globalId); if ( !pPlug ) { return 0; } // this is needed to allow for the update of the subunit pointer later on // in the deserializeUpdateSubunit. pPlug->m_subunitType = subunitType; pPlug->m_subunitId = subunitId; result &= deser.read( basePath + "m_infoPlugType", pPlug->m_infoPlugType ); result &= deser.read( basePath + "m_nrOfChannels", pPlug->m_nrOfChannels ); result &= deser.read( basePath + "m_name", pPlug->m_name ); result &= pPlug->deserializeClusterInfos( basePath + "m_clusterInfos", deser ); result &= deser.read( basePath + "m_samplingFrequency", pPlug->m_samplingFrequency ); result &= pPlug->deserializeFormatInfos( basePath + "m_formatInfos", deser ); // input and output connections can't be processed here because not all plugs might // deserialized at this point. so we do that in deserializeUpdate. if ( !result ) { delete pPlug; return 0; } return pPlug; } bool Plug::deserializeConnections( std::string basePath, Util::IODeserialize& deser ) { bool result; result = deserializePlugVector( basePath + "/m_inputConnections", deser, m_unit->getPlugManager(), m_inputConnections ); result &= deserializePlugVector( basePath + "/m_outputConnections", deser, m_unit->getPlugManager(), m_outputConnections ); return result; } bool Plug::deserializeUpdateSubunit() { m_subunit = m_unit->getSubunit( m_subunitType, m_subunitId ); return true; } ///////////////////////////////////////// ///////////////////////////////////////// const char* avPlugAddressTypeStrings[] = { "PCR", "external", "asynchronous", "subunit", "functionblock", "undefined", }; const char* avPlugAddressTypeToString( Plug::EPlugAddressType type ) { if ( type > ( int )( sizeof( avPlugAddressTypeStrings ) / sizeof( avPlugAddressTypeStrings[0] ) ) ) { type = Plug::eAPA_Undefined; } return avPlugAddressTypeStrings[type]; } const char* avPlugTypeStrings[] = { "IsoStream", "AsyncStream", "MIDI", "Sync", "Analog", "Digital", "Unknown", }; const char* avPlugTypeToString( Plug::EPlugType type ) { if ( type > ( int )( sizeof( avPlugTypeStrings ) / sizeof( avPlugTypeStrings[0] ) ) ) { type = Plug::eAPT_Unknown; } return avPlugTypeStrings[type]; } const char* avPlugDirectionStrings[] = { "Input", "Output", "Unknown", }; const char* avPlugDirectionToString( Plug::EPlugDirection type ) { if ( type > ( int )( sizeof( avPlugDirectionStrings ) / sizeof( avPlugDirectionStrings[0] ) ) ) { type = Plug::eAPD_Unknown; } return avPlugDirectionStrings[type]; } ///////////////////////////////////// PlugManager::PlugManager( ) : m_globalIdCounter( 0 ) { } PlugManager::PlugManager( const PlugManager& rhs ) : m_globalIdCounter( rhs.m_globalIdCounter ) { setDebugLevel( rhs.getDebugLevel() ); } PlugManager::~PlugManager() { } void PlugManager::setVerboseLevel( int l ) { setDebugLevel(l); for ( PlugVector::iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { (*it)->setVerboseLevel(l); } debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); } bool PlugManager::addPlug( Plug& plug ) { m_plugs.push_back( &plug ); // inherit debug level plug.setVerboseLevel(getDebugLevel()); return true; } bool PlugManager::remPlug( Plug& plug ) { for ( PlugVector::iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* plugIt = *it; if ( plugIt == &plug ) { m_plugs.erase( it ); return true; } } return false; } // helper function static void addConnection( PlugConnectionVector& connections, Plug& srcPlug, Plug& destPlug ) { for ( PlugConnectionVector::iterator it = connections.begin(); it != connections.end(); ++it ) { PlugConnection* con = *it; if ( ( &( con->getSrcPlug() ) == &srcPlug ) && ( &( con->getDestPlug() ) == &destPlug ) ) { return; } } connections.push_back( new PlugConnection( srcPlug, destPlug ) ); } bool PlugManager::tidyPlugConnections(PlugConnectionVector& connections) { connections.clear(); for ( PlugVector::const_iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* plug = *it; for ( PlugVector::const_iterator it = plug->getInputConnections().begin(); it != plug->getInputConnections().end(); ++it ) { addConnection( connections, *( *it ), *plug ); } plug->getInputConnections().clear(); for ( PlugVector::const_iterator it = plug->getOutputConnections().begin(); it != plug->getOutputConnections().end(); ++it ) { addConnection( connections, *plug, *( *it ) ); } plug->getOutputConnections().clear(); } for ( PlugConnectionVector::iterator it = connections.begin(); it != connections.end(); ++it ) { PlugConnection * con = *it; con->getSrcPlug().getOutputConnections().push_back(&( con->getDestPlug() )); con->getDestPlug().getInputConnections().push_back(&( con->getSrcPlug() )); } return true; } static void addConnectionOwner( PlugConnectionOwnerVector& connections, Plug& srcPlug, Plug& destPlug ) { for ( PlugConnectionOwnerVector::iterator it = connections.begin(); it != connections.end(); ++it ) { PlugConnection& con = *it; if ( ( &( con.getSrcPlug() ) == &srcPlug ) && ( &( con.getDestPlug() ) == &destPlug ) ) { return; } } connections.push_back( PlugConnection( srcPlug, destPlug ) ); } void PlugManager::showPlugs() const { if(getDebugLevel() < DEBUG_LEVEL_INFO) return; // \todo The information provided here could be better arranged. For a start it is ok, but // there is room for improvement. Something for a lazy sunday afternoon (tip: maybe drink some // beer to get into the mood) printf( "\nSummary\n" ); printf( "-------\n\n" ); printf( "Nr | AddressType | Direction | SubUnitType | SubUnitId | FunctionBlockType | FunctionBlockId | Id | Type |Name\n" ); printf( "---+-----------------+-----------+-------------+-----------+-------------------+-----------------+------+--------------+------\n" ); for ( PlugVector::const_iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* plug = *it; printf( "%2d | %15s | %9s | %11s | 0x%02x | 0x%02x | 0x%02x | 0x%02x | %12s | %s\n", plug->getGlobalId(), avPlugAddressTypeToString( plug->getPlugAddressType() ), avPlugDirectionToString( plug->getDirection() ), subunitTypeToString( plug->getSubunitType() ), plug->getSubunitId(), plug->getFunctionBlockType(), plug->getFunctionBlockId(), plug->getPlugId(), avPlugTypeToString( plug->getPlugType() ), plug->getName() ); } printf( "\nConnections\n" ); printf( "-----------\n" ); PlugConnectionOwnerVector connections; for ( PlugVector::const_iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* plug = *it; for ( PlugVector::const_iterator it = plug->getInputConnections().begin(); it != plug->getInputConnections().end(); ++it ) { addConnectionOwner( connections, *( *it ), *plug ); } for ( PlugVector::const_iterator it = plug->getOutputConnections().begin(); it != plug->getOutputConnections().end(); ++it ) { addConnectionOwner( connections, *plug, *( *it ) ); } } printf( "digraph avcconnections {\n" ); for ( PlugConnectionOwnerVector::iterator it = connections.begin(); it != connections.end(); ++it ) { PlugConnection& con = *it; printf( "\t\"(%d) %s\" -> \"(%d) %s\"\n", con.getSrcPlug().getGlobalId(), con.getSrcPlug().getName(), con.getDestPlug().getGlobalId(), con.getDestPlug().getName() ); } for ( PlugVector::const_iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* plug = *it; if ( plug->getFunctionBlockType() != 0xff ) { std::ostringstream strstrm; switch(plug->getFunctionBlockType()) { case 0x80: strstrm << "Selector FB"; break; case 0x81: strstrm << "Feature FB"; break; case 0x82: strstrm << "Processing FB"; break; case 0x83: strstrm << "CODEC FB"; break; default: strstrm << plug->getFunctionBlockType(); } if ( plug->getPlugDirection() == Plug::eAPD_Input ) { printf( "\t\"(%d) %s\" -> \"(%s, ID %d)\"\n", plug->getGlobalId(), plug->getName(), strstrm.str().c_str(), plug->getFunctionBlockId() ); } else { printf( "\t\"(%s, ID %d)\" -> \t\"(%d) %s\"\n", strstrm.str().c_str(), plug->getFunctionBlockId(), plug->getGlobalId(), plug->getName() ); } } } const char* colorStrings[] = { "coral", "slateblue", "white", "green", "yellow", "grey", }; for ( PlugVector::const_iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* plug = *it; printf( "\t\"(%d) %s\" [color=%s,style=filled];\n", plug->getGlobalId(), plug->getName(), colorStrings[plug->getPlugAddressType() ] ); } printf("}\n" ); printf( "Use \"dot -Tps FILENAME.dot -o FILENAME.ps\" " "to generate graph\n"); debugOutput( DEBUG_LEVEL_VERBOSE, "Plug details\n" ); debugOutput( DEBUG_LEVEL_VERBOSE, "------------\n" ); for ( PlugVector::const_iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* plug = *it; debugOutput( DEBUG_LEVEL_VERBOSE, "Plug %d:\n", plug->getGlobalId() ); plug->showPlug(); } } Plug* PlugManager::getPlug( ESubunitType subunitType, subunit_id_t subunitId, function_block_type_t functionBlockType, function_block_id_t functionBlockId, Plug::EPlugAddressType plugAddressType, Plug::EPlugDirection plugDirection, plug_id_t plugId ) const { debugOutput( DEBUG_LEVEL_VERBOSE, "SBT, SBID, FBT, FBID, AT, PD, ID = " "(0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x)\n", subunitType, subunitId, functionBlockType, functionBlockId, plugAddressType, plugDirection, plugId ); for ( PlugVector::const_iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* plug = *it; if ( ( subunitType == plug->getSubunitType() ) && ( subunitId == plug->getSubunitId() ) && ( functionBlockType == plug->getFunctionBlockType() ) && ( functionBlockId == plug->getFunctionBlockId() ) && ( plugAddressType == plug->getPlugAddressType() ) && ( plugDirection == plug->getPlugDirection() ) && ( plugId == plug->getPlugId() ) ) { return plug; } } return 0; } Plug* PlugManager::getPlug( int iGlobalId ) const { for ( PlugVector::const_iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* pPlug = *it; if ( pPlug->getGlobalId() == iGlobalId ) { return pPlug; } } return 0; } PlugVector PlugManager::getPlugsByType( ESubunitType subunitType, subunit_id_t subunitId, function_block_type_t functionBlockType, function_block_id_t functionBlockId, Plug::EPlugAddressType plugAddressType, Plug::EPlugDirection plugDirection, Plug::EPlugType type) const { debugOutput( DEBUG_LEVEL_VERBOSE, "SBT, SBID, FBT, FBID, AT, PD, T = " "(0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x)\n", subunitType, subunitId, functionBlockType, functionBlockId, plugAddressType, plugDirection, type ); PlugVector plugVector; for ( PlugVector::const_iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* pPlug = *it; if ( ( subunitType == pPlug->getSubunitType() ) && ( subunitId == pPlug->getSubunitId() ) && ( functionBlockType == pPlug->getFunctionBlockType() ) && ( functionBlockId == pPlug->getFunctionBlockId() ) && ( plugAddressType == pPlug->getPlugAddressType() ) && ( plugDirection == pPlug->getPlugDirection() ) && ( type == pPlug->getPlugType() ) ) { plugVector.push_back( pPlug ); } } return plugVector; } bool PlugManager::serialize( std::string basePath, Util::IOSerialize& ser ) const { bool result = true; int i = 0; for ( PlugVector::const_iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* pPlug = *it; std::ostringstream strstrm; strstrm << basePath << i; result &= pPlug->serialize( strstrm.str() + "/", ser ); i++; } result &= ser.write( basePath + "m_globalIdCounter", m_globalIdCounter ); return result; } PlugManager* PlugManager::deserialize( std::string basePath, Util::IODeserialize& deser, Unit& unit ) { PlugManager* pMgr = new PlugManager; if ( !pMgr ) { return 0; } if(!deser.read( basePath + "m_globalIdCounter", pMgr->m_globalIdCounter )) { pMgr->m_globalIdCounter = 0; } int i = 0; Plug* pPlug = 0; do { std::ostringstream strstrm; strstrm << basePath << i; // unit still holds a null pointer for the plug manager // therefore we have to *this as additional argument pPlug = Plug::deserialize( strstrm.str() + "/", deser, unit, *pMgr ); if ( pPlug ) { pMgr->m_plugs.push_back( pPlug ); i++; } } while ( pPlug ); return pMgr; } bool PlugManager::deserializeUpdate( std::string basePath, Util::IODeserialize& deser) { bool result = true; for ( PlugVector::const_iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* pPlug = *it; std::ostringstream strstrm; strstrm << basePath << "Plug" << pPlug->getGlobalId(); result &= pPlug->deserializeConnections( strstrm.str(), deser ); result &= pPlug->deserializeUpdateSubunit(); } return result; } //////////////////////////////////// PlugConnection::PlugConnection( Plug& srcPlug, Plug& destPlug ) : m_srcPlug( &srcPlug ) , m_destPlug( &destPlug ) { } PlugConnection::PlugConnection() : m_srcPlug( 0 ) , m_destPlug( 0 ) { } bool PlugConnection::serialize( std::string basePath, Util::IOSerialize& ser ) const { bool result; result = ser.write( basePath + "m_srcPlug", m_srcPlug->getGlobalId() ); result &= ser.write( basePath + "m_destPlug", m_destPlug->getGlobalId() ); return result; } PlugConnection* PlugConnection::deserialize( std::string basePath, Util::IODeserialize& deser, Unit& unit ) { if ( !deser.isExisting( basePath + "m_srcPlug" ) ) { return 0; } PlugConnection* pConnection = new PlugConnection; if ( !pConnection ) { return 0; } bool result; int iSrcPlugId; int iDestPlugId; result = deser.read( basePath + "m_srcPlug", iSrcPlugId ); result &= deser.read( basePath + "m_destPlug", iDestPlugId ); if ( !result ) { delete pConnection; return 0; } pConnection->m_srcPlug = unit.getPlugManager().getPlug( iSrcPlugId ); pConnection->m_destPlug = unit.getPlugManager().getPlug( iDestPlugId ); if ( !pConnection->m_srcPlug || !pConnection->m_destPlug ) { delete pConnection; return 0; } return pConnection; } ExtendedStreamFormatCmd Plug::setPlugAddrToStreamFormatCmd( ExtendedStreamFormatCmd::ESubFunction subFunction) { ExtendedStreamFormatCmd extStreamFormatInfoCmd( m_unit->get1394Service(), subFunction ); switch( getSubunitType() ) { case eST_Unit: { UnitPlugAddress::EPlugType ePlugType = UnitPlugAddress::ePT_Unknown; switch ( m_addressType ) { case eAPA_PCR: ePlugType = UnitPlugAddress::ePT_PCR; break; case eAPA_ExternalPlug: ePlugType = UnitPlugAddress::ePT_ExternalPlug; break; case eAPA_AsynchronousPlug: ePlugType = UnitPlugAddress::ePT_AsynchronousPlug; break; default: ePlugType = UnitPlugAddress::ePT_Unknown; } UnitPlugAddress unitPlugAddress( ePlugType, m_id ); extStreamFormatInfoCmd.setPlugAddress( PlugAddress( convertPlugDirection( getPlugDirection() ), PlugAddress::ePAM_Unit, unitPlugAddress ) ); } break; case eST_Music: case eST_Audio: { switch( m_addressType ) { case eAPA_SubunitPlug: { SubunitPlugAddress subunitPlugAddress( m_id ); extStreamFormatInfoCmd.setPlugAddress( PlugAddress( convertPlugDirection( getPlugDirection() ), PlugAddress::ePAM_Subunit, subunitPlugAddress ) ); } break; case eAPA_FunctionBlockPlug: { FunctionBlockPlugAddress functionBlockPlugAddress( m_functionBlockType, m_functionBlockId, m_id ); extStreamFormatInfoCmd.setPlugAddress( PlugAddress( convertPlugDirection( getPlugDirection() ), PlugAddress::ePAM_FunctionBlock, functionBlockPlugAddress ) ); } break; default: extStreamFormatInfoCmd.setPlugAddress(PlugAddress()); } } break; default: debugError( "Unknown subunit type\n" ); } extStreamFormatInfoCmd.setNodeId( m_unit->getConfigRom().getNodeId() ); extStreamFormatInfoCmd.setCommandType( AVCCommand::eCT_Status ); extStreamFormatInfoCmd.setSubunitId( getSubunitId() ); extStreamFormatInfoCmd.setSubunitType( getSubunitType() ); return extStreamFormatInfoCmd; } bool serializePlugVector( std::string basePath, Util::IOSerialize& ser, const PlugVector& vec) { bool result = true; int i = 0; for ( PlugVector::const_iterator it = vec.begin(); it != vec.end(); ++it ) { const Plug* pPlug = *it; std::ostringstream strstrm; strstrm << basePath << i; result &= ser.write( strstrm.str() + "/global_id", pPlug->getGlobalId() ); i++; } return result; } bool deserializePlugVector( std::string basePath, Util::IODeserialize& deser, const PlugManager& plugManager, PlugVector& vec ) { int i = 0; Plug* pPlug = 0; do { std::ostringstream strstrm; unsigned int iPlugId; strstrm << basePath << i; if ( !deser.isExisting( strstrm.str() + "/global_id" ) ) { // no more plugs found, so this is the normal return point return true; } if ( !deser.read( strstrm.str() + "/global_id", iPlugId ) ) { return false; } pPlug = plugManager.getPlug( iPlugId ); if ( pPlug ) { vec.push_back( pPlug ); i++; } } while ( pPlug ); // if we reach here, the plug manager didn't find any plug with the id iPlugId. return false; } } libffado-2.4.5/src/libavc/general/avc_plug.h0000644000175000001440000003063714206145246020312 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVC_PLUG #define AVC_PLUG #include "../ccm/avc_signal_source.h" #include "../streamformat/avc_extended_stream_format.h" #include "../avc_definitions.h" #include "avc_generic.h" #include "libutil/serialize.h" #include "debugmodule/debugmodule.h" #include class Ieee1394Service; class ConfigRom; namespace AVC { class Unit; class Subunit; class PlugManager; class Plug; typedef std::vector PlugVector; class PlugConnection; typedef std::vector PlugConnectionVector; class Plug { public: enum EPlugAddressType { eAPA_PCR, eAPA_ExternalPlug, eAPA_AsynchronousPlug, eAPA_SubunitPlug, eAPA_FunctionBlockPlug, eAPA_Undefined, }; std::string plugAddressTypeToString(enum EPlugAddressType t); enum EPlugType { eAPT_IsoStream, eAPT_AsyncStream, eAPT_Midi, eAPT_Sync, eAPT_Analog, eAPT_Digital, eAPT_Unknown, }; std::string plugTypeToString(enum EPlugType t); enum EPlugDirection { eAPD_Input, eAPD_Output, eAPD_Unknown, }; std::string plugDirectionToString(enum EPlugDirection t); // \todo This constructors sucks. too many parameters. fix it. Plug( Unit* unit, Subunit* subunit, function_block_type_t functionBlockType, function_block_type_t functionBlockId, EPlugAddressType plugAddressType, EPlugDirection plugDirection, plug_id_t plugId ); Plug( Unit* unit, Subunit* subunit, function_block_type_t functionBlockType, function_block_type_t functionBlockId, EPlugAddressType plugAddressType, EPlugDirection plugDirection, plug_id_t plugId, int globalId ); Plug( const Plug& rhs ); virtual ~Plug(); bool inquireConnnection( Plug& plug ); bool setConnection( Plug& plug ); int getSignalSource(); int getGlobalId() const { return m_globalId; } plug_id_t getPlugId() const { return m_id; } ESubunitType getSubunitType() const; subunit_id_t getSubunitId() const; const char* getName() const { return m_name.c_str(); } bool setName(const char *n) { m_name=n; return true; } bool setName(std::string n) { m_name=n; return true; } EPlugDirection getPlugDirection() const { return m_direction; } bool setPlugDirection(EPlugDirection d) { m_direction=d; return true; } sampling_frequency_t getSamplingFrequency() const { return m_samplingFrequency; } int getSampleRate() const; // 22050, 24000, 32000, ... bool setSampleRate( int rate ); bool supportsSampleRate( int rate ); int getNrOfChannels() const; bool setNrOfChannels(int i); int getNrOfStreams() const; // FIXME: this is the same as getPlugDirection EPlugDirection getDirection() const { return m_direction; } EPlugAddressType getPlugAddressType() const { return m_addressType; } EPlugType getPlugType() const { return m_infoPlugType; } bool setPlugType(EPlugType t) { m_infoPlugType=t; return true; } function_block_type_t getFunctionBlockType() const { return m_functionBlockType; } function_block_id_t getFunctionBlockId() const { return m_functionBlockId; } // const PlugVector& getInputConnections() const // { return m_inputConnections; } // const PlugVector& getOutputConnections() const // { return m_outputConnections; } PlugVector& getInputConnections() { return m_inputConnections; } PlugVector& getOutputConnections() { return m_outputConnections; } static PlugAddress::EPlugDirection convertPlugDirection( EPlugDirection direction); void showPlug() const; bool serialize( std::string basePath, Util::IOSerialize& ser ) const; static Plug* deserialize( std::string basePath, Util::IODeserialize& deser, Unit& avDevice, PlugManager& plugManager ); bool deserializeConnections( std::string basePath, Util::IODeserialize& deser ); bool deserializeUpdateSubunit(); public: struct ChannelInfo { stream_position_t m_streamPosition; stream_position_location_t m_location; std::string m_name; }; typedef std::vector ChannelInfoVector; struct ClusterInfo { int m_index; port_type_t m_portType; std::string m_name; nr_of_channels_t m_nrOfChannels; ChannelInfoVector m_channelInfos; stream_format_t m_streamFormat; int m_buildSource; // To track how we are instantiated }; typedef std::vector ClusterInfoVector; ClusterInfoVector& getClusterInfos() { return m_clusterInfos; } virtual void setVerboseLevel( int i ); // the discovery interface // everything can be overloaded, or // can be left as is public: virtual bool discover(); virtual bool discoverConnections(); virtual bool propagateFromConnectedPlug( ); protected: virtual bool discoverPlugType(); virtual bool discoverName(); virtual bool discoverNoOfChannels(); virtual bool discoverChannelPosition(); virtual bool discoverChannelName(); virtual bool discoverClusterInfo(); virtual bool discoverStreamFormat(); virtual bool discoverSupportedStreamFormats(); virtual bool discoverConnectionsInput(); virtual bool discoverConnectionsOutput(); virtual bool initFromDescriptor(); bool propagateFromPlug( Plug *p ); ExtendedStreamFormatCmd setPlugAddrToStreamFormatCmd( ExtendedStreamFormatCmd::ESubFunction subFunction); protected: Plug(); SignalSourceCmd setSrcPlugAddrToSignalCmd(); void setDestPlugAddrToSignalCmd( SignalSourceCmd& signalSourceCmd, Plug& plug ); void debugOutputClusterInfos( int debugLevel ); bool addPlugConnection( PlugVector& connections, Plug& plug ); bool discoverConnectionsFromSpecificData( EPlugDirection discoverDirection, PlugAddressSpecificData* plugAddress, PlugVector& connections ); Plug* getPlugDefinedBySpecificData( UnitPlugSpecificDataPlugAddress* pUnitPlugAddress, SubunitPlugSpecificDataPlugAddress* pSubunitPlugAddress, FunctionBlockPlugSpecificDataPlugAddress* pFunctionBlockPlugAddress ); EPlugDirection toggleDirection( EPlugDirection direction ) const; const ClusterInfo* getClusterInfoByIndex( int index ) const; bool serializeChannelInfos( std::string basePath, Util::IOSerialize& ser, const ClusterInfo& clusterInfo ) const; bool deserializeChannelInfos( std::string basePath, Util::IODeserialize& deser, ClusterInfo& clusterInfo ); bool serializeClusterInfos( std::string basePath, Util::IOSerialize& ser ) const; bool deserializeClusterInfos( std::string basePath, Util::IODeserialize& deser ); bool serializeFormatInfos( std::string basePath, Util::IOSerialize& ser ) const; bool deserializeFormatInfos( std::string basePath, Util::IODeserialize& deser ); protected: // Supported stream formats struct FormatInfo { FormatInfo() : m_samplingFrequency( eSF_DontCare ) , m_isSyncStream( false ) , m_audioChannels( 0 ) , m_midiChannels( 0 ) , m_index( 0xff ) {} sampling_frequency_t m_samplingFrequency; bool m_isSyncStream; number_of_channels_t m_audioChannels; number_of_channels_t m_midiChannels; byte_t m_index; }; typedef std::vector FormatInfoVector; Unit* m_unit; Subunit* m_subunit; ESubunitType m_subunitType; subunit_t m_subunitId; function_block_type_t m_functionBlockType; function_block_id_t m_functionBlockId; EPlugAddressType m_addressType; EPlugDirection m_direction; plug_id_t m_id; EPlugType m_infoPlugType; nr_of_channels_t m_nrOfChannels; std::string m_name; ClusterInfoVector m_clusterInfos; sampling_frequency_t m_samplingFrequency; FormatInfoVector m_formatInfos; PlugVector m_inputConnections; PlugVector m_outputConnections; int m_globalId; DECLARE_DEBUG_MODULE; }; const char* avPlugAddressTypeToString( Plug::EPlugAddressType addressType ); const char* avPlugTypeToString( Plug::EPlugType type ); const char* avPlugDirectionToString( Plug::EPlugDirection direction ); class PlugManager { public: PlugManager( ); PlugManager( const PlugManager& rhs ); ~PlugManager(); bool addPlug( Plug& plug ); bool remPlug( Plug& plug ); void showPlugs() const; int getPlugCount() { return m_plugs.size(); }; int requestNewGlobalId() { return m_globalIdCounter++; }; Plug* getPlug( ESubunitType subunitType, subunit_id_t subunitId, function_block_type_t functionBlockType, function_block_id_t functionBlockId, Plug::EPlugAddressType plugAddressType, Plug::EPlugDirection plugDirection, plug_id_t plugId ) const; Plug* getPlug( int iGlobalId ) const; PlugVector getPlugsByType( ESubunitType subunitType, subunit_id_t subunitId, function_block_type_t functionBlockType, function_block_id_t functionBlockId, Plug::EPlugAddressType plugAddressType, Plug::EPlugDirection plugDirection, Plug::EPlugType type) const; bool serialize( std::string basePath, Util::IOSerialize& ser ) const; static PlugManager* deserialize( std::string basePath, Util::IODeserialize& deser, Unit& avDevice ); void setVerboseLevel( int i ); PlugVector& getPlugs() { return m_plugs; } bool tidyPlugConnections(PlugConnectionVector&); bool deserializeUpdate( std::string basePath, Util::IODeserialize& deser ); private: int m_globalIdCounter; PlugVector m_plugs; DECLARE_DEBUG_MODULE; }; class PlugConnection { public: PlugConnection( Plug& srcPlug, Plug& destPlug ); Plug& getSrcPlug() const { return *m_srcPlug; } Plug& getDestPlug() const { return *m_destPlug; } bool serialize( std::string basePath, Util::IOSerialize& ser ) const; static PlugConnection* deserialize( std::string basePath, Util::IODeserialize& deser, Unit& avDevice ); private: PlugConnection(); private: Plug* m_srcPlug; Plug* m_destPlug; }; typedef std::vector PlugConnectionOwnerVector; bool serializePlugVector( std::string basePath, Util::IOSerialize& ser, const PlugVector& vec); bool deserializePlugVector( std::string basePath, Util::IODeserialize& deser, const PlugManager& plugManager, PlugVector& vec ); } #endif // AVC_PLUG libffado-2.4.5/src/libavc/general/avc_plug_info.cpp0000644000175000001440000001224714206145246021655 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_plug_info.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "libutil/ByteSwap.h" #include using namespace std; namespace AVC { PlugInfoCmd::PlugInfoCmd( Ieee1394Service& ieee1394service, ESubFunction eSubFunction ) : AVCCommand( ieee1394service, AVC1394_CMD_PLUG_INFO ) , m_serialBusIsochronousInputPlugs( 0xff ) , m_serialBusIsochronousOutputPlugs( 0xff ) , m_externalInputPlugs( 0xff ) , m_externalOutputPlugs( 0xff ) , m_serialBusAsynchronousInputPlugs( 0xff ) , m_serialBusAsynchronousOuputPlugs( 0xff ) , m_destinationPlugs( 0xff ) , m_sourcePlugs( 0xff ) , m_subFunction( eSubFunction ) { } PlugInfoCmd::PlugInfoCmd( const PlugInfoCmd& rhs ) : AVCCommand( rhs ) , m_serialBusIsochronousInputPlugs( rhs.m_serialBusIsochronousInputPlugs ) , m_serialBusIsochronousOutputPlugs( rhs.m_serialBusIsochronousOutputPlugs ) , m_externalInputPlugs( rhs.m_externalInputPlugs ) , m_externalOutputPlugs( rhs.m_externalOutputPlugs ) , m_serialBusAsynchronousInputPlugs( rhs.m_serialBusAsynchronousInputPlugs ) , m_serialBusAsynchronousOuputPlugs( rhs.m_serialBusAsynchronousOuputPlugs ) , m_destinationPlugs( rhs.m_destinationPlugs ) , m_sourcePlugs( rhs.m_sourcePlugs ) , m_subFunction( rhs.m_subFunction ) { } PlugInfoCmd::~PlugInfoCmd() { } void PlugInfoCmd::clear() { m_serialBusIsochronousInputPlugs=0xff; m_serialBusIsochronousOutputPlugs=0xff; m_externalInputPlugs=0xff; m_externalOutputPlugs=0xff; m_serialBusAsynchronousInputPlugs=0xff; m_serialBusAsynchronousOuputPlugs=0xff; m_destinationPlugs=0xff; m_sourcePlugs=0xff; } bool PlugInfoCmd::serialize( Util::Cmd::IOSSerialize& se ) { byte_t reserved = 0xff; AVCCommand::serialize( se ); se.write( m_subFunction, "PlugInfoCmd subFunction" ); switch( getSubunitType() ) { case eST_Unit: switch( m_subFunction ) { case eSF_SerialBusIsochronousAndExternalPlug: se.write( m_serialBusIsochronousInputPlugs, "PlugInfoCmd serialBusIsochronousInputPlugs" ); se.write( m_serialBusIsochronousOutputPlugs, "PlugInfoCmd serialBusIsochronousOutputPlugs" ); se.write( m_externalInputPlugs, "PlugInfoCmd externalInputPlugs" ); se.write( m_externalOutputPlugs, "PlugInfoCmd externalOutputPlugs" ); break; case eSF_SerialBusAsynchonousPlug: se.write( m_serialBusAsynchronousInputPlugs, "PlugInfoCmd serialBusAsynchronousInputPlugs" ); se.write( m_serialBusAsynchronousOuputPlugs, "PlugInfoCmd serialBusAsynchronousOuputPlugs" ); se.write( reserved, "PlugInfoCmd" ); se.write( reserved, "PlugInfoCmd" ); break; default: cerr << "Could not serialize with subfucntion " << m_subFunction << endl; return false; } break; default: se.write( m_destinationPlugs, "PlugInfoCmd destinationPlugs" ); se.write( m_sourcePlugs, "PlugInfoCmd sourcePlugs" ); se.write( reserved, "PlugInfoCmd" ); se.write( reserved, "PlugInfoCmd" ); } return true; } bool PlugInfoCmd::deserialize( Util::Cmd::IISDeserialize& de ) { byte_t reserved; AVCCommand::deserialize( de ); de.read( &m_subFunction ); switch ( getSubunitType() ) { case eST_Unit: switch ( m_subFunction ) { case eSF_SerialBusIsochronousAndExternalPlug: de.read( &m_serialBusIsochronousInputPlugs ); de.read( &m_serialBusIsochronousOutputPlugs ); de.read( &m_externalInputPlugs ); de.read( &m_externalOutputPlugs ); break; case eSF_SerialBusAsynchonousPlug: de.read( &m_serialBusAsynchronousInputPlugs ); de.read( &m_serialBusAsynchronousOuputPlugs ); de.read( &reserved ); de.read( &reserved ); break; default: cerr << "Could not deserialize with subfunction " << m_subFunction << endl; return false; } break; default: de.read( &m_destinationPlugs ); de.read( &m_sourcePlugs ); de.read( &reserved ); de.read( &reserved ); } return true; } bool PlugInfoCmd::setSubFunction( ESubFunction subFunction ) { m_subFunction = subFunction; return true; } } libffado-2.4.5/src/libavc/general/avc_plug_info.h0000644000175000001440000000530014206145246021312 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCPLUGINFO_H #define AVCPLUGINFO_H #include "avc_generic.h" #define AVC1394_CMD_PLUG_INFO 0x02 #define AVC1394_PLUG_INFO_SUBFUNCTION_SERIAL_BUS_ISOCHRONOUS_AND_EXTERNAL_PLUG 0x00 #define AVC1394_PLUG_INFO_SUBFUNCTION_SERIAL_BUS_ASYNCHRONOUS_PLUG 0x01 #define AVC1394_PLUG_INFO_SUBFUNCTION_SERIAL_BUS_GENERIC_BUS_PLUG_BLUETOOTH 0x40 #define AVC1394_PLUG_INFO_SUBFUNCTION_SERIAL_BUS_NOT_USED 0xFF namespace AVC { class PlugInfoCmd: public AVCCommand { public: enum ESubFunction { eSF_SerialBusIsochronousAndExternalPlug = AVC1394_PLUG_INFO_SUBFUNCTION_SERIAL_BUS_ISOCHRONOUS_AND_EXTERNAL_PLUG, eSF_SerialBusAsynchonousPlug = AVC1394_PLUG_INFO_SUBFUNCTION_SERIAL_BUS_ASYNCHRONOUS_PLUG, eSF_SerialBusPlug = AVC1394_PLUG_INFO_SUBFUNCTION_SERIAL_BUS_GENERIC_BUS_PLUG_BLUETOOTH, eSF_NotUsed = AVC1394_PLUG_INFO_SUBFUNCTION_SERIAL_BUS_NOT_USED, }; PlugInfoCmd( Ieee1394Service& ieee1394service, ESubFunction eSubFunction = eSF_SerialBusIsochronousAndExternalPlug ); PlugInfoCmd( const PlugInfoCmd& rhs ); virtual ~PlugInfoCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual void clear(); virtual const char* getCmdName() const { return "PlugInfoCmd"; } nr_of_plugs_t m_serialBusIsochronousInputPlugs; nr_of_plugs_t m_serialBusIsochronousOutputPlugs; nr_of_plugs_t m_externalInputPlugs; nr_of_plugs_t m_externalOutputPlugs; nr_of_plugs_t m_serialBusAsynchronousInputPlugs; nr_of_plugs_t m_serialBusAsynchronousOuputPlugs; nr_of_plugs_t m_destinationPlugs; nr_of_plugs_t m_sourcePlugs; bool setSubFunction( ESubFunction subFunction ); protected: subfunction_t m_subFunction; }; } #endif // AVCPLUGINFO_H libffado-2.4.5/src/libavc/general/avc_signal_format.cpp0000644000175000001440000000765314206145246022525 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_signal_format.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "libutil/ByteSwap.h" #include #define AVC1394_CMD_INPUT_PLUG_SIGNAL_FORMAT 0x19 #define AVC1394_CMD_OUTPUT_PLUG_SIGNAL_FORMAT 0x18 using namespace std; namespace AVC { OutputPlugSignalFormatCmd::OutputPlugSignalFormatCmd(Ieee1394Service& ieee1394service) : AVCCommand( ieee1394service, AVC1394_CMD_OUTPUT_PLUG_SIGNAL_FORMAT ) , m_plug ( 0 ) , m_eoh ( 1 ) , m_form ( 0 ) , m_fmt ( 0 ) { m_fdf[0]=0xFF; m_fdf[1]=0xFF; m_fdf[2]=0xFF; } OutputPlugSignalFormatCmd::~OutputPlugSignalFormatCmd() { } bool OutputPlugSignalFormatCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= AVCCommand::serialize( se ); result &= se.write(m_plug,"OutputPlugSignalFormatCmd plug"); byte_t tmp = ((m_eoh & 0x01)<<7); tmp |= ((m_form & 0x01)<<6); tmp |= (m_fmt & 0x3f); result &= se.write(tmp,"OutputPlugSignalFormatCmd eoh,form,fmt"); result &= se.write(m_fdf[0],"OutputPlugSignalFormatCmd fdf[0]"); result &= se.write(m_fdf[1],"OutputPlugSignalFormatCmd fdf[1]"); result &= se.write(m_fdf[2],"OutputPlugSignalFormatCmd fdf[2]"); return result; } bool OutputPlugSignalFormatCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= AVCCommand::deserialize( de ); result &= de.read(&m_plug); byte_t tmp; result &= de.read(&tmp); m_eoh=((tmp & 0x80)>>7); m_form=((tmp & 0x40)>>6); m_fmt=tmp & 0x3f; result &= de.read(&m_fdf[0]); result &= de.read(&m_fdf[1]); result &= de.read(&m_fdf[2]); return result; } //------------------------ InputPlugSignalFormatCmd::InputPlugSignalFormatCmd(Ieee1394Service& ieee1394service) : AVCCommand( ieee1394service, AVC1394_CMD_INPUT_PLUG_SIGNAL_FORMAT ) , m_plug ( 0 ) , m_eoh ( 1 ) , m_form ( 0 ) , m_fmt ( 0 ) { m_fdf[0]=0xFF; m_fdf[1]=0xFF; m_fdf[2]=0xFF; } InputPlugSignalFormatCmd::~InputPlugSignalFormatCmd() { } bool InputPlugSignalFormatCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= AVCCommand::serialize( se ); result &= se.write(m_plug,"InputPlugSignalFormatCmd plug"); byte_t tmp = ((m_eoh& 0x01)<<7); tmp |= ((m_form& 0x01)<<6); tmp |= (m_fmt& 0x3f); result &= se.write(tmp,"InputPlugSignalFormatCmd eoh,form,fmt"); result &= se.write(m_fdf[0],"InputPlugSignalFormatCmd fdf[0]"); result &= se.write(m_fdf[1],"InputPlugSignalFormatCmd fdf[1]"); result &= se.write(m_fdf[2],"InputPlugSignalFormatCmd fdf[2]"); return result; } bool InputPlugSignalFormatCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= AVCCommand::deserialize( de ); result &= de.read(&m_plug); byte_t tmp; result &= de.read(&tmp); m_eoh=((tmp & 0x80)>>7); m_form=((tmp & 0x40)>>6); m_fmt=tmp & 0x3f; result &= de.read(&m_fdf[0]); result &= de.read(&m_fdf[1]); result &= de.read(&m_fdf[2]); return result; } } libffado-2.4.5/src/libavc/general/avc_signal_format.h0000644000175000001440000000366014206145246022164 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCSIGNALFORMAT_H #define AVCSIGNALFORMAT_H #include "avc_generic.h" namespace AVC { class OutputPlugSignalFormatCmd: public AVCCommand { public: OutputPlugSignalFormatCmd(Ieee1394Service& ieee1394service); virtual ~OutputPlugSignalFormatCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "OutputPlugSignalFormatCmd"; } byte_t m_plug; byte_t m_eoh; byte_t m_form; byte_t m_fmt; byte_t m_fdf[3]; }; class InputPlugSignalFormatCmd: public AVCCommand { public: InputPlugSignalFormatCmd(Ieee1394Service& ieee1394service); virtual ~InputPlugSignalFormatCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "InputPlugSignalFormatCmd"; } byte_t m_plug; byte_t m_eoh; byte_t m_form; byte_t m_fmt; byte_t m_fdf[3]; }; } #endif // AVCSIGNALFORMAT_H libffado-2.4.5/src/libavc/general/avc_subunit.cpp0000644000175000001440000001664414206145246021371 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_subunit.h" #include "../general/avc_unit.h" #include "../general/avc_plug.h" #include "libieee1394/configrom.h" #include "../general/avc_plug_info.h" #include "../streamformat/avc_extended_stream_format.h" #include "libutil/cmd_serialize.h" #include namespace AVC { IMPL_DEBUG_MODULE( Subunit, Subunit, DEBUG_LEVEL_NORMAL ); //////////////////////////////////////////// Subunit::Subunit( Unit& unit, ESubunitType type, subunit_t id ) : m_unit( &unit ) , m_sbType( type ) , m_sbId( id ) { } Subunit::Subunit() { } Subunit::~Subunit() { for ( PlugVector::iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { delete *it; } } void Subunit::setVerboseLevel(int l) { setDebugLevel(l); debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); } Plug * Subunit::createPlug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId ) { return new Plug( unit, subunit, functionBlockType, functionBlockId, plugAddressType, plugDirection, plugId ); } bool Subunit::discover() { if ( !discoverPlugs() ) { debugError( "plug discovery failed\n" ); return false; } return true; } bool Subunit::discoverPlugs() { debugOutput(DEBUG_LEVEL_NORMAL, "Discovering plugs...\n"); PlugInfoCmd plugInfoCmd( getUnit().get1394Service(), PlugInfoCmd::eSF_SerialBusIsochronousAndExternalPlug ); plugInfoCmd.setNodeId( getUnit().getConfigRom().getNodeId() ); plugInfoCmd.setCommandType( AVCCommand::eCT_Status ); plugInfoCmd.setSubunitType( getSubunitType() ); plugInfoCmd.setSubunitId( getSubunitId() ); plugInfoCmd.setVerbose( getDebugLevel() ); if ( !plugInfoCmd.fire() ) { debugError( "plug info command failed\n" ); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "number of source plugs = %d\n", plugInfoCmd.m_sourcePlugs ); debugOutput( DEBUG_LEVEL_VERBOSE, "number of destination output " "plugs = %d\n", plugInfoCmd.m_destinationPlugs ); if ( !discoverPlugs( Plug::eAPD_Input, plugInfoCmd.m_destinationPlugs ) ) { debugError( "destination plug discovering failed\n" ); return false; } if ( !discoverPlugs( Plug::eAPD_Output, plugInfoCmd.m_sourcePlugs ) ) { debugError( "source plug discovering failed\n" ); return false; } return true; } bool Subunit::discoverConnections() { debugOutput(DEBUG_LEVEL_NORMAL, "Discovering connections...\n"); for ( PlugVector::iterator it = getPlugs().begin(); it != getPlugs().end(); ++it ) { Plug* plug = *it; if ( !plug->discoverConnections() ) { debugError( "plug connection discovering failed ('%s')\n", plug->getName() ); return false; } } return true; } bool Subunit::discoverPlugs(Plug::EPlugDirection plugDirection, plug_id_t plugMaxId ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Discovering plugs for direction %d...\n", plugDirection); for ( int plugIdx = 0; plugIdx < plugMaxId; ++plugIdx ) { Plug* plug = createPlug( &getUnit(), &getSubunit(), 0xff, 0xff, Plug::eAPA_SubunitPlug, plugDirection, plugIdx ); if ( !plug ) { debugError( "plug creation failed\n" ); return false; } plug->setVerboseLevel(getDebugLevel()); if ( !plug->discover() ) { debugError( "plug discover failed\n" ); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "plug '%s' found\n", plug->getName() ); getPlugs().push_back( plug ); } return true; } bool Subunit::addPlug( Plug& plug ) { m_plugs.push_back( &plug ); return true; } Plug* Subunit::getPlug(Plug::EPlugDirection direction, plug_id_t plugId) { for ( PlugVector::iterator it = m_plugs.begin(); it != m_plugs.end(); ++it ) { Plug* plug = *it; if ( ( plug->getPlugId() == plugId ) && ( plug->getDirection() == direction ) ) { return plug; } } return 0; } bool Subunit::initPlugFromDescriptor( Plug& plug ) { debugOutput(DEBUG_LEVEL_NORMAL, "plug loading from descriptor not implemented\n"); return false; } bool Subunit::serialize( std::string basePath, Util::IOSerialize& ser ) const { bool result; result = ser.write( basePath + "m_sbType", m_sbType ); result &= ser.write( basePath + "m_sbId", m_sbId ); result &= serializePlugVector( basePath + "m_plugs", ser, m_plugs ); result &= serializeChild( basePath, ser ); return result; } Subunit* Subunit::deserialize( std::string basePath, Util::IODeserialize& deser, Unit& unit ) { bool result; ESubunitType sbType; if ( !deser.isExisting( basePath + "m_sbType" ) ) { return 0; } result = deser.read( basePath + "m_sbType", sbType ); Subunit* pSubunit = unit.createSubunit(unit, sbType, 0); if ( !pSubunit ) { return 0; } pSubunit->m_unit = &unit; pSubunit->m_sbType = sbType; result &= deser.read( basePath + "m_sbId", pSubunit->m_sbId ); result &= pSubunit->deserializeChild( basePath, deser, unit ); if ( !result ) { delete pSubunit; return 0; } return pSubunit; } bool Subunit::deserializeUpdate( std::string basePath, Util::IODeserialize& deser ) { bool result; std::ostringstream strstrm; strstrm << basePath << m_sbId << "/"; result = deserializePlugVector( strstrm.str() + "m_plugs", deser, m_unit->getPlugManager(), m_plugs ); result &= deserializeUpdateChild( strstrm.str(), deser ); return result; } } libffado-2.4.5/src/libavc/general/avc_subunit.h0000644000175000001440000000663714206145246021037 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVC_SUBUNIT_H #define AVC_SUBUNIT_H #include "debugmodule/debugmodule.h" #include "../avc_definitions.h" #include "../general/avc_plug.h" #include "../general/avc_extended_subunit_info.h" #include "../general/avc_generic.h" #include "../audiosubunit/avc_function_block.h" #include namespace AVC { class Unit; class Subunit { public: Subunit( Unit& avDevice, ESubunitType type, subunit_t id ); virtual ~Subunit(); virtual bool discover(); virtual bool discoverConnections(); virtual const char* getName() = 0; subunit_t getSubunitId() { return m_sbId; } ESubunitType getSubunitType() { return m_sbType; } Unit& getUnit() const { return *m_unit; } Subunit& getSubunit() { return *this; } virtual Plug *createPlug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId ); bool addPlug( Plug& plug ); virtual bool initPlugFromDescriptor( Plug& plug ); PlugVector& getPlugs() { return m_plugs; } Plug* getPlug(Plug::EPlugDirection direction, plug_id_t plugId); virtual void setVerboseLevel(int l); bool serialize( std::string basePath, Util::IOSerialize& ser ) const; static Subunit* deserialize( std::string basePath, Util::IODeserialize& deser, Unit& avDevice ); bool deserializeUpdate( std::string basePath, Util::IODeserialize& deser ); protected: Subunit(); virtual bool serializeChild( std::string basePath, Util::IOSerialize& ser ) const = 0; virtual bool deserializeChild( std::string basePath, Util::IODeserialize& deser, Unit& avDevice ) = 0; virtual bool deserializeUpdateChild( std::string basePath, Util::IODeserialize& deser ) = 0; bool discoverPlugs(); bool discoverPlugs(Plug::EPlugDirection plugDirection, AVC::plug_id_t plugMaxId ); protected: Unit* m_unit; ESubunitType m_sbType; subunit_t m_sbId; PlugVector m_plugs; DECLARE_DEBUG_MODULE; }; typedef std::vector SubunitVector; } #endif libffado-2.4.5/src/libavc/general/avc_subunit_info.cpp0000644000175000001440000000501214206145246022367 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_subunit_info.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "libutil/ByteSwap.h" #include using namespace std; namespace AVC { SubUnitInfoCmd::SubUnitInfoCmd( Ieee1394Service& ieee1349service ) : AVCCommand( ieee1349service, AVC1394_CMD_SUBUNIT_INFO ) { clear(); } bool SubUnitInfoCmd::clear() { m_page = 0xff; m_extension_code = 0x7; for ( int i = 0; i < eMaxSubunitsPerPage; ++i ) { m_table[i].m_subunit_type = 0xff; m_table[i].m_max_subunit_id = 0xff; } m_nrOfValidEntries = 0; return true; } SubUnitInfoCmd::~SubUnitInfoCmd() { } bool SubUnitInfoCmd::serialize( Util::Cmd::IOSSerialize& se ) { AVCCommand::serialize( se ); byte_t operand = 0; operand = (( m_page & 0x7 ) << 4 ) | ( m_extension_code & 0x7 ); se.write( operand, "SubUnitInfoCmd page and extension_code" ); for ( int i = 0; i < eMaxSubunitsPerPage; ++i ) { operand = ( m_table[i].m_subunit_type << 3 ) | ( m_table[i].m_max_subunit_id & 0x7 ); se.write( operand, "SubUnitInfoCmd subunit_type and max_subunit_ID" ); } return true; } bool SubUnitInfoCmd::deserialize( Util::Cmd::IISDeserialize& de ) { AVCCommand::deserialize( de ); byte_t operand; de.read( &operand ); m_page = ( operand >> 4 ) & 0x7; m_extension_code = operand & 0x7; m_nrOfValidEntries = 0; for ( int i = 0; i < eMaxSubunitsPerPage; ++i ) { de.read( &operand ); m_table[i].m_subunit_type = operand >> 3; m_table[i].m_max_subunit_id = operand & 0x7; if ( operand != 0xff ) { m_nrOfValidEntries++; } } return true; } } libffado-2.4.5/src/libavc/general/avc_subunit_info.h0000644000175000001440000000356214206145246022044 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCSUBUNITINFO_H #define AVCSUBUNITINFO_H #include "avc_generic.h" #define AVC1394_CMD_SUBUNIT_INFO 0x31 namespace AVC { // No extended subunit queries supported class SubUnitInfoCmd: public AVCCommand { public: SubUnitInfoCmd( Ieee1394Service& ieee1349service ); virtual ~SubUnitInfoCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "SubUnitInfoCmd"; } bool clear(); page_t m_page; extension_code_t m_extension_code; enum { eMaxSubunitsPerPage = 4, eMaxSubunitsPerUnit = 32, }; struct TableEntry { subunit_type_t m_subunit_type; max_subunit_id_t m_max_subunit_id; }; struct TableEntry m_table[eMaxSubunitsPerPage]; short getMaxNoOfPages() { return eMaxSubunitsPerUnit / eMaxSubunitsPerPage; } short m_nrOfValidEntries; short getNrOfValidEntries() { return m_nrOfValidEntries; } }; } #endif // AVCSUBUNITINFO_H libffado-2.4.5/src/libavc/general/avc_unit.cpp0000644000175000001440000007707214206145246020661 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_unit.h" #include "avc_subunit.h" #include "avc_plug.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "../general/avc_plug_info.h" #include "../general/avc_extended_plug_info.h" #include "../general/avc_subunit_info.h" #include "../streamformat/avc_extended_stream_format.h" #include "libutil/cmd_serialize.h" #include "../avc_definitions.h" #include "debugmodule/debugmodule.h" #include #include namespace AVC { IMPL_DEBUG_MODULE( Unit, Unit, DEBUG_LEVEL_NORMAL ); Unit::Unit( ) : m_pPlugManager( new PlugManager( ) ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Unit\n" ); m_pPlugManager->setVerboseLevel( getDebugLevel() ); } Unit::~Unit() { clean(); } Plug * Unit::createPlug( Unit* unit, Subunit* subunit, function_block_type_t functionBlockType, function_block_type_t functionBlockId, Plug::EPlugAddressType plugAddressType, Plug::EPlugDirection plugDirection, plug_id_t plugId, int globalId ) { Plug *p= new Plug( unit, subunit, functionBlockType, functionBlockId, plugAddressType, plugDirection, plugId, globalId ); if (p) p->setVerboseLevel(getDebugLevel()); return p; } Subunit* Unit::createSubunit(Unit& unit, ESubunitType type, subunit_t id ) { Subunit* s=NULL; switch (type) { case eST_Audio: s=new SubunitAudio(unit, id ); break; case eST_Music: s=new SubunitMusic(unit, id ); break; default: s=NULL; break; } if(s) s->setVerboseLevel(getDebugLevel()); return s; } void Unit::setVerboseLevel(int l) { setDebugLevel(l); for ( SubunitVector::const_iterator it = m_subunits.begin(); it != m_subunits.end(); ++it ) { (*it)->setVerboseLevel(l); } m_pPlugManager->setVerboseLevel(l); debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); } // prepare the device for a rediscovery bool Unit::clean() { for ( SubunitVector::iterator it = m_subunits.begin(); it != m_subunits.end(); ++it ) { delete *it; } m_subunits.clear(); for ( PlugVector::iterator it = m_pcrPlugs.begin(); it != m_pcrPlugs.end(); ++it ) { delete *it; } m_pcrPlugs.clear(); for ( PlugVector::iterator it = m_externalPlugs.begin(); it != m_externalPlugs.end(); ++it ) { delete *it; } m_externalPlugs.clear(); for ( PlugConnectionVector::iterator it = m_plugConnections.begin(); it != m_plugConnections.end(); ++it ) { delete *it; } m_plugConnections.clear(); delete m_pPlugManager; m_pPlugManager = new PlugManager(); if (m_pPlugManager == NULL) { debugError("Could not create new plugmanager\n"); return false; } m_syncInfos.clear(); return true; } bool Unit::discover() { debugOutput( DEBUG_LEVEL_VERBOSE, "Discovering AVC::Unit...\n"); if( !clean() ) { debugError( "Could not clean unit data structures\n" ); return false; } if ( !enumerateSubUnits() ) { debugError( "Could not enumerate sub units\n" ); return false; } if ( !discoverPlugs() ) { debugError( "Detecting plugs failed\n" ); return false; } if ( !rediscoverConnections() ) { debugError( "Detecting connections failed\n" ); return false; } if ( !discoverSyncModes() ) { debugError( "Detecting sync modes failed\n" ); return false; } if ( !propagatePlugInfo() ) { debugError( "Failed to propagate plug info\n" ); return false; } return true; } bool Unit::rediscoverConnections() { debugOutput( DEBUG_LEVEL_VERBOSE, "Re-discovering plug connections...\n"); // clear the previous connections for ( PlugConnectionVector::iterator it = m_plugConnections.begin(); it != m_plugConnections.end(); ++it ) { delete *it; } m_plugConnections.clear(); if ( !discoverPlugConnections() ) { debugError( "Detecting plug connections failed\n" ); return false; } if ( !discoverSubUnitsPlugConnections() ) { debugError( "Detecting subunit plug connections failed\n" ); return false; } if ( !m_pPlugManager->tidyPlugConnections(m_plugConnections) ) { debugError( "Tidying of plug connections failed\n" ); return false; } return true; } bool Unit::enumerateSubUnits() { SubUnitInfoCmd subUnitInfoCmd( get1394Service() ); subUnitInfoCmd.setCommandType( AVCCommand::eCT_Status ); // NOTE: BeBoB has always exactly one audio and one music subunit. This // means is fits into the first page of the SubUnitInfo command. // So there is no need to do more than needed // FIXME: to be fully spec compliant this needs to be fixed, but let's not // do that for now subUnitInfoCmd.m_page = 0; subUnitInfoCmd.setNodeId( getConfigRom().getNodeId() ); subUnitInfoCmd.setVerbose( getDebugLevel() ); if ( !subUnitInfoCmd.fire() ) { debugError( "Subunit info command failed\n" ); // shouldn't this be an error situation? return false; } for ( int i = 0; i < subUnitInfoCmd.getNrOfValidEntries(); ++i ) { subunit_type_t subunit_type = subUnitInfoCmd.m_table[i].m_subunit_type; unsigned int subunitId = getNrOfSubunits( subunit_type ); debugOutput( DEBUG_LEVEL_VERBOSE, "subunit_id = %2d, subunit_type = %2d (%s)\n", subunitId, subunit_type, subunitTypeToString( subunit_type ) ); Subunit* subunit = 0; switch( subunit_type ) { case eST_Audio: subunit = createSubunit( *this, eST_Audio, subunitId ); if ( !subunit ) { debugFatal( "Could not allocate SubunitAudio\n" ); return false; } subunit->setVerboseLevel(getDebugLevel()); if ( !subunit->discover() ) { debugError( "enumerateSubUnits: Could not discover " "subunit_id = %2d, subunit_type = %2d (%s)\n", subunitId, subunit_type, subunitTypeToString( subunit_type ) ); delete subunit; return false; } else { m_subunits.push_back( subunit ); } break; case eST_Music: subunit = createSubunit( *this, eST_Music, subunitId ); if ( !subunit ) { debugFatal( "Could not allocate SubunitMusic\n" ); return false; } subunit->setVerboseLevel(getDebugLevel()); if ( !subunit->discover() ) { debugError( "enumerateSubUnits: Could not discover " "subunit_id = %2d, subunit_type = %2d (%s)\n", subunitId, subunit_type, subunitTypeToString( subunit_type ) ); delete subunit; return false; } else { m_subunits.push_back( subunit ); } break; default: debugOutput( DEBUG_LEVEL_NORMAL, "Unsupported subunit found, subunit_type = %d (%s)\n", subunit_type, subunitTypeToString( subunit_type ) ); continue; } } return true; } Subunit* Unit::getSubunit( subunit_type_t subunitType, subunit_id_t subunitId ) const { for ( SubunitVector::const_iterator it = m_subunits.begin(); it != m_subunits.end(); ++it ) { Subunit* subunit = *it; if ( ( subunitType == subunit->getSubunitType() ) && ( subunitId == subunit->getSubunitId() ) ) { return subunit; } } return 0; } unsigned int Unit::getNrOfSubunits( subunit_type_t subunitType ) const { unsigned int nrOfSubunits = 0; for ( SubunitVector::const_iterator it = m_subunits.begin(); it != m_subunits.end(); ++it ) { Subunit* subunit = *it; if ( subunitType == subunit->getSubunitType() ) { nrOfSubunits++; } } return nrOfSubunits; } bool Unit::discoverPlugs() { debugOutput( DEBUG_LEVEL_NORMAL, "Discovering plugs...\n"); ////////////////////////////////////////////// // Get number of available isochronous input // and output plugs of unit PlugInfoCmd plugInfoCmd( get1394Service() ); plugInfoCmd.setNodeId( getConfigRom().getNodeId() ); plugInfoCmd.setCommandType( AVCCommand::eCT_Status ); plugInfoCmd.setVerbose( getDebugLevel() ); if ( !plugInfoCmd.fire() ) { debugError( "plug info command failed\n" ); return false; } debugOutput( DEBUG_LEVEL_NORMAL, "number of iso input plugs = %d\n", plugInfoCmd.m_serialBusIsochronousInputPlugs ); debugOutput( DEBUG_LEVEL_NORMAL, "number of iso output plugs = %d\n", plugInfoCmd.m_serialBusIsochronousOutputPlugs ); debugOutput( DEBUG_LEVEL_NORMAL, "number of external input plugs = %d\n", plugInfoCmd.m_externalInputPlugs ); debugOutput( DEBUG_LEVEL_NORMAL, "number of external output plugs = %d\n", plugInfoCmd.m_externalOutputPlugs ); if ( !discoverPlugsPCR( Plug::eAPD_Input, plugInfoCmd.m_serialBusIsochronousInputPlugs ) ) { debugError( "pcr input plug discovering failed\n" ); return false; } if ( !discoverPlugsPCR( Plug::eAPD_Output, plugInfoCmd.m_serialBusIsochronousOutputPlugs ) ) { debugError( "pcr output plug discovering failed\n" ); return false; } if ( !discoverPlugsExternal( Plug::eAPD_Input, plugInfoCmd.m_externalInputPlugs ) ) { debugError( "external input plug discovering failed\n" ); return false; } if ( !discoverPlugsExternal( Plug::eAPD_Output, plugInfoCmd.m_externalOutputPlugs ) ) { debugError( "external output plug discovering failed\n" ); return false; } return true; } bool Unit::discoverPlugsPCR( Plug::EPlugDirection plugDirection, plug_id_t plugMaxId ) { debugOutput( DEBUG_LEVEL_NORMAL, "Discovering PCR plugs, direction %d...\n",plugDirection); for ( int plugId = 0; plugId < plugMaxId; ++plugId ) { Plug* plug = createPlug( this, NULL, 0xff, 0xff, Plug::eAPA_PCR, plugDirection, plugId ); if( plug ) plug->setVerboseLevel(getDebugLevel()); if ( !plug || !plug->discover() ) { debugError( "plug discovering failed\n" ); delete plug; return false; } debugOutput( DEBUG_LEVEL_NORMAL, "plug '%s' found\n", plug->getName() ); m_pcrPlugs.push_back( plug ); } return true; } bool Unit::discoverPlugsExternal( Plug::EPlugDirection plugDirection, plug_id_t plugMaxId ) { debugOutput( DEBUG_LEVEL_NORMAL, "Discovering External plugs, direction %d...\n",plugDirection); for ( int plugId = 0; plugId < plugMaxId; ++plugId ) { Plug* plug = createPlug( this, NULL, 0xff, 0xff, Plug::eAPA_ExternalPlug, plugDirection, plugId ); if( plug ) plug->setVerboseLevel(getDebugLevel()); if ( !plug || !plug->discover() ) { debugError( "plug discovering failed\n" ); return false; } debugOutput( DEBUG_LEVEL_NORMAL, "plug '%s' found\n", plug->getName() ); m_externalPlugs.push_back( plug ); } return true; } bool Unit::discoverPlugConnections() { debugOutput( DEBUG_LEVEL_NORMAL, "Discovering PCR plug connections...\n"); for ( PlugVector::iterator it = m_pcrPlugs.begin(); it != m_pcrPlugs.end(); ++it ) { Plug* plug = *it; if ( !plug->discoverConnections() ) { debugError( "Could not discover PCR plug connections\n" ); return false; } } debugOutput( DEBUG_LEVEL_NORMAL, "Discovering External plug connections...\n"); for ( PlugVector::iterator it = m_externalPlugs.begin(); it != m_externalPlugs.end(); ++it ) { Plug* plug = *it; if ( !plug->discoverConnections() ) { debugError( "Could not discover External plug connections\n" ); return false; } } return true; } bool Unit::discoverSubUnitsPlugConnections() { for ( SubunitVector::iterator it = m_subunits.begin(); it != m_subunits.end(); ++it ) { Subunit* subunit = *it; if ( !subunit->discoverConnections() ) { debugError( "Subunit '%s' plug connections failed\n", subunit->getName() ); return false; } } return true; } bool Unit::propagatePlugInfo() { debugOutput( DEBUG_LEVEL_NORMAL, "Propagating info to PCR plugs...\n"); for ( PlugVector::iterator it = m_pcrPlugs.begin(); it != m_pcrPlugs.end(); ++it ) { Plug* plug = *it; debugOutput( DEBUG_LEVEL_NORMAL, "plug: %s\n", plug->getName()); if (!plug->propagateFromConnectedPlug()) { debugWarning( "Could not propagate info for plug '%s'\n", plug->getName()); } } debugOutput( DEBUG_LEVEL_NORMAL, "Propagating info to External plugs...\n"); for ( PlugVector::iterator it = m_externalPlugs.begin(); it != m_externalPlugs.end(); ++it ) { Plug* plug = *it; debugOutput( DEBUG_LEVEL_NORMAL, "plug: %s\n", plug->getName()); if (!plug->propagateFromConnectedPlug()) { debugWarning( "Could not propagate info for plug '%s'\n", plug->getName()); } } return true; } PlugConnection* Unit::getPlugConnection( Plug& srcPlug ) const { for ( PlugConnectionVector::const_iterator it = m_plugConnections.begin(); it != m_plugConnections.end(); ++it ) { PlugConnection* plugConnection = *it; if ( &( plugConnection->getSrcPlug() ) == &srcPlug ) { return plugConnection; } } return 0; } Plug* Unit::getPlugById( PlugVector& plugs, Plug::EPlugDirection plugDirection, int id ) { for ( PlugVector::iterator it = plugs.begin(); it != plugs.end(); ++it ) { Plug* plug = *it; if ( ( id == plug->getPlugId() ) && ( plugDirection == plug->getPlugDirection() ) ) { return plug; } } return 0; } PlugVector Unit::getPlugsByType( PlugVector& plugs, Plug::EPlugDirection plugDirection, Plug::EPlugType type) { PlugVector plugVector; for ( PlugVector::iterator it = plugs.begin(); it != plugs.end(); ++it ) { Plug* plug = *it; if ( ( type == plug->getPlugType() ) && ( plugDirection == plug->getPlugDirection() ) ) { plugVector.push_back( plug ); } } return plugVector; } Plug* Unit::getSyncPlug( int maxPlugId, Plug::EPlugDirection ) { return 0; } bool Unit::discoverSyncModes() { // Following possible sync plugs exists: // - Music subunit sync output plug = internal sync (CSP) // - Unit input plug 0 = SYT match // - Unit input plut 1 = Sync stream // // If last sync mode is reported it is mostelikely not // implemented *sic* // // Following sync sources are device specific: // - All unit external input plugs which have a // sync information (WS, SPDIF, ...) // First we have to find the sync input and output plug // in the music subunit. // Note PCR input means 1394bus-to-device where as // MSU input means subunit-to-device PlugVector syncPCRInputPlugs = getPlugsByType( m_pcrPlugs, Plug::eAPD_Input, Plug::eAPT_Sync ); if ( !syncPCRInputPlugs.size() ) { debugOutput(DEBUG_LEVEL_NORMAL, "No PCR sync input plug found\n" ); } PlugVector syncPCROutputPlugs = getPlugsByType( m_pcrPlugs, Plug::eAPD_Output, Plug::eAPT_Sync ); if ( !syncPCROutputPlugs.size() ) { debugOutput(DEBUG_LEVEL_NORMAL, "No PCR sync output plug found\n" ); } PlugVector isoPCRInputPlugs = getPlugsByType( m_pcrPlugs, Plug::eAPD_Input, Plug::eAPT_IsoStream ); if ( !isoPCRInputPlugs.size() ) { debugOutput(DEBUG_LEVEL_NORMAL, "No PCR iso input plug found\n" ); } PlugVector isoPCROutputPlugs = getPlugsByType( m_pcrPlugs, Plug::eAPD_Output, Plug::eAPT_IsoStream ); if ( !isoPCROutputPlugs.size() ) { debugOutput(DEBUG_LEVEL_NORMAL, "No PCR iso output plug found\n" ); } PlugVector digitalExternalInputPlugs = getPlugsByType( m_externalPlugs, Plug::eAPD_Input, Plug::eAPT_Digital ); if ( !digitalExternalInputPlugs.size() ) { debugOutput( DEBUG_LEVEL_VERBOSE, "No external digital input plugs found\n" ); } PlugVector syncExternalInputPlugs = getPlugsByType( m_externalPlugs, Plug::eAPD_Input, Plug::eAPT_Sync ); if ( !syncExternalInputPlugs.size() ) { debugOutput( DEBUG_LEVEL_VERBOSE, "No external sync input plugs found\n" ); } PlugVector syncMSUInputPlugs = m_pPlugManager->getPlugsByType( eST_Music, 0, 0xff, 0xff, Plug::eAPA_SubunitPlug, Plug::eAPD_Input, Plug::eAPT_Sync ); if ( !syncMSUInputPlugs.size() ) { debugWarning( "No sync input plug for MSU subunit found\n" ); } PlugVector syncMSUOutputPlugs = m_pPlugManager->getPlugsByType( eST_Music, 0, 0xff, 0xff, Plug::eAPA_SubunitPlug, Plug::eAPD_Output, Plug::eAPT_Sync ); if ( !syncMSUOutputPlugs.size() ) { debugWarning( "No sync output plug for MSU subunit found\n" ); } debugOutput( DEBUG_LEVEL_VERBOSE, "PCR Sync Input Plugs:\n" ); showPlugs( syncPCRInputPlugs ); debugOutput( DEBUG_LEVEL_VERBOSE, "PCR Sync Output Plugs:\n" ); showPlugs( syncPCROutputPlugs ); debugOutput( DEBUG_LEVEL_VERBOSE, "PCR Iso Input Plugs:\n" ); showPlugs( isoPCRInputPlugs ); debugOutput( DEBUG_LEVEL_VERBOSE, "PCR Iso Output Plugs:\n" ); showPlugs( isoPCROutputPlugs ); debugOutput( DEBUG_LEVEL_VERBOSE, "External digital Input Plugs:\n" ); showPlugs( digitalExternalInputPlugs ); debugOutput( DEBUG_LEVEL_VERBOSE, "External sync Input Plugs:\n" ); showPlugs( syncExternalInputPlugs ); debugOutput( DEBUG_LEVEL_VERBOSE, "MSU Sync Input Plugs:\n" ); showPlugs( syncMSUInputPlugs ); debugOutput( DEBUG_LEVEL_VERBOSE, "MSU Sync Output Plugs:\n" ); showPlugs( syncMSUOutputPlugs ); m_syncInfos.clear(); // Currently there is no usable setup for sync streams. // There is no point in wasting time here. Let's skip // 'sync stream input' and 'sync stream output'. // Check all PCR iso input to MSU input connections // -> SYT match checkSyncConnectionsAndAddToList( isoPCRInputPlugs, syncMSUInputPlugs, "Syt Match" ); // Check all MSU sync output to MSU input connections // -> CSP checkSyncConnectionsAndAddToList( syncMSUOutputPlugs, syncMSUInputPlugs, "Internal (CSP)" ); // Check all external digital input to MSU input connections // -> SPDIF/ADAT sync checkSyncConnectionsAndAddToList( digitalExternalInputPlugs, syncMSUInputPlugs, "Digital Input Sync" ); // Check all external sync input to MSU input connections // -> SPDIF/ADAT sync checkSyncConnectionsAndAddToList( syncExternalInputPlugs, syncMSUInputPlugs, "Digital Input Sync" ); return true; } const Unit::SyncInfo* Unit::getActiveSyncInfo() { SyncInfo* activeSyncInfo = NULL; PlugVector syncMSUInputPlugs = m_pPlugManager->getPlugsByType( eST_Music, 0, 0xff, 0xff, Plug::eAPA_SubunitPlug, Plug::eAPD_Input, Plug::eAPT_Sync ); if ( !syncMSUInputPlugs.size() ) { debugWarning( "No sync input plug for MSU subunit found\n" ); } // Currently active connection signal source cmd, command type // status, source unknown, destination MSU sync input plug for ( PlugVector::const_iterator it = syncMSUInputPlugs.begin(); it != syncMSUInputPlugs.end(); ++it ) { AVC::Plug* msuPlug = *it; for ( PlugVector::const_iterator jt = msuPlug->getInputConnections().begin(); jt != msuPlug->getInputConnections().end(); ++jt ) { AVC::Plug* plug = *jt; for ( SyncInfoVector::iterator it = m_syncInfos.begin(); it != m_syncInfos.end(); ++it ) { SyncInfo* pSyncInfo = &*it; if ( ( pSyncInfo->m_source == plug ) && ( pSyncInfo->m_destination == msuPlug ) ) { activeSyncInfo = pSyncInfo; break; } } if(activeSyncInfo) { debugOutput( DEBUG_LEVEL_NORMAL, "Active Sync Connection: %s, '%s' -> '%s'\n", activeSyncInfo->m_description.c_str(), plug->getName(), msuPlug->getName() ); } } } return activeSyncInfo; } bool Unit::checkSyncConnectionsAndAddToList( PlugVector& plhs, PlugVector& prhs, std::string syncDescription ) { for ( PlugVector::iterator plIt = plhs.begin(); plIt != plhs.end(); ++plIt ) { AVC::Plug* pl = *plIt; for ( PlugVector::iterator prIt = prhs.begin(); prIt != prhs.end(); ++prIt ) { AVC::Plug* pr = *prIt; if ( pl->inquireConnnection( *pr ) ) { m_syncInfos.push_back( SyncInfo( *pl, *pr, syncDescription ) ); debugOutput( DEBUG_LEVEL_NORMAL, "%s, sync connection '%s' -> '%s'\n", syncDescription.c_str(), pl->getName(), pr->getName() ); } } } return true; } bool Unit::setActiveSync(const SyncInfo& syncInfo) { bool retval = true; if ( ! syncInfo.m_source->inquireConnnection( *syncInfo.m_destination ) ) { // this should not happen debugError("Sync connection '%s' -> '%s' not possible. This might be a bug.\n", syncInfo.m_source->getName(), syncInfo.m_destination->getName()); } if(!syncInfo.m_source->setConnection( *syncInfo.m_destination )) { debugError("Could not set sync source connection while device reported it as possible.\n"); retval = false; // proceed to rediscovery anyway } // we now have to rediscover the connections if ( !rediscoverConnections() ) { debugError( "Re-discovery of plug connections failed\n" ); return false; } return retval; } void Unit::show() { if (getDebugLevel() >= DEBUG_LEVEL_VERY_VERBOSE) { m_pPlugManager->showPlugs(); } //SubunitMusic* s=getMusicSubunit(0); //if(s) s->showMusicPlugs(); } void Unit::showPlugs( PlugVector& plugs ) const { int i = 0; for ( PlugVector::const_iterator it = plugs.begin(); it != plugs.end(); ++it, ++i ) { Plug* plug = *it; debugOutput( DEBUG_LEVEL_VERBOSE, "Plug %d\n", i ); plug->showPlug(); } } template bool serializeVector( std::string path, Util::IOSerialize& ser, const T& vec ) { bool result = true; // if vec.size() == 0 int i = 0; for ( typename T::const_iterator it = vec.begin(); it != vec.end(); ++it ) { std::ostringstream strstrm; strstrm << path << i; result &= ( *it )->serialize( strstrm.str() + "/", ser ); i++; } return result; } template bool deserializeVector( std::string path, Util::IODeserialize& deser, Unit& unit, VT& vec ) { int i = 0; T* ptr = 0; do { std::ostringstream strstrm; strstrm << path << i << "/"; ptr = T::deserialize( strstrm.str(), deser, unit ); if ( ptr ) { vec.push_back( ptr ); } i++; } while ( ptr ); return true; } bool Unit::serializeSyncInfoVector( std::string basePath, Util::IOSerialize& ser, const SyncInfoVector& vec ) const { bool result = true; int i = 0; for ( SyncInfoVector::const_iterator it = vec.begin(); it != vec.end(); ++it ) { const SyncInfo& info = *it; std::ostringstream strstrm; strstrm << basePath << i << "/"; result &= ser.write( strstrm.str() + "m_source", info.m_source->getGlobalId() ); result &= ser.write( strstrm.str() + "m_destination", info.m_destination->getGlobalId() ); result &= ser.write( strstrm.str() + "m_description", std::string( info.m_description ) ); i++; } return result; } bool Unit::deserializeSyncInfoVector( std::string basePath, Util::IODeserialize& deser, SyncInfoVector& vec ) { int i = 0; bool bFinished = false; do { bool result; std::ostringstream strstrm; strstrm << basePath << i << "/"; plug_id_t sourceId; plug_id_t destinationId; std::string description; if ( deser.isExisting( strstrm.str() + "m_source" ) ) { result = deser.read( strstrm.str() + "m_source", sourceId ); result &= deser.read( strstrm.str() + "m_destination", destinationId ); result &= deser.read( strstrm.str() + "m_description", description ); } else { result = false; } if ( result ) { SyncInfo syncInfo; syncInfo.m_source = m_pPlugManager->getPlug( sourceId ); syncInfo.m_destination = m_pPlugManager->getPlug( destinationId ); syncInfo.m_description = description; vec.push_back( syncInfo ); i++; } else { bFinished = true; } } while ( !bFinished ); return true; } bool Unit::serialize( std::string basePath, Util::IOSerialize& ser ) const { bool result; result = serializeVector( basePath + "Subunit", ser, m_subunits ); result &= serializePlugVector( basePath + "PcrPlug", ser, m_pcrPlugs ); result &= serializePlugVector( basePath + "ExternalPlug", ser, m_externalPlugs ); result &= serializeVector( basePath + "PlugConnection", ser, m_plugConnections ); result &= m_pPlugManager->serialize( basePath + "Plug", ser ); // serialize all av plugs result &= serializeSyncInfoVector( basePath + "SyncInfo", ser, m_syncInfos ); return result; } bool Unit::deserialize( std::string basePath, Util::IODeserialize& deser ) { bool result = true; result &= deserializeVector( basePath + "Subunit", deser, *this, m_subunits ); if (m_pPlugManager) delete m_pPlugManager; // load all plugs m_pPlugManager = PlugManager::deserialize( basePath + "Plug", deser, *this ); if ( !m_pPlugManager ) return false; // update the plug related stuff in the subunits. we have to // do that in 2 steps because we have a circular dependency. for ( SubunitVector::iterator it = m_subunits.begin(); it != m_subunits.end(); ++it ) { result &= (*it)->deserializeUpdate( basePath + "Subunit", deser ); } // load path /PcrPlug0/global_id result &= deserializePlugVector( basePath + "PcrPlug", deser, getPlugManager(), m_pcrPlugs ); // load path /ExternalPlug0/global_id result &= deserializePlugVector( basePath + "ExternalPlug", deser, getPlugManager(), m_externalPlugs ); result &= deserializeVector( basePath + "PlugConnection", deser, *this, m_plugConnections ); result &= deserializeVector( basePath + "Subunit", deser, *this, m_subunits ); result &= deserializeSyncInfoVector( basePath + "SyncInfo", deser, m_syncInfos ); // update connectsion between plugs (plug.m_inputConnections // and plug.m_outputConnnections list) m_pPlugManager->deserializeUpdate( basePath, deser ); // this might have changed since the cache was saved // if the config ID doesn't account for the clock source if(!rediscoverConnections()) { debugError("Could not rediscover plug connections\n"); } return result; } } // end of namespace libffado-2.4.5/src/libavc/general/avc_unit.h0000644000175000001440000001410114206145246020306 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVC_UNIT_H #define AVC_UNIT_H #include #include "debugmodule/debugmodule.h" #include "../avc_definitions.h" #include "../general/avc_extended_cmd_generic.h" #include "../general/avc_subunit.h" #include "../general/avc_plug.h" #include "../musicsubunit/avc_musicsubunit.h" #include "../audiosubunit/avc_audiosubunit.h" #include "libutil/serialize.h" #include #include class ConfigRom; class Ieee1394Service; namespace AVC { class Unit { public: Unit( ); virtual ~Unit(); virtual void setVerboseLevel(int l); virtual void show(); // these have to be implemented by the parent class /// Returns the 1394 service virtual Ieee1394Service& get1394Service() = 0; /// Returns the ConfigRom virtual ConfigRom& getConfigRom() const = 0; /// Discovers the unit's internals virtual bool discover(); PlugManager& getPlugManager() { return *m_pPlugManager; } struct SyncInfo { SyncInfo( Plug& source, Plug& destination, std::string description ) : m_source( &source ) , m_destination( &destination ) , m_description( description ) {} SyncInfo() : m_source( 0 ) , m_destination( 0 ) , m_description( "" ) {} Plug* m_source; Plug* m_destination; std::string m_description; }; typedef std::vector SyncInfoVector; virtual const SyncInfoVector& getSyncInfos() const { return m_syncInfos; } virtual const SyncInfo* getActiveSyncInfo(); virtual bool setActiveSync( const SyncInfo& syncInfo ); virtual bool serialize( std::string basePath, Util::IOSerialize& ser ) const; virtual bool deserialize( std::string basePath, Util::IODeserialize& deser ); SubunitAudio* getAudioSubunit( subunit_id_t subunitId ) { return dynamic_cast( getSubunit( eST_Audio , subunitId ));}; SubunitMusic* getMusicSubunit( subunit_id_t subunitId ) { return dynamic_cast( getSubunit( eST_Music , subunitId ));}; Subunit* getSubunit( subunit_type_t subunitType, subunit_id_t subunitId ) const; virtual AVC::Subunit* createSubunit(Unit& unit, ESubunitType type, subunit_t id ); virtual AVC::Plug* createPlug( AVC::Unit* unit, AVC::Subunit* subunit, AVC::function_block_type_t functionBlockType, AVC::function_block_type_t functionBlockId, AVC::Plug::EPlugAddressType plugAddressType, AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugId, int globalId = -1 ); protected: /// cleans the internal data structures that are created by discovery virtual bool clean(); virtual bool enumerateSubUnits(); virtual bool rediscoverConnections(); virtual bool discoverPlugConnections(); virtual bool discoverSubUnitsPlugConnections(); virtual bool discoverPlugs(); virtual bool discoverPlugsPCR( AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugMaxId ); virtual bool discoverPlugsExternal( AVC::Plug::EPlugDirection plugDirection, AVC::plug_id_t plugMaxId ); virtual bool propagatePlugInfo(); virtual bool discoverSyncModes(); virtual bool checkSyncConnectionsAndAddToList( AVC::PlugVector& plhs, AVC::PlugVector& prhs, std::string syncDescription ); virtual Plug* getSyncPlug( int maxPlugId, Plug::EPlugDirection ); unsigned int getNrOfSubunits( subunit_type_t subunitType ) const; PlugConnection* getPlugConnection( Plug& srcPlug ) const; Plug* getPlugById( PlugVector& plugs, Plug::EPlugDirection plugDireciton, int id ); PlugVector getPlugsByType( PlugVector& plugs, Plug::EPlugDirection plugDirection, Plug::EPlugType type); // bool setSamplingFrequencyPlug( Plug& plug, // Plug::EPlugDirection direction, // ESamplingFrequency samplingFrequency ); void showPlugs( PlugVector& plugs ) const; bool serializeSyncInfoVector( std::string basePath, Util::IOSerialize& ser, const SyncInfoVector& vec ) const; bool deserializeSyncInfoVector( std::string basePath, Util::IODeserialize& deser, SyncInfoVector& vec ); protected: SubunitVector m_subunits; PlugVector m_pcrPlugs; PlugVector m_externalPlugs; PlugConnectionVector m_plugConnections; PlugManager* m_pPlugManager; SyncInfoVector m_syncInfos; private: DECLARE_DEBUG_MODULE; }; } #endif libffado-2.4.5/src/libavc/general/avc_unit_info.cpp0000644000175000001440000000444414206145246021665 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_unit_info.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "libutil/ByteSwap.h" #include using namespace std; namespace AVC { UnitInfoCmd::UnitInfoCmd( Ieee1394Service& ieee1349service ) : AVCCommand( ieee1349service, AVC1394_CMD_UNIT_INFO ) , m_reserved( 0xff ) , m_unit_type( 0xff ) , m_unit( 0xff ) , m_company_id( 0xffffffff ) { } UnitInfoCmd::~UnitInfoCmd() { } bool UnitInfoCmd::serialize( Util::Cmd::IOSSerialize& se ) { AVCCommand::serialize( se ); se.write( m_reserved, "UnitInfoCmd reserved" ); byte_t operand = ( m_unit_type << 3 ) | ( m_unit & 0x7 ); se.write( operand, "UnitInfoCmd unit_type and unit" ); operand = ( m_company_id >> 16 ) & 0xff; se.write( operand, "UnitInfoCmd company_ID (2)" ); operand = ( m_company_id >> 8 ) & 0xff; se.write( operand, "UnitInfoCmd company_ID (1)" ); operand = ( m_company_id >> 0 ) & 0xff; se.write( operand, "UnitInfoCmd company_ID (0)" ); return true; } bool UnitInfoCmd::deserialize( Util::Cmd::IISDeserialize& de ) { AVCCommand::deserialize( de ); de.read( &m_reserved ); byte_t operand; de.read( &operand ); m_unit_type = ( operand >> 3 ); m_unit = ( operand & 0x7 ); de.read( &operand ); m_company_id = 0; m_company_id |= operand << 16; de.read( &operand ); m_company_id |= operand << 8; de.read( &operand ); m_company_id |= operand; return true; } } libffado-2.4.5/src/libavc/general/avc_unit_info.h0000644000175000001440000000262414206145246021330 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCUNITINFO_H #define AVCUNITINFO_H #include "avc_generic.h" #define AVC1394_CMD_UNIT_INFO 0x30 namespace AVC { class UnitInfoCmd: public AVCCommand { public: UnitInfoCmd( Ieee1394Service& ieee1349service ); virtual ~UnitInfoCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "UnitInfoCmd"; } reserved_t m_reserved; unit_type_t m_unit_type; unit_t m_unit; company_id_t m_company_id; }; } #endif // AVCUNITINFO_H libffado-2.4.5/src/libavc/general/avc_vendor_dependent_cmd.cpp0000644000175000001440000000404414206145246024035 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_vendor_dependent_cmd.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "libutil/ByteSwap.h" #include using namespace std; #define AVC1394_CMD_VENDOR_DEPENDENT 0x00 namespace AVC { VendorDependentCmd::VendorDependentCmd(Ieee1394Service& ieee1394service) : AVCCommand( ieee1394service, AVC1394_CMD_VENDOR_DEPENDENT ) { } VendorDependentCmd::~VendorDependentCmd() { } bool VendorDependentCmd::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= AVCCommand::serialize( se ); byte_t tmp; tmp=(m_companyId >> 16) & 0xFF; result &= se.write(tmp,"VendorDependentCmd companyid[2]"); tmp=(m_companyId >> 8) & 0xFF; result &= se.write(tmp,"VendorDependentCmd companyid[1]"); tmp=(m_companyId) & 0xFF; result &= se.write(tmp,"VendorDependentCmd companyid[0]"); return result; } bool VendorDependentCmd::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= AVCCommand::deserialize( de ); byte_t tmp[3]; result &= de.read(&tmp[2]); result &= de.read(&tmp[1]); result &= de.read(&tmp[0]); m_companyId = (tmp[2] << 16) | (tmp[1]<<8) | tmp[0]; return result; } } libffado-2.4.5/src/libavc/general/avc_vendor_dependent_cmd.h0000644000175000001440000000252114206145246023500 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCVENDORDEPENDENT_H #define AVCVENDORDEPENDENT_H #include "avc_generic.h" namespace AVC { class VendorDependentCmd: public AVCCommand { public: VendorDependentCmd(Ieee1394Service& ieee1394service); virtual ~VendorDependentCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "VendorDependentCmd"; } uint32_t m_companyId; }; } #endif // AVCVENDORDEPENDENT_H libffado-2.4.5/src/libavc/musicsubunit/0000755000175000001440000000000014206145612017442 5ustar jwoitheuserslibffado-2.4.5/src/libavc/musicsubunit/avc_descriptor_music.cpp0000644000175000001440000006402014206145246024362 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_descriptor_music.h" #include "../descriptors/avc_descriptor.h" #include "../descriptors/avc_descriptor_cmd.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "../general/avc_subunit.h" #include "../general/avc_unit.h" #include "libutil/ByteSwap.h" // info block implementations namespace AVC { AVCMusicGeneralStatusInfoBlock::AVCMusicGeneralStatusInfoBlock( ) : AVCInfoBlock( 0x8100 ) , m_current_transmit_capability ( 0 ) , m_current_receive_capability ( 0 ) , m_current_latency_capability ( 0xFFFFFFFF ) {} bool AVCMusicGeneralStatusInfoBlock::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= AVCInfoBlock::serialize(se); quadlet_t tmp=CondSwapToBus32(m_current_latency_capability); result &= se.write(m_current_transmit_capability, "AVCMusicGeneralStatusInfoBlock m_current_transmit_capability"); result &= se.write(m_current_receive_capability, "AVCMusicGeneralStatusInfoBlock m_current_receive_capability"); result &= se.write(tmp, "AVCMusicGeneralStatusInfoBlock m_current_latency_capability"); return result; } bool AVCMusicGeneralStatusInfoBlock::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= AVCInfoBlock::deserialize(de); if (m_primary_field_length != 6) { debugWarning("Incorrect primary field length: %u, should be 6\n", m_primary_field_length); return false; } result &= de.read(&m_current_transmit_capability); result &= de.read(&m_current_receive_capability); result &= de.read(&m_current_latency_capability); m_current_latency_capability=CondSwapFromBus32(m_current_latency_capability); return result; } // --------- AVCMusicOutputPlugStatusInfoBlock::AVCMusicOutputPlugStatusInfoBlock( ) : AVCInfoBlock( 0x8101 ) {} bool AVCMusicOutputPlugStatusInfoBlock::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= AVCInfoBlock::serialize(se); debugWarning("%s not supported\n", getInfoBlockName()); result=false; return result; } bool AVCMusicOutputPlugStatusInfoBlock::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= AVCInfoBlock::deserialize(de); debugWarning("%s not supported, skipping\n", getInfoBlockName()); de.skip(m_compound_length-4); return result; } // --------- AVCMusicClusterInfoBlock::AVCMusicClusterInfoBlock( ) : AVCInfoBlock( 0x810A ) , m_stream_format ( 0 ) , m_port_type ( 0 ) , m_nb_signals ( 0 ) {} AVCMusicClusterInfoBlock::~AVCMusicClusterInfoBlock( ) { clear(); } bool AVCMusicClusterInfoBlock::clear( ) { m_stream_format=0; m_port_type=0; m_nb_signals=0; m_SignalInfos.clear(); return true; } bool AVCMusicClusterInfoBlock::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= AVCInfoBlock::serialize(se); result &= se.write(m_stream_format, "AVCMusicClusterInfoBlock m_stream_format"); result &= se.write(m_port_type, "AVCMusicClusterInfoBlock m_port_type"); result &= se.write(m_nb_signals, "AVCMusicClusterInfoBlock m_nb_signals"); if (m_SignalInfos.size() != m_nb_signals) { debugError("not enough elements in AVCMusicClusterInfoBlock vector\n"); return false; } unsigned int cnt; for (cnt=0;cnt0) { result &= m_RawTextInfoBlock.serialize(se); } else if (m_NameInfoBlock.m_compound_length>0) { result &= m_NameInfoBlock.serialize(se); } return result; } bool AVCMusicClusterInfoBlock::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= AVCInfoBlock::deserialize(de); unsigned int consumed_at_start=de.getNrOfConsumedBytes(); result &= de.read(&m_stream_format); result &= de.read(&m_port_type); result &= de.read(&m_nb_signals); unsigned int cnt; for (cnt=0;cnt0) { uint16_t block_type; AVCInfoBlock::peekBlockType(de, &block_type); if(block_type==m_RawTextInfoBlock.m_supported_info_block_type) { result &= m_RawTextInfoBlock.deserialize(de); } else if (block_type==m_NameInfoBlock.m_supported_info_block_type) { result &= m_NameInfoBlock.deserialize(de); } else { debugWarning("Unexpected info block, skipping...\n"); de.skip(bytes_left); } } return result; } std::string AVCMusicClusterInfoBlock::getName() { if(m_RawTextInfoBlock.m_compound_length>0) { return m_RawTextInfoBlock.m_text; } else if (m_NameInfoBlock.m_compound_length>0) { return m_NameInfoBlock.m_text; } else { return std::string("Unknown"); } } void AVCMusicClusterInfoBlock::show() { debugOutput(DEBUG_LEVEL_NORMAL, "AVCMusicClusterInfoBlock %s\n", getName().c_str()); debugOutput(DEBUG_LEVEL_NORMAL, " m_stream_format......: 0x%02X\n", m_stream_format); debugOutput(DEBUG_LEVEL_NORMAL, " m_port_type..........: 0x%02X\n", m_port_type); debugOutput(DEBUG_LEVEL_NORMAL, " m_nb_signals.........: %d\n", m_nb_signals); int i=0; for ( AVCMusicClusterInfoBlock::SignalInfoVectorIterator sig_it = m_SignalInfos.begin(); sig_it != m_SignalInfos.end(); ++sig_it ) { struct AVCMusicClusterInfoBlock::sSignalInfo s=(*sig_it); debugOutput(DEBUG_LEVEL_NORMAL, " Signal %d\n", i); debugOutput(DEBUG_LEVEL_NORMAL, " music_plug_id........: 0x%04X\n", s.music_plug_id); debugOutput(DEBUG_LEVEL_NORMAL, " stream_position......: 0x%02X\n", s.stream_position); debugOutput(DEBUG_LEVEL_NORMAL, " stream_location......: 0x%02X\n", s.stream_location); i++; } } // --------- AVCMusicSubunitPlugInfoBlock::AVCMusicSubunitPlugInfoBlock( ) : AVCInfoBlock( 0x8109 ) , m_subunit_plug_id ( 0 ) , m_signal_format ( 0 ) , m_plug_type ( 0xFF ) , m_nb_clusters ( 0 ) , m_nb_channels ( 0 ) {} AVCMusicSubunitPlugInfoBlock::~AVCMusicSubunitPlugInfoBlock( ) { clear(); } bool AVCMusicSubunitPlugInfoBlock::clear() { m_subunit_plug_id=0; m_signal_format=0; m_plug_type=0xFF; m_nb_clusters=0; m_nb_channels=0; // clean up dynamically allocated stuff for ( AVCMusicClusterInfoBlockVectorIterator it = m_Clusters.begin(); it != m_Clusters.end(); ++it ) { delete *it; } m_Clusters.clear(); return true; } bool AVCMusicSubunitPlugInfoBlock::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= AVCInfoBlock::serialize(se); result &= se.write(m_subunit_plug_id, "AVCMusicPlugInfoBlock m_subunit_plug_id"); result &= se.write(m_signal_format, "AVCMusicPlugInfoBlock m_signal_format"); result &= se.write(m_plug_type, "AVCMusicPlugInfoBlock m_plug_type"); result &= se.write(m_nb_clusters, "AVCMusicPlugInfoBlock m_nb_clusters"); result &= se.write(m_nb_channels, "AVCMusicPlugInfoBlock m_nb_channels"); unsigned int cnt; if (m_Clusters.size() != m_nb_clusters) { debugError("not enough elements in AVCMusicClusterInfoBlock vector\n"); return false; } for (cnt=0;cntserialize(se); } // do the optional text/name info block if(m_RawTextInfoBlock.m_compound_length>0) { result &= m_RawTextInfoBlock.serialize(se); } else if (m_NameInfoBlock.m_compound_length>0) { result &= m_NameInfoBlock.serialize(se); } return result; } bool AVCMusicSubunitPlugInfoBlock::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= AVCInfoBlock::deserialize(de); if (m_primary_field_length != 8) { debugWarning("Incorrect primary field length: %u, should be 4\n", m_primary_field_length); return false; } unsigned int consumed_at_start=de.getNrOfConsumedBytes(); result &= de.read(&m_subunit_plug_id); result &= de.read(&m_signal_format); result &= de.read(&m_plug_type); result &= de.read(&m_nb_clusters); result &= de.read(&m_nb_channels); unsigned int cnt; for (cnt=0;cntdeserialize(de); } unsigned int consumed_at_cluster_end=de.getNrOfConsumedBytes(); // do the optional text info block // first find out if the block is present int bytes_done=4+consumed_at_cluster_end-consumed_at_start; int bytes_left=m_compound_length-bytes_done; debugOutput(DEBUG_LEVEL_VERBOSE,"len=%d, @start=%d @end=%d done=%d, left=%d\n", m_compound_length, consumed_at_start, consumed_at_cluster_end, bytes_done, bytes_left); if(bytes_left>0) { uint16_t block_type; AVCInfoBlock::peekBlockType(de, &block_type); if(block_type==m_RawTextInfoBlock.m_supported_info_block_type) { result &= m_RawTextInfoBlock.deserialize(de); } else if (block_type==m_NameInfoBlock.m_supported_info_block_type) { result &= m_NameInfoBlock.deserialize(de); } else { debugWarning("Unexpected info block, skipping...\n"); de.skip(bytes_left); } } return result; } std::string AVCMusicSubunitPlugInfoBlock::getName() { if(m_RawTextInfoBlock.m_compound_length>0) { return m_RawTextInfoBlock.m_text; } else if (m_NameInfoBlock.m_compound_length>0) { return m_NameInfoBlock.m_text; } else { return std::string("Unknown"); } } // --------- AVCMusicPlugInfoBlock::AVCMusicPlugInfoBlock( ) : AVCInfoBlock( 0x810B ) , m_music_plug_type ( 0 ) , m_music_plug_id ( 0 ) , m_routing_support ( 0 ) , m_source_plug_function_type ( 0 ) , m_source_plug_id ( 0 ) , m_source_plug_function_block_id ( 0 ) , m_source_stream_position ( 0 ) , m_source_stream_location ( 0 ) , m_dest_plug_function_type ( 0 ) , m_dest_plug_id ( 0 ) , m_dest_plug_function_block_id ( 0 ) , m_dest_stream_position ( 0 ) , m_dest_stream_location ( 0 ) {} bool AVCMusicPlugInfoBlock::clear( ) { m_music_plug_type=0; m_music_plug_id=0; m_routing_support=0; m_source_plug_function_type=0; m_source_plug_id=0; m_source_plug_function_block_id=0; m_source_stream_position=0; m_source_stream_location=0; m_dest_plug_function_type=0; m_dest_plug_id=0; m_dest_plug_function_block_id=0; m_dest_stream_position=0; m_dest_stream_location=0; return true; } bool AVCMusicPlugInfoBlock::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= AVCInfoBlock::serialize(se); result &= se.write(m_music_plug_type, "AVCMusicPlugInfoBlock m_music_plug_type" ); result &= se.write(m_music_plug_id, "AVCMusicPlugInfoBlock m_music_plug_id" ); result &= se.write(m_routing_support, "AVCMusicPlugInfoBlock m_routing_support" ); result &= se.write(m_source_plug_function_type, "AVCMusicPlugInfoBlock m_source_plug_function_type" ); result &= se.write(m_source_plug_id, "AVCMusicPlugInfoBlock m_source_plug_id" ); result &= se.write(m_source_plug_function_block_id, "AVCMusicPlugInfoBlock m_source_plug_function_block_id" ); result &= se.write(m_source_stream_position, "AVCMusicPlugInfoBlock m_source_stream_position" ); result &= se.write(m_source_stream_location, "AVCMusicPlugInfoBlock m_source_stream_location" ); result &= se.write(m_dest_plug_function_type, "AVCMusicPlugInfoBlock m_dest_plug_function_type" ); result &= se.write(m_dest_plug_id, "AVCMusicPlugInfoBlock m_dest_plug_id" ); result &= se.write(m_dest_plug_function_block_id, "AVCMusicPlugInfoBlock m_dest_plug_function_block_id" ); result &= se.write(m_dest_stream_position, "AVCMusicPlugInfoBlock m_dest_stream_position" ); result &= se.write(m_dest_stream_location, "AVCMusicPlugInfoBlock m_dest_stream_location" ); // do the optional text/name info block if(m_RawTextInfoBlock.m_compound_length>0) { result &= m_RawTextInfoBlock.serialize(se); } else if (m_NameInfoBlock.m_compound_length>0) { result &= m_NameInfoBlock.serialize(se); } return result; } bool AVCMusicPlugInfoBlock::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= AVCInfoBlock::deserialize(de); if (m_primary_field_length != 14) { debugWarning("Incorrect primary field length: %u, should be 4\n", m_primary_field_length); return false; } result &= de.read(&m_music_plug_type); result &= de.read(&m_music_plug_id); result &= de.read(&m_routing_support); result &= de.read(&m_source_plug_function_type); result &= de.read(&m_source_plug_id); result &= de.read(&m_source_plug_function_block_id); result &= de.read(&m_source_stream_position); result &= de.read(&m_source_stream_location); result &= de.read(&m_dest_plug_function_type); result &= de.read(&m_dest_plug_id); result &= de.read(&m_dest_plug_function_block_id); result &= de.read(&m_dest_stream_position); result &= de.read(&m_dest_stream_location); if(m_compound_length>18) { uint16_t block_type; AVCInfoBlock::peekBlockType(de, &block_type); if(block_type==m_RawTextInfoBlock.m_supported_info_block_type) { result &= m_RawTextInfoBlock.deserialize(de); } else if (block_type==m_NameInfoBlock.m_supported_info_block_type) { result &= m_NameInfoBlock.deserialize(de); } else { debugWarning("Unexpected info block, skipping...\n"); de.skip(m_compound_length-18); } } return result; } std::string AVCMusicPlugInfoBlock::getName() { if(m_RawTextInfoBlock.m_compound_length>0) { return m_RawTextInfoBlock.m_text; } else if (m_NameInfoBlock.m_compound_length>0) { return m_NameInfoBlock.m_text; } else { return std::string("Unknown"); } } void AVCMusicPlugInfoBlock::show() { debugOutput(DEBUG_LEVEL_NORMAL, "AVCMusicPlugInfoBlock %s\n", getName().c_str()); debugOutput(DEBUG_LEVEL_NORMAL, " m_music_plug_type...............: 0x%02X\n", m_music_plug_type); debugOutput(DEBUG_LEVEL_NORMAL, " m_music_plug_id.................: 0x%04X\n", m_music_plug_id); debugOutput(DEBUG_LEVEL_NORMAL, " m_routing_support...............: 0x%02X\n", m_routing_support); debugOutput(DEBUG_LEVEL_NORMAL, " m_source_plug_function_type.....: 0x%02X\n", m_source_plug_function_type); debugOutput(DEBUG_LEVEL_NORMAL, " m_source_plug_id................: 0x%02X\n", m_source_plug_id); debugOutput(DEBUG_LEVEL_NORMAL, " m_source_plug_function_block_id.: 0x%02X\n", m_source_plug_function_block_id); debugOutput(DEBUG_LEVEL_NORMAL, " m_source_stream_position........: 0x%02X\n", m_source_stream_position); debugOutput(DEBUG_LEVEL_NORMAL, " m_source_stream_location........: 0x%02X\n", m_source_stream_location); debugOutput(DEBUG_LEVEL_NORMAL, " m_dest_plug_function_type.......: 0x%02X\n", m_dest_plug_function_type); debugOutput(DEBUG_LEVEL_NORMAL, " m_dest_plug_id..................: 0x%02X\n", m_dest_plug_id); debugOutput(DEBUG_LEVEL_NORMAL, " m_dest_plug_function_block_id...: 0x%02X\n", m_dest_plug_function_block_id); debugOutput(DEBUG_LEVEL_NORMAL, " m_dest_stream_position..........: 0x%02X\n", m_dest_stream_position); debugOutput(DEBUG_LEVEL_NORMAL, " m_dest_stream_location..........: 0x%02X\n", m_dest_stream_location); } // --------- AVCMusicRoutingStatusInfoBlock::AVCMusicRoutingStatusInfoBlock( ) : AVCInfoBlock( 0x8108 ) , m_nb_dest_plugs ( 0 ) , m_nb_source_plugs ( 0 ) , m_nb_music_plugs ( 0 ) {} AVCMusicRoutingStatusInfoBlock::~AVCMusicRoutingStatusInfoBlock( ) { clear(); } bool AVCMusicRoutingStatusInfoBlock::clear() { m_nb_dest_plugs=0; m_nb_source_plugs=0; m_nb_music_plugs=0; // clean up dynamically allocated stuff for ( AVCMusicSubunitPlugInfoBlockVectorIterator it = mDestPlugInfoBlocks.begin(); it != mDestPlugInfoBlocks.end(); ++it ) { delete *it; } mDestPlugInfoBlocks.clear(); for ( AVCMusicSubunitPlugInfoBlockVectorIterator it = mSourcePlugInfoBlocks.begin(); it != mSourcePlugInfoBlocks.end(); ++it ) { delete *it; } mSourcePlugInfoBlocks.clear(); for ( AVCMusicPlugInfoBlockVectorIterator it = mMusicPlugInfoBlocks.begin(); it != mMusicPlugInfoBlocks.end(); ++it ) { delete *it; } mMusicPlugInfoBlocks.clear(); return true; } bool AVCMusicRoutingStatusInfoBlock::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= AVCInfoBlock::serialize(se); result &= se.write(m_nb_dest_plugs, "AVCMusicRoutingStatusInfoBlock m_nb_dest_plugs"); result &= se.write(m_nb_source_plugs, "AVCMusicRoutingStatusInfoBlock m_nb_source_plugs"); result &= se.write(m_nb_music_plugs, "AVCMusicRoutingStatusInfoBlock m_nb_music_plugs"); unsigned int cnt; if (mDestPlugInfoBlocks.size() != m_nb_dest_plugs) { debugError("not enough elements in dest AVCMusicSubunitPlugInfoBlock vector\n"); return false; } for (cnt=0;cntserialize(se); } if (mSourcePlugInfoBlocks.size() != m_nb_source_plugs) { debugError("not enough elements in src AVCMusicSubunitPlugInfoBlock\n"); return false; } for (cnt=0;cntserialize(se); } if (mMusicPlugInfoBlocks.size() != m_nb_music_plugs) { debugError("not enough elements in AVCMusicPlugInfoBlock\n"); return false; } for (cnt=0;cntserialize(se); } return result; } bool AVCMusicRoutingStatusInfoBlock::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; result &= AVCInfoBlock::deserialize(de); if (m_primary_field_length != 4) { debugWarning("Incorrect primary field length: %u, should be 4\n", m_primary_field_length); return false; } result &= de.read(&m_nb_dest_plugs); result &= de.read(&m_nb_source_plugs); result &= de.read(&m_nb_music_plugs); unsigned int cnt; for (cnt=0;cntdeserialize(de); } for (cnt=0;cntdeserialize(de); } for (cnt=0;cntdeserialize(de); } return result; } AVCMusicSubunitPlugInfoBlock * AVCMusicRoutingStatusInfoBlock::getSubunitPlugInfoBlock(Plug::EPlugDirection direction, plug_id_t id) { if (direction == Plug::eAPD_Input) { for ( AVCMusicSubunitPlugInfoBlockVectorIterator it = mDestPlugInfoBlocks.begin(); it != mDestPlugInfoBlocks.end(); ++it ) { AVCMusicSubunitPlugInfoBlock *b=(*it); if (b->m_subunit_plug_id == id) return b; } debugOutput(DEBUG_LEVEL_VERBOSE, "no plug info found.\n"); return NULL; } else if (direction == Plug::eAPD_Output) { for ( AVCMusicSubunitPlugInfoBlockVectorIterator it = mSourcePlugInfoBlocks.begin(); it != mSourcePlugInfoBlocks.end(); ++it ) { AVCMusicSubunitPlugInfoBlock *b=(*it); if (b->m_subunit_plug_id == id) return b; } debugOutput(DEBUG_LEVEL_VERBOSE, "no plug info found.\n"); return NULL; } else { debugOutput(DEBUG_LEVEL_VERBOSE, "Invalid direction.\n"); return NULL; } } AVCMusicPlugInfoBlock * AVCMusicRoutingStatusInfoBlock::getMusicPlugInfoBlock(plug_id_t id) { for ( AVCMusicPlugInfoBlockVectorIterator it = mMusicPlugInfoBlocks.begin(); it != mMusicPlugInfoBlocks.end(); ++it ) { AVCMusicPlugInfoBlock *b=(*it); if (b->m_music_plug_id == id) return b; } debugOutput(DEBUG_LEVEL_VERBOSE, "no music plug info found.\n"); return NULL; } // ---------------------- AVCMusicStatusDescriptor::AVCMusicStatusDescriptor( Unit* unit, Subunit* subunit ) : AVCDescriptor(unit, subunit, AVCDescriptorSpecifier::eSubunit0x80) {} bool AVCMusicStatusDescriptor::serialize( Util::Cmd::IOSSerialize& se ) { bool result=true; result &= AVCDescriptor::serialize(se); result &= m_general_status_infoblock.serialize(se); if (m_output_plug_status_infoblock.m_compound_length>0) { result &= m_output_plug_status_infoblock.serialize(se); } if (m_routing_status_infoblock.m_compound_length>0) { result &= m_routing_status_infoblock.serialize(se); } return true; } bool AVCMusicStatusDescriptor::deserialize( Util::Cmd::IISDeserialize& de ) { bool result=true; unsigned int blocks_done=0; const unsigned int max_blocks=10; result &= AVCDescriptor::deserialize(de); uint16_t block_type; uint16_t block_length; // process all infoblocks until done or until failure while(AVCInfoBlock::peekBlockType(de, &block_type) && result) { AVCInfoBlock::peekBlockLength(de, &block_length); debugOutput(DEBUG_LEVEL_VERBOSE, "type=0x%04X, length=%u\n",block_type, block_length); switch (block_type) { case 0x8100: m_general_status_infoblock.setVerbose(getVerboseLevel()); result &= m_general_status_infoblock.deserialize(de); break; case 0x8101: m_output_plug_status_infoblock.setVerbose(getVerboseLevel()); result &= m_output_plug_status_infoblock.deserialize(de); break; case 0x8108: m_routing_status_infoblock.setVerbose(getVerboseLevel()); result &= m_routing_status_infoblock.deserialize(de); break; default: debugWarning("Unknown info block type: 0x%04X, length=%u, skipping...\n", block_type, block_length); de.skip(block_length); break; } if(blocks_done++>max_blocks) { debugError("Too much info blocks in descriptor, probably a runaway parser\n"); break; } } return result; } AVCMusicSubunitPlugInfoBlock * AVCMusicStatusDescriptor::getSubunitPlugInfoBlock(Plug::EPlugDirection direction, plug_id_t id) { return m_routing_status_infoblock.getSubunitPlugInfoBlock(direction, id); } AVCMusicPlugInfoBlock * AVCMusicStatusDescriptor::getMusicPlugInfoBlock(plug_id_t id) { return m_routing_status_infoblock.getMusicPlugInfoBlock(id); } unsigned int AVCMusicStatusDescriptor::getNbMusicPlugs() { return m_routing_status_infoblock.m_nb_music_plugs; } } libffado-2.4.5/src/libavc/musicsubunit/avc_descriptor_music.h0000644000175000001440000001666314206145246024041 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** * Implements the AV/C Descriptors/InfoBlocks for the Music Subunit as in TA2004007 * */ #ifndef AVCDESCRIPTORMUSIC_H #define AVCDESCRIPTORMUSIC_H #include "../descriptors/avc_descriptor.h" #include "../avc_definitions.h" #include "../general/avc_generic.h" #include "../general/avc_plug.h" #include "debugmodule/debugmodule.h" #include #include class Ieee1394Service; namespace AVC { /** * The info blocks */ class AVCMusicGeneralStatusInfoBlock : public AVCInfoBlock { public: virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); AVCMusicGeneralStatusInfoBlock( ); virtual ~AVCMusicGeneralStatusInfoBlock() {}; virtual const char* getInfoBlockName() const {return "AVCMusicGeneralStatusInfoBlock";}; byte_t m_current_transmit_capability; byte_t m_current_receive_capability; quadlet_t m_current_latency_capability; protected: private: }; class AVCMusicOutputPlugStatusInfoBlock : public AVCInfoBlock { public: virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); AVCMusicOutputPlugStatusInfoBlock( ); virtual ~AVCMusicOutputPlugStatusInfoBlock() {}; virtual const char* getInfoBlockName() const {return "AVCMusicOutputPlugStatusInfoBlock";}; protected: private: }; class AVCMusicClusterInfoBlock : public AVCInfoBlock { public: struct sSignalInfo { uint16_t music_plug_id; byte_t stream_position; byte_t stream_location; }; typedef std::vector SignalInfoVector; typedef std::vector::iterator SignalInfoVectorIterator; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual bool clear(); std::string getName(); AVCMusicClusterInfoBlock( ); virtual ~AVCMusicClusterInfoBlock(); virtual const char* getInfoBlockName() const {return "AVCMusicClusterInfoBlock";}; byte_t m_stream_format; byte_t m_port_type; byte_t m_nb_signals; SignalInfoVector m_SignalInfos; AVCRawTextInfoBlock m_RawTextInfoBlock; AVCNameInfoBlock m_NameInfoBlock; virtual void show(); protected: private: }; typedef std::vector AVCMusicClusterInfoBlockVector; typedef std::vector::iterator AVCMusicClusterInfoBlockVectorIterator; class AVCMusicSubunitPlugInfoBlock : public AVCInfoBlock { public: enum AVCMusicSubunitPlugInfoBlockPlugType { ePT_IsoStream = 0x0, ePT_AsyncStream = 0x1, ePT_Midi = 0x2, ePT_Sync = 0x3, ePT_Analog = 0x4, ePT_Digital = 0x5, ePT_Unknown = 0xff, }; virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); AVCMusicSubunitPlugInfoBlock( ); virtual ~AVCMusicSubunitPlugInfoBlock(); virtual const char* getInfoBlockName() const {return "AVCMusicSubunitPlugInfoBlock";}; virtual bool clear(); std::string getName(); byte_t m_subunit_plug_id; uint16_t m_signal_format; byte_t m_plug_type; uint16_t m_nb_clusters; uint16_t m_nb_channels; AVCMusicClusterInfoBlockVector m_Clusters; AVCRawTextInfoBlock m_RawTextInfoBlock; AVCNameInfoBlock m_NameInfoBlock; protected: private: }; typedef std::vector AVCMusicSubunitPlugInfoBlockVector; typedef std::vector::iterator AVCMusicSubunitPlugInfoBlockVectorIterator; class AVCMusicPlugInfoBlock : public AVCInfoBlock { public: virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual bool clear(); AVCMusicPlugInfoBlock( ); virtual ~AVCMusicPlugInfoBlock() {}; virtual const char* getInfoBlockName() const {return "AVCMusicPlugInfoBlock";}; std::string getName(); byte_t m_music_plug_type; uint16_t m_music_plug_id; byte_t m_routing_support; byte_t m_source_plug_function_type; byte_t m_source_plug_id; byte_t m_source_plug_function_block_id; byte_t m_source_stream_position; byte_t m_source_stream_location; byte_t m_dest_plug_function_type; byte_t m_dest_plug_id; byte_t m_dest_plug_function_block_id; byte_t m_dest_stream_position; byte_t m_dest_stream_location; AVCRawTextInfoBlock m_RawTextInfoBlock; AVCNameInfoBlock m_NameInfoBlock; virtual void show(); protected: private: }; typedef std::vector AVCMusicPlugInfoBlockVector; typedef std::vector::iterator AVCMusicPlugInfoBlockVectorIterator; class AVCMusicRoutingStatusInfoBlock : public AVCInfoBlock { public: virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); AVCMusicRoutingStatusInfoBlock( ); virtual ~AVCMusicRoutingStatusInfoBlock(); virtual const char* getInfoBlockName() const {return "AVCMusicRoutingStatusInfoBlock";}; AVCMusicSubunitPlugInfoBlock *getSubunitPlugInfoBlock(Plug::EPlugDirection, plug_id_t); AVCMusicPlugInfoBlock *getMusicPlugInfoBlock(plug_id_t); virtual bool clear(); byte_t m_nb_dest_plugs; byte_t m_nb_source_plugs; uint16_t m_nb_music_plugs; AVCMusicSubunitPlugInfoBlockVector mDestPlugInfoBlocks; AVCMusicSubunitPlugInfoBlockVector mSourcePlugInfoBlocks; AVCMusicPlugInfoBlockVector mMusicPlugInfoBlocks; protected: private: }; /** * */ class AVCMusicStatusDescriptor : public AVCDescriptor { public: virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); AVCMusicStatusDescriptor( Unit* unit, Subunit* subunit ); virtual ~AVCMusicStatusDescriptor() {} virtual const char* getDescriptorName() const {return "AVCMusicStatusDescriptor";}; AVCMusicSubunitPlugInfoBlock *getSubunitPlugInfoBlock(Plug::EPlugDirection, plug_id_t); AVCMusicPlugInfoBlock *getMusicPlugInfoBlock(plug_id_t); unsigned int getNbMusicPlugs(); private: // the child info blocks AVCMusicGeneralStatusInfoBlock m_general_status_infoblock; AVCMusicOutputPlugStatusInfoBlock m_output_plug_status_infoblock; AVCMusicRoutingStatusInfoBlock m_routing_status_infoblock; }; } #endif // AVCDESCRIPTORMUSIC_H libffado-2.4.5/src/libavc/musicsubunit/avc_musicsubunit.cpp0000644000175000001440000002553614206145246023547 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "libieee1394/configrom.h" #include "../general/avc_subunit.h" #include "../general/avc_unit.h" #include "../general/avc_plug_info.h" #include "../streamformat/avc_extended_stream_format.h" #include "libutil/cmd_serialize.h" #include "avc_musicsubunit.h" #include "avc_descriptor_music.h" #include #include namespace AVC { //////////////////////////////////////////// SubunitMusic::SubunitMusic( Unit& unit, subunit_t id ) : Subunit( unit, eST_Music, id ) , m_status_descriptor ( new AVCMusicStatusDescriptor( &unit, this ) ) { } SubunitMusic::SubunitMusic() : Subunit() , m_status_descriptor ( NULL ) { } SubunitMusic::~SubunitMusic() { if (m_status_descriptor) delete m_status_descriptor; } bool SubunitMusic::discover() { debugOutput(DEBUG_LEVEL_NORMAL, "Discovering %s...\n", getName()); // discover the AV/C generic part if ( !Subunit::discover() ) { return false; } // now we have the subunit plugs return true; } bool SubunitMusic::initPlugFromDescriptor( Plug& plug ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Loading info from descriptor for plug: \n"); bool result=true; // load the descriptor (if not already loaded) if (m_status_descriptor != NULL) { result &= m_status_descriptor->load(); } AVCMusicSubunitPlugInfoBlock *info; info = m_status_descriptor->getSubunitPlugInfoBlock(plug.getDirection(), plug.getPlugId()); if (info == NULL) { debugError("Could not find plug info block\n"); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, "Found plug: %s\n",info->getName().c_str()); // plug name result &= plug.setName(info->getName()); // plug type switch (info->m_plug_type) { case AVCMusicSubunitPlugInfoBlock::ePT_IsoStream: result &= plug.setPlugType(Plug::eAPT_IsoStream); break; case AVCMusicSubunitPlugInfoBlock::ePT_AsyncStream: result &= plug.setPlugType(Plug::eAPT_AsyncStream); break; case AVCMusicSubunitPlugInfoBlock::ePT_Midi: result &= plug.setPlugType(Plug::eAPT_Midi); break; case AVCMusicSubunitPlugInfoBlock::ePT_Sync: result &= plug.setPlugType(Plug::eAPT_Sync); break; case AVCMusicSubunitPlugInfoBlock::ePT_Analog: result &= plug.setPlugType(Plug::eAPT_Analog); break; case AVCMusicSubunitPlugInfoBlock::ePT_Digital: result &= plug.setPlugType(Plug::eAPT_Digital); break; } // number of channels result &= plug.setNrOfChannels(info->m_nb_channels); int idx=1; for ( AVCMusicClusterInfoBlockVectorIterator it = info->m_Clusters.begin(); it != info->m_Clusters.end(); ++it ) { struct Plug::ClusterInfo cinfo; AVCMusicClusterInfoBlock *c=(*it); cinfo.m_index=idx; //FIXME: is this correct? cinfo.m_portType=c->m_port_type; cinfo.m_nrOfChannels=c->m_nb_signals; cinfo.m_streamFormat=c->m_stream_format; cinfo.m_name=c->getName(); debugOutput(DEBUG_LEVEL_VERBOSE, "Adding cluster idx=%2d type=%02X nbch=%2d fmt=%02X name=%s\n", cinfo.m_index, cinfo.m_portType, cinfo.m_nrOfChannels, cinfo.m_streamFormat, cinfo.m_name.c_str()); for ( AVCMusicClusterInfoBlock::SignalInfoVectorIterator sig_it = c->m_SignalInfos.begin(); sig_it != c->m_SignalInfos.end(); ++sig_it ) { struct AVCMusicClusterInfoBlock::sSignalInfo s=(*sig_it); struct Plug::ChannelInfo sinfo; sinfo.m_streamPosition = s.stream_position; sinfo.m_location = s.stream_location; AVCMusicPlugInfoBlock *mplug = m_status_descriptor->getMusicPlugInfoBlock(s.music_plug_id); if (mplug==NULL) { debugWarning("No music plug found for this signal\n"); sinfo.m_name = "unknown"; } else { sinfo.m_name = mplug->getName(); #if AVC_STREAMCONFIG_USE_MUSICPLUG if (plug.getDirection() == Plug::eAPD_Input) { // it's an input plug to the subunit // so we have to check the source field of the music plug if(s.stream_position != mplug->m_source_stream_position) { debugWarning("s.stream_position (= 0x%02X) != mplug->m_source_stream_position (= 0x%02X)\n", s.stream_position, mplug->m_source_stream_position); // use the one from the music plug sinfo.m_streamPosition= mplug->m_source_stream_position; } if(s.stream_location != mplug->m_source_stream_location) { debugWarning("s.stream_location (= 0x%02X) != mplug->m_source_stream_location (= 0x%02X)\n", s.stream_location, mplug->m_source_stream_location); // use the one from the music plug sinfo.m_location=mplug->m_source_stream_location; } } else if (plug.getDirection() == Plug::eAPD_Output) { // it's an output plug from the subunit // so we have to check the destination field of the music plug if(s.stream_position != mplug->m_dest_stream_position) { debugWarning("s.stream_position (= 0x%02X) != mplug->m_dest_stream_position (= 0x%02X)\n", s.stream_position, mplug->m_dest_stream_position); // use the one from the music plug sinfo.m_streamPosition=mplug->m_dest_stream_position; } if(s.stream_location != mplug->m_dest_stream_location) { debugWarning("s.stream_location (= 0x%02X) != mplug->m_dest_stream_location (= 0x%02X)\n", s.stream_location, mplug->m_dest_stream_location); // use the one from the music plug //sinfo.m_location=mplug->m_dest_stream_location; // HACK: recent ECHO firmware only fills the AVCMusicClusterInfoBlock correctly sinfo.m_location=s.stream_location; } } else { debugWarning("Invalid plug direction.\n"); } #endif } debugOutput(DEBUG_LEVEL_VERBOSE, "Adding signal pos=%2d loc=%2d name=%s\n", sinfo.m_streamPosition, sinfo.m_location, sinfo.m_name.c_str()); cinfo.m_channelInfos.push_back(sinfo); } idx++; plug.getClusterInfos().push_back(cinfo); } return result; } bool SubunitMusic::loadDescriptors() { bool result=true; if (m_status_descriptor != NULL) { result &= m_status_descriptor->load(); } else { debugError("BUG: m_status_descriptor == NULL\n"); return false; } return result; } void SubunitMusic::showMusicPlugs() { if(m_status_descriptor) { unsigned int nbplugs = m_status_descriptor->getNbMusicPlugs(); printf( "digraph musicplugconnections {\n" ); for(unsigned int i=0;igetMusicPlugInfoBlock(i); if(mplug==NULL) { debugError("NULL plug!\n"); return; } char plugstr[32]; snprintf(plugstr, 32, "MusicPlug %d", mplug->m_music_plug_id); // the plug itself printf( "\t\"%s\" [color=red,style=filled];\n", plugstr ); Plug * plug; // the source connection (where it's signal originates) plug = m_unit->getPlugManager().getPlug( eST_Music, 0, 0xFF, 0xFF, Plug::eAPA_SubunitPlug, Plug::eAPD_Input, mplug->m_source_plug_id); if(plug) { printf( "\t\"(%d) %s\" -> \"%s\"\n", plug->getGlobalId(), plug->getName(), plugstr ); } else { debugWarning("Destination plug not found\n"); } // the destination connection (where it's signal goes) plug = m_unit->getPlugManager().getPlug( eST_Music, 0, 0xFF, 0xFF, Plug::eAPA_SubunitPlug, Plug::eAPD_Output, mplug->m_dest_plug_id); if(plug) { printf( "\t\"%s\" -> \"(%d) %s\"\n", plugstr, plug->getGlobalId(), plug->getName() ); } else { debugWarning("Source plug not found\n"); } } printf("}\n" ); printf( "Use \"dot -Tps FILENAME.dot -o FILENAME.ps\" " "to generate graph\n"); } } void SubunitMusic::setVerboseLevel(int l) { Subunit::setVerboseLevel(l); if (m_status_descriptor != NULL) { m_status_descriptor->setVerboseLevel(l); } } const char* SubunitMusic::getName() { return "MusicSubunit"; } bool SubunitMusic::serializeChild( std::string basePath, Util::IOSerialize& ser ) const { return true; } bool SubunitMusic::deserializeChild( std::string basePath, Util::IODeserialize& deser, Unit& unit ) { return true; } bool SubunitMusic::deserializeUpdateChild( std::string basePath, Util::IODeserialize& deser ) { return true; } } libffado-2.4.5/src/libavc/musicsubunit/avc_musicsubunit.h0000644000175000001440000000373414206145246023210 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVC_MUSICSUBUNIT_H #define AVC_MUSICSUBUNIT_H #include #include "libutil/serialize.h" namespace AVC { class Unit; class Plug; class AVCMusicStatusDescriptor; // ///////////////////////////// class SubunitMusic: public Subunit { public: SubunitMusic( Unit& avDevice, subunit_t id ); SubunitMusic(); virtual ~SubunitMusic(); virtual bool discover(); virtual bool initPlugFromDescriptor( Plug& plug ); virtual bool loadDescriptors(); virtual void showMusicPlugs(); virtual void setVerboseLevel(int l); virtual const char* getName(); protected: virtual bool serializeChild( std::string basePath, Util::IOSerialize& ser ) const; virtual bool deserializeChild( std::string basePath, Util::IODeserialize& deser, Unit& avDevice ); virtual bool deserializeUpdateChild( std::string basePath, Util::IODeserialize& deser ); class AVCMusicStatusDescriptor* m_status_descriptor; }; } #endif libffado-2.4.5/src/libavc/streamformat/0000755000175000001440000000000014206145612017414 5ustar jwoitheuserslibffado-2.4.5/src/libavc/streamformat/avc_extended_stream_format.cpp0000644000175000001440000002523014206145246025501 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "avc_extended_stream_format.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include "libutil/ByteSwap.h" 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::Cmd::IOSSerialize& se ) { se.write( m_numberOfChannels, "StreamFormatInfo numberOfChannels" ); se.write( m_streamFormat, "StreamFormatInfo streamFormat" ); return true; } bool StreamFormatInfo::deserialize( Util::Cmd::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::Cmd::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::Cmd::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::Cmd::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::Cmd::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::Cmd::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::Cmd::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::Cmd::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::Cmd::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; } } libffado-2.4.5/src/libavc/streamformat/avc_extended_stream_format.h0000644000175000001440000003340514206145246025151 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef AVCEXTENDEDSTREAMFROMAT_H #define AVCEXTENDEDSTREAMFROMAT_H #include "../general/avc_generic.h" #include "../general/avc_extended_cmd_generic.h" #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_IEC60958_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_IEC60958_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 enum ERateControl { eRC_Supported = 0x00, eRC_DontCare = 0x01, }; //////////////////////////////////////////////////////////// class StreamFormatInfo: public IBusData { public: StreamFormatInfo(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::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::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::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::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::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_IEC60958_3 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC60958_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::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::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::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::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 libffado-2.4.5/src/libavc/util/0000755000175000001440000000000014206145612015665 5ustar jwoitheuserslibffado-2.4.5/src/libcontrol/0000755000175000001440000000000014206145612015617 5ustar jwoitheuserslibffado-2.4.5/src/libcontrol/BasicElements.cpp0000644000175000001440000000161214206145246021044 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "BasicElements.h" namespace Control { // no implementations } libffado-2.4.5/src/libcontrol/BasicElements.h0000644000175000001440000001050414206145246020511 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef CONTROL_BASICELEMENTS_H #define CONTROL_BASICELEMENTS_H #include "debugmodule/debugmodule.h" #include #include #include "Element.h" namespace Control { /*! @brief Base class for contignous control elements */ class Continuous : public Element { public: Continuous(Element *p) : Element(p) {}; Continuous(Element *p, std::string n) : Element(p, n) {}; virtual ~Continuous() {}; virtual bool setValue(double v) = 0; virtual double getValue() = 0; virtual bool setValue(int idx, double v) = 0; virtual double getValue(int idx) = 0; virtual double getMinimum() = 0; virtual double getMaximum() = 0; }; /*! @brief Base class for discrete control elements */ class Discrete : public Element { public: Discrete(Element *p) : Element(p) {}; Discrete(Element *p, std::string n) : Element(p, n) {}; virtual ~Discrete() {}; virtual bool setValue(int v) = 0; virtual int getValue() = 0; virtual bool setValue(int idx, int v) = 0; virtual int getValue(int idx) = 0; virtual int getMinimum() = 0; virtual int getMaximum() = 0; }; /*! @brief Base class for textual control elements */ class Text : public Element { public: Text(Element *p) : Element(p) {}; Text(Element *p, std::string n) : Element(p, n) {}; virtual ~Text() {}; virtual bool setValue(std::string v) = 0; virtual std::string getValue() = 0; }; /*! @brief Base class for register access control elements */ class Register : public Element { public: Register(Element *p) : Element(p) {}; Register(Element *p, std::string n) : Element(p, n) {}; virtual ~Register() {}; virtual bool setValue(uint64_t addr, uint64_t value) = 0; virtual uint64_t getValue(uint64_t addr) = 0; }; /*! @brief Base class for basic enumerated control elements */ class Enum : public Element { public: Enum(Element *p) : Element(p) {}; Enum(Element *p, std::string n) : Element(p, n) {}; virtual ~Enum() {}; virtual bool select(int idx) = 0; virtual int selected() = 0; virtual int count() = 0; virtual std::string getEnumLabel(int idx) = 0; ///> Some devices update their internal configuration on select virtual bool devConfigChanged(int idx) { return false; }; }; /*! @brief Base class for attribute enumerated control elements The idea of this is that one can have a set of config values available for a certain enum choice. Example: for clock source selection: idx Label signal locked available 0 WordClock 0 0 1 1 S/PDIF 1 0 1 ... Attributes: 0 signal 1 locked 2 available */ class AttributeEnum : public Enum { public: AttributeEnum(Element *p) : Enum(p) {}; AttributeEnum(Element *p, std::string n) : Enum(p, n) {}; virtual ~AttributeEnum() {}; virtual int attributeCount() = 0; ///> get a specific attribute value for the selected enum virtual std::string getAttributeValue(int attridx) = 0; ///> get the name of the attribute with a certain index virtual std::string getAttributeName(int attridx) = 0; }; /*! @brief Base class for basic boolean control elements */ class Boolean : public Element { public: Boolean(Element *p) : Element(p) {}; Boolean(Element *p, std::string n) : Element(p, n) {}; virtual ~Boolean() {}; virtual bool select(bool) = 0; virtual bool selected() = 0; virtual std::string getBooleanLabel(bool n) { if (n) return "True"; return "False"; } }; }; // namespace Control #endif // CONTROL_BASICELEMENTS_H libffado-2.4.5/src/libcontrol/ClockSelect.cpp0000644000175000001440000002052214206145246020522 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ClockSelect.h" #include "ffadodevice.h" #include namespace Control { //// --- ClockSelect --- //// ClockSelect::ClockSelect(FFADODevice &d) : AttributeEnum(&d) , m_Device( d ) { setName("ClockSelect"); setLabel("Clock Source"); setDescription("Select the device clock source"); } bool ClockSelect::select(int idx) { debugOutput(DEBUG_LEVEL_VERBOSE, "Selecting clock idx: %d\n", idx); FFADODevice::ClockSourceVector v = m_Device.getSupportedClockSources(); if(idx >= (int)v.size()) { debugError("index out of range\n"); return false; } if(idx < 0) { debugError("index < 0\n"); return false; } if(!m_Device.setActiveClockSource(v.at(idx))) { debugWarning("could not set active clocksource\n"); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, " clock id: %d\n", v.at(idx).id); return true; } int ClockSelect::selected() { debugOutput(DEBUG_LEVEL_VERBOSE, "Finding active clock\n"); FFADODevice::ClockSourceVector v = m_Device.getSupportedClockSources(); FFADODevice::ClockSource active = m_Device.getActiveClockSource(); int i=0; for (i=0; i < (int)v.size(); i++) { FFADODevice::ClockSource c = v.at(i); if(c.id == active.id) { debugOutput(DEBUG_LEVEL_VERBOSE, " Active clock at %d, id %d\n", i, c.id); return i; } } debugError("No active clock source found!\n"); return -1; } int ClockSelect::count() { return m_Device.getSupportedClockSources().size(); } std::string ClockSelect::getEnumLabel(int idx) { FFADODevice::ClockSourceVector v = m_Device.getSupportedClockSources(); if(idx >= (int)v.size()) { debugError("index out of range\n"); return "Error"; } if(idx < 0) { debugError("index < 0\n"); return "Error"; } return v.at(idx).description; } int ClockSelect::attributeCount() { /* /// 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; */ return 7; } std::string ClockSelect::getAttributeValue(int attridx) { char tmp[16]; std::string retval = "bad attr index"; FFADODevice::ClockSource active = m_Device.getActiveClockSource(); switch(attridx) { case 0: retval = FFADODevice::ClockSourceTypeToString(active.type); break; case 1: snprintf(tmp, 16, "%u", active.id); retval = tmp; break; case 2: snprintf(tmp, 16, "%u", active.valid); retval = tmp; break; case 3: snprintf(tmp, 16, "%u", active.active); retval = tmp; break; case 4: snprintf(tmp, 16, "%u", active.locked); retval = tmp; break; case 5: snprintf(tmp, 16, "%u", active.slipping); retval = tmp; break; case 6: retval = active.description; } return retval; } std::string ClockSelect::getAttributeName(int attridx) { switch(attridx) { case 0: return "type"; case 1: return "id"; case 2: return "valid"; case 3: return "active"; case 4: return "locked"; case 5: return "slipping"; case 6: return "description"; default: return "bad attr index"; } } bool ClockSelect::canChangeValue() { return m_Device.getStreamingState() == FFADODevice::eSS_Idle; } void ClockSelect::show() { debugOutput( DEBUG_LEVEL_NORMAL, "ClockSelect Element %s, active: %s\n", getName().c_str(), m_Device.getActiveClockSource().description.c_str()); } // --- samplerate selection --- SamplerateSelect::SamplerateSelect(FFADODevice &d) : Enum(&d) , m_Device( d ) { setName("SamplerateSelect"); setLabel("Samplerate Select"); setDescription("Select the device sample rate"); } bool SamplerateSelect::select(int idx) { std::vector freqs = m_Device.getSupportedSamplingFrequencies(); if (idx >= 0 && idx < (int)freqs.size()) { if(!m_Device.setSamplingFrequency(freqs.at(idx))) { debugWarning("Could not select samplerate\n"); return false; } return true; } else { debugWarning("bad index specified\n"); return false; } } int SamplerateSelect::selected() { std::vector freqs = m_Device.getSupportedSamplingFrequencies(); int samplerate = m_Device.getSamplingFrequency(); for (int i = 0; i < (int)freqs.size(); i++) { if (samplerate == freqs.at(i)) { return i; } } debugError("could not find the selected samplerate\n"); return -1; } int SamplerateSelect::count() { return m_Device.getSupportedSamplingFrequencies().size(); } std::string SamplerateSelect::getEnumLabel(int idx) { char tmp[16]; std::string retval = "Error"; std::vector freqs = m_Device.getSupportedSamplingFrequencies(); if (idx >= 0 && idx < (int)freqs.size()) { snprintf(tmp, 16, "%u", freqs.at(idx)); retval = tmp; } else { debugWarning("bad index specified\n"); } return retval; } bool SamplerateSelect::canChangeValue() { return m_Device.getStreamingState() == FFADODevice::eSS_Idle; } void SamplerateSelect::show() { debugOutput( DEBUG_LEVEL_NORMAL, "SamplerateSelect Element %s, current: %d\n", getName().c_str(), m_Device.getSamplingFrequency()); } bool SamplerateSelect::devConfigChanged(int idx) { std::vector freqs = m_Device.getSupportedSamplingFrequencies(); if (idx >= 0 && idx < (int)freqs.size()) { return m_Device.onSamplerateChange(freqs.at(idx)); } else { debugWarning("bad index specified\n"); return false; } } // --- stream status feedback selection --- StreamingStatus::StreamingStatus(FFADODevice &d) : Enum(&d) , m_Device( d ) { setName("StreamingStatus"); setLabel("Streaming Status"); setDescription("Obtain information of the current streaming status of a device"); } bool StreamingStatus::select(int idx) { debugWarning("Cannot change stream status through control interface\n"); return false; } int StreamingStatus::selected() { return m_Device.getStreamingState(); } int StreamingStatus::count() { // NOTE: this should correspond to the number of enums in FFADODevice::eStreamingState return 4; } std::string StreamingStatus::getEnumLabel(int idx) { switch(idx) { case FFADODevice::eSS_Idle: return "Idle"; case FFADODevice::eSS_Sending: return "Sending"; case FFADODevice::eSS_Receiving: return "Receiving"; case FFADODevice::eSS_Both: return "Both"; default: debugError("Invalid enum index specified: %d\n", idx); return "Invalid enum index"; } } bool StreamingStatus::canChangeValue() { return false; } void StreamingStatus::show() { debugOutput( DEBUG_LEVEL_NORMAL, "StreamingStatus Element %s, current: %d\n", getName().c_str(), m_Device.getStreamingState()); } } // namespace Control libffado-2.4.5/src/libcontrol/ClockSelect.h0000644000175000001440000000527414206145246020176 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef CONTROL_CLOCK_SELECT_H #define CONTROL_CLOCK_SELECT_H #include "debugmodule/debugmodule.h" #include "BasicElements.h" #include #include class FFADODevice; namespace Control { /*! @brief Clock selection control element @note this is a workaround and should be done more elegant */ class ClockSelect : public AttributeEnum { public: ClockSelect(FFADODevice &); virtual ~ClockSelect() {}; virtual bool select(int idx); virtual int selected(); virtual int count(); virtual std::string getEnumLabel(int idx); virtual int attributeCount(); ///> get a specific attribute value for the selected enum virtual std::string getAttributeValue(int attridx); ///> get the name of the attribute with a certain index virtual std::string getAttributeName(int attridx); virtual bool canChangeValue(); virtual void show(); protected: FFADODevice &m_Device; }; /*! @brief Samplerate selection control element @note this is a workaround and should be done more elegant */ class SamplerateSelect : public Enum { public: SamplerateSelect(FFADODevice &); virtual ~SamplerateSelect() {}; virtual bool select(int idx); virtual int selected(); virtual int count(); virtual std::string getEnumLabel(int idx); virtual bool canChangeValue(); virtual void show(); virtual bool devConfigChanged(int idx); protected: FFADODevice &m_Device; }; /*! * @brief Stream status control element */ class StreamingStatus : public Enum { public: StreamingStatus(FFADODevice &); virtual ~StreamingStatus() {}; virtual bool select(int idx); virtual int selected(); virtual int count(); virtual std::string getEnumLabel(int idx); virtual bool canChangeValue(); virtual void show(); protected: FFADODevice &m_Device; }; }; // namespace Control #endif // CONTROL_CLOCK_SELECT_H libffado-2.4.5/src/libcontrol/CrossbarRouter.cpp0000644000175000001440000000163714206145246021314 0ustar jwoitheusers/* * Copyright (C) 2005-2009 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "CrossbarRouter.h" namespace Control { // no implementation } // namespace Control libffado-2.4.5/src/libcontrol/CrossbarRouter.h0000644000175000001440000000425714206145246020762 0ustar jwoitheusers/* * Copyright (C) 2005-2009 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef CONTROL_CROSSBAR_ROUTER_H #define CONTROL_CROSSBAR_ROUTER_H #include "debugmodule/debugmodule.h" #include "ffadotypes.h" #include "Element.h" #include #include #include namespace Control { /*! @brief Abstract Base class for Crossbar router elements */ class CrossbarRouter : public Element { public: CrossbarRouter(Element *p) : Element(p) {}; CrossbarRouter(Element *p, std::string n) : Element(p, n) {}; virtual ~CrossbarRouter() {}; virtual void show() = 0; virtual stringlist getSourceNames() = 0; virtual stringlist getDestinationNames() = 0; virtual std::string getSourceForDestination(const std::string& dstname) = 0; virtual stringlist getDestinationsForSource(const std::string& srcname) = 0; virtual bool canConnect(const std::string& srcname, const std::string& dstname) = 0; virtual bool setConnectionState(const std::string& srcname, const std::string& dstname, const bool enable) = 0; virtual bool getConnectionState(const std::string& srcname, const std::string& dstname) = 0; virtual bool clearAllConnections() = 0; // peak metering virtual bool hasPeakMetering() = 0; virtual double getPeakValue(const std::string& dest) = 0; virtual std::map getPeakValues() = 0; protected: }; }; // namespace Control #endif // CONTROL_CROSSBAR_ROUTER_H libffado-2.4.5/src/libcontrol/Element.cpp0000644000175000001440000002021314206145246017715 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "Element.h" #include "libutil/PosixMutex.h" namespace Control { IMPL_DEBUG_MODULE( Element, Element, DEBUG_LEVEL_NORMAL ); // This serves as an ID // incremented on every Element creation // ID's are unique as long as we don't create // more than 2^64 elements. // if we would create 1000 Elements per second // we'd still need >500000 years to wrap. // I guess we're safe. static uint64_t GlobalElementCounter=0; Element::Element(Element *parent) : m_element_lock ( NULL ) , m_parent( parent ) , m_Name ( "NoName" ) , m_Label ( "No Label" ) , m_Description ( "No Description" ) , m_id(GlobalElementCounter++) { // no parent, we are the root of an independent control tree // this means we have to create a lock if(parent == NULL) { m_element_lock = new Util::PosixMutex("CTLEL"); } } Element::Element(Element *parent, std::string n) : m_element_lock ( NULL ) , m_parent( parent ) , m_Name( n ) , m_Label ( "No Label" ) , m_Description ( "No Description" ) , m_id(GlobalElementCounter++) { // no parent, we are the root of an independent control tree // this means we have to create a lock if(parent == NULL) { m_element_lock = new Util::PosixMutex("CTLEL"); } } Element::~Element() { if(m_element_lock) delete m_element_lock; } void Element::lockControl() { if(!m_parent) { debugOutput( DEBUG_LEVEL_VERBOSE, "Locking tree...\n"); } getLock().Lock(); } void Element::unlockControl() { if(!m_parent) { debugOutput( DEBUG_LEVEL_VERBOSE, "Unlocking tree...\n"); } getLock().Unlock(); } bool Element::isControlLocked() { return getLock().isLocked(); } Util::Mutex& Element::getLock() { assert(m_parent != NULL || m_element_lock != NULL); if(m_parent) { return m_parent->getLock(); } else { return *m_element_lock; } } bool Element::canChangeValue() { return true; } void Element::show() { debugOutput( DEBUG_LEVEL_NORMAL, "Element %s\n", getName().c_str()); } void Element::setVerboseLevel(int l) { setDebugLevel(l); if(m_element_lock) m_element_lock->setVerboseLevel(l); debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); } bool Element::addSignalHandler( SignalFunctor* functor ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Adding signal handler (%p)\n", functor); m_signalHandlers.push_back( functor ); return true; } bool Element::remSignalHandler( SignalFunctor* functor ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Removing signal handler (%p)\n", functor); for ( std::vector< SignalFunctor* >::iterator it = m_signalHandlers.begin(); it != m_signalHandlers.end(); ++it ) { if ( *it == functor ) { debugOutput(DEBUG_LEVEL_VERBOSE, " found\n"); m_signalHandlers.erase( it ); return true; } } debugOutput(DEBUG_LEVEL_VERBOSE, " not found\n"); return false; } bool Element::emitSignal(int id, int value) { for ( std::vector< SignalFunctor* >::iterator it = m_signalHandlers.begin(); it != m_signalHandlers.end(); ++it ) { SignalFunctor *f = *it; if(f && f->m_id == id) (*f)(value); } return true; } bool Element::emitSignal(int id) { for ( std::vector< SignalFunctor* >::iterator it = m_signalHandlers.begin(); it != m_signalHandlers.end(); ++it ) { SignalFunctor *f = *it; if(f && f->m_id == id) (*f)(); } return true; } //// --- Container --- //// Container::Container(Element *p) : Element(p) { } Container::Container(Element *p, std::string n) : Element(p, n) { } Container::~Container() { } unsigned int Container::countElements() { lockControl(); unsigned int s = m_Children.size(); unlockControl(); return s; } const ElementVector & Container::getElementVector() { if(!getLock().isLocked()) { debugWarning("called on unlocked tree!\n"); } return m_Children; } /** * Finds an element by name * Note that you have to run this with the lock held, * otherwise there is no guarantee that the element will * not be deleted from the tree by an async event (e.g. busreset) * * @param name element name * @return element or null if not found */ Element * Container::getElementByName(std::string name) { if(!getLock().isLocked()) debugWarning("Getting a Config::Element without locking the control tree, dangerous!\n"); for ( ElementVectorIterator it = m_Children.begin(); it != m_Children.end(); ++it ) { if((*it)->getName() == name) { debugOutput( DEBUG_LEVEL_VERBOSE, "Found Element %s (%s) \n", (*it)->getName().c_str(), (*it)->getDescription().c_str()); return *it; } } return NULL; } bool Container::addElement(Element *e) { Util::MutexLockHelper lock(getLock()); if (e==NULL) { debugWarning("Cannot add NULL element\n"); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "Adding Element %s to %s\n", e->getName().c_str(), getName().c_str()); // don't allow duplicates, only makes life hard for ( ElementVectorIterator it = m_Children.begin(); it != m_Children.end(); ++it ) { if(*it == e) { debugOutput( DEBUG_LEVEL_VERBOSE, "Not adding Element %s, already present\n", e->getName().c_str()); return false; } } m_Children.push_back(e); // unlock before emitting the signal lock.earlyUnlock(); emitSignal(eS_Updated, m_Children.size()); return true; } bool Container::deleteElementNoLock(Element *e) { if(e == NULL) return false; debugOutput( DEBUG_LEVEL_VERBOSE, "Deleting Element %s from %s\n", e->getName().c_str(), getName().c_str()); for ( ElementVectorIterator it = m_Children.begin(); it != m_Children.end(); ++it ) { if(*it == e) { m_Children.erase(it); return true; } } debugOutput( DEBUG_LEVEL_VERBOSE, "Element %s not found \n",e->getName().c_str()); return false; //not found } bool Container::deleteElement(Element *e) { bool retval; Util::MutexLockHelper lock(getLock()); retval = deleteElementNoLock(e); if(retval) { // unlock before emitting the signal lock.earlyUnlock(); emitSignal(eS_Updated, m_Children.size()); } return retval; } bool Container::clearElements(bool delete_pointers) { Util::MutexLockHelper lock(getLock()); while(m_Children.size()) { Element *e=m_Children[0]; deleteElementNoLock(e); if (delete_pointers) delete e; } // unlock before emitting the signal lock.earlyUnlock(); emitSignal(eS_Updated, m_Children.size()); return true; } void Container::show() { Util::MutexLockHelper lock(getLock()); debugOutput( DEBUG_LEVEL_NORMAL, "Container %s (%zd Elements)\n", getName().c_str(), m_Children.size()); for ( ElementVectorIterator it = m_Children.begin(); it != m_Children.end(); ++it ) { (*it)->show(); } } void Container::setVerboseLevel(int l) { setDebugLevel(l); for ( ElementVectorIterator it = m_Children.begin(); it != m_Children.end(); ++it ) { (*it)->setVerboseLevel(l); } debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); } } // namespace Control libffado-2.4.5/src/libcontrol/Element.h0000644000175000001440000001101514206145246017362 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef CONTROL_ELEMENT_H #define CONTROL_ELEMENT_H #include "debugmodule/debugmodule.h" #include #include #include #include "libutil/Mutex.h" #include "libutil/Functors.h" namespace Control { class Element; /*! @brief Base class for control signal functors This class should be subclassed to implement ffado control signal handlers. */ class SignalFunctor { friend class Element; public: SignalFunctor(int signal_id) : m_id(signal_id) {}; virtual ~SignalFunctor() {} virtual void operator() () = 0; virtual void operator() (int) = 0; protected: int m_id; }; /*! @brief Base class for control elements This class should be subclassed to implement ffado control elements. */ class Element { public: Element(Element *); Element(Element *, std::string n); virtual ~Element(); virtual std::string getName() {return m_Name;}; virtual bool setName( std::string n ) { m_Name=n; return true;}; virtual std::string getLabel() {return m_Label;}; virtual bool setLabel( std::string n ) { m_Label=n; return true;}; virtual std::string getDescription() {return m_Description;}; virtual bool setDescription( std::string n ) { m_Description=n; return true;}; uint64_t getId() {return m_id;}; // can the value of this element change? virtual bool canChangeValue(); // these allow to prevent external access to the control elements // e.g. when the config tree is rebuilt virtual void lockControl(); virtual void unlockControl(); virtual bool isControlLocked(); /** * Update signal handler */ bool addSignalHandler( SignalFunctor* functor ); bool remSignalHandler( SignalFunctor* functor ); virtual void show(); /** * set verbosity level */ virtual void setVerboseLevel(int l); virtual int getVerboseLevel() {return getDebugLevel();}; protected: bool emitSignal(int id, int value); bool emitSignal(int id); Util::Mutex& getLock(); private: Util::Mutex* m_element_lock; Element* m_parent; std::string m_Name; std::string m_Label; std::string m_Description; uint64_t m_id; std::vector< SignalFunctor* > m_signalHandlers; protected: DECLARE_DEBUG_MODULE; }; typedef std::vector ElementVector; typedef std::vector::iterator ElementVectorIterator; typedef std::vector::const_iterator ConstElementVectorIterator; /*! @brief Base class for control containers This class should be subclassed to implement ffado control container elements. Containers are classes that can hold a set of control elements. They themselves are control elements such that hierarchies can be defined using them. Special control containers that act on all of their children can also be implemented. */ class Container : public Element { public: Container(Element *); Container(Element *, std::string n); virtual ~Container(); virtual bool addElement(Element *e); virtual bool deleteElement(Element *e); virtual bool clearElements() {return clearElements(false);}; virtual bool clearElements(bool delete_pointers); unsigned int countElements(); /** * Returns the element vector. be sure to lock the tree while using * the return value. * @return */ const ElementVector & getElementVector(); Element * getElementByName(std::string name); virtual void show(); virtual void setVerboseLevel(int l); enum eSignals { eS_Updated, }; private: bool deleteElementNoLock(Element *e); protected: ElementVector m_Children; }; }; // namespace Control #endif // CONTROL_ELEMENT_H libffado-2.4.5/src/libcontrol/MatrixMixer.cpp0000644000175000001440000000325014206145246020577 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "MatrixMixer.h" namespace Control { std::string MatrixMixer::getRowName(const int) { return ""; } std::string MatrixMixer::getColName(const int) { return ""; } bool MatrixMixer::setRowName(const int, const std::string&) { return false; } bool MatrixMixer::setColName(const int, const std::string&) { return false; } std::vector MatrixMixer::availableConnectionsForRow(const int) { return std::vector(); } std::vector MatrixMixer::availableConnectionsForCol(const int) { return std::vector(); } bool MatrixMixer::connectRowTo(const int, const std::string&) { return false; } bool MatrixMixer::connectColTo(const int, const std::string&) { return false; } } // namespace Control libffado-2.4.5/src/libcontrol/MatrixMixer.h0000644000175000001440000000563514206145246020255 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef CONTROL_MATRIX_MIXER_H #define CONTROL_MATRIX_MIXER_H #include "debugmodule/debugmodule.h" #include "Element.h" #include #include namespace Control { /*! @brief Abstract Base class for Matrix Mixer elements */ class MatrixMixer : public Element { public: MatrixMixer(Element *p) : Element(p) {}; MatrixMixer(Element *p, std::string n) : Element(p, n) {}; virtual ~MatrixMixer() {}; virtual void show() = 0; /*! @{ @brief general dimensions */ virtual int getRowCount() = 0; virtual int getColCount() = 0; // @} /*! @{ @brief per-coefficient access */ virtual int canWrite(const int, const int) = 0; virtual double setValue(const int, const int, const double) = 0; virtual double getValue(const int, const int) = 0; // @} /*! @{ @brief functions to access the entire coefficient map at once */ virtual bool getCoefficientMap(int &) = 0; virtual bool storeCoefficientMap(int &) = 0; // @} /*! @{ @brief names for the channels If named channels are not supported, just implement hasNames() to return false The default implementations do nothing */ virtual bool hasNames() const = 0; virtual std::string getRowName(const int); virtual std::string getColName(const int); virtual bool setRowName(const int row, const std::string& name); virtual bool setColName(const int col, const std::string& name); // @} /*! @{ @brief connections for channels If connections are not supported, implement canConnect() to return false. The default implementations do nothing. */ virtual bool canConnect() const = 0; virtual std::vector availableConnectionsForRow(const int); virtual std::vector availableConnectionsForCol(const int); virtual bool connectRowTo(const int row, const std::string& target); virtual bool connectColTo(const int col, const std::string& target); // @} protected: }; }; // namespace Control #endif // CONTROL_MATRIX_MIXER_H libffado-2.4.5/src/libcontrol/Nickname.cpp0000644000175000001440000000331214206145246020052 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "Nickname.h" #include "ffadodevice.h" namespace Control { //// --- Nickname --- //// Nickname::Nickname(FFADODevice &d) : Text(&d) , m_Device( d ) { setName("Nickname"); setLabel("Nickname"); setDescription("Get/Set device nickname"); } bool Nickname::setValue(std::string v) { debugOutput( DEBUG_LEVEL_VERBOSE, "%s setValue(%s)\n", getName().c_str(), v.c_str()); return m_Device.setNickname(v); } std::string Nickname::getValue() { debugOutput( DEBUG_LEVEL_VERBOSE, "%s getValue()=%s\n", getName().c_str(), m_Device.getNickname().c_str()); return m_Device.getNickname(); } bool Nickname::canChangeValue() { return m_Device.canChangeNickname(); } void Nickname::show() { debugOutput( DEBUG_LEVEL_NORMAL, "Nickname Element %s, %s\n", getName().c_str(), m_Device.getNickname().c_str()); } } // namespace Control libffado-2.4.5/src/libcontrol/Nickname.h0000644000175000001440000000265014206145246017523 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef CONTROL_NICKNAME_H #define CONTROL_NICKNAME_H #include "debugmodule/debugmodule.h" #include "BasicElements.h" #include class FFADODevice; namespace Control { /*! @brief Nickname control element @note this is a workaround and should be done more elegant */ class Nickname : public Text { public: Nickname(FFADODevice &); virtual ~Nickname() {}; virtual bool setValue(std::string v); virtual std::string getValue(); virtual bool canChangeValue(); virtual void show(); protected: FFADODevice &m_Device; }; }; // namespace Control #endif // CONTROL_NICKNAME_H libffado-2.4.5/src/libieee1394/0000755000175000001440000000000014206145612015367 5ustar jwoitheuserslibffado-2.4.5/src/libieee1394/ARMHandler.cpp0000644000175000001440000001257314206145246020023 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include "ieee1394service.h" /** * @param parent the 1394service that created this handler * @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. * */ Ieee1394Service::ARMHandler::ARMHandler(Ieee1394Service &parent, nodeaddr_t start, size_t length, unsigned int access_rights, unsigned int notification_options, unsigned int client_transactions) : m_parent(parent) , 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_debugModule(parent.m_debugModule) { m_buffer = (byte_t*)calloc(length, sizeof(byte_t)); memset(&m_response, 0, sizeof(m_response)); } Ieee1394Service::ARMHandler::~ARMHandler() { if(m_buffer) delete m_buffer; } bool Ieee1394Service::ARMHandler::handleRead(struct raw1394_arm_request *req) { debugOutput(DEBUG_LEVEL_VERBOSE, "Read\n"); printRequest(req); return true; } bool Ieee1394Service::ARMHandler::handleWrite(struct raw1394_arm_request *req) { debugOutput(DEBUG_LEVEL_VERBOSE, "Write\n"); printRequest(req); return true; } bool Ieee1394Service::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 Ieee1394Service::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: %u\n", arm_req->generation); debugOutput(DEBUG_LEVEL_VERBOSE, " buffer length: %u\n", arm_req->buffer_length); printBufferBytes(DEBUG_LEVEL_VERBOSE, arm_req->buffer_length, arm_req->buffer); } void Ieee1394Service::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"); } libffado-2.4.5/src/libieee1394/CycleTimerHelper.cpp0000644000175000001440000007532214206145246021307 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "CycleTimerHelper.h" #include "ieee1394service.h" #include "libutil/PosixThread.h" #include "libutil/PosixMutex.h" #include "libutil/Atomic.h" #include "libutil/Watchdog.h" #define DLL_PI (3.141592653589793238) #define DLL_2PI (2 * DLL_PI) #define DLL_SQRT2 (1.414213562373095049) IMPL_DEBUG_MODULE( CycleTimerHelper, CycleTimerHelper, DEBUG_LEVEL_NORMAL ); CycleTimerHelper::CycleTimerHelper(Ieee1394Service &parent, unsigned int update_period_us) : m_Parent ( parent ) , m_ticks_per_update ( ((uint64_t)TICKS_PER_SECOND) * ((uint64_t)update_period_us) / 1000000ULL ) , m_usecs_per_update ( update_period_us ) , m_avg_wakeup_delay ( 0.0 ) , m_dll_e2 ( 0.0 ) , m_current_time_usecs ( 0 ) , m_next_time_usecs ( 0 ) , m_current_time_ticks ( 0 ) , m_next_time_ticks ( 0 ) , m_first_run ( true ) , m_sleep_until ( 0 ) , m_cycle_timer_prev ( 0 ) , m_cycle_timer_ticks_prev ( 0 ) , m_current_shadow_idx ( 0 ) , m_Thread ( NULL ) , m_realtime ( false ) , m_priority ( 0 ) , m_update_lock( new Util::PosixMutex("CTRUPD") ) , m_busreset_functor ( NULL) , m_unhandled_busreset ( false ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this); double bw_rel = IEEE1394SERVICE_CYCLETIMER_DLL_BANDWIDTH_HZ*((double)update_period_us)/1e6; m_dll_coeff_b = bw_rel * (DLL_SQRT2 * DLL_2PI); m_dll_coeff_c = bw_rel * bw_rel * DLL_2PI * DLL_2PI; } CycleTimerHelper::CycleTimerHelper(Ieee1394Service &parent, unsigned int update_period_us, bool rt, int prio) : m_Parent ( parent ) , m_ticks_per_update ( ((uint64_t)TICKS_PER_SECOND) * ((uint64_t)update_period_us) / 1000000ULL ) , m_usecs_per_update ( update_period_us ) , m_avg_wakeup_delay ( 0.0 ) , m_dll_e2 ( 0.0 ) , m_current_time_usecs ( 0 ) , m_next_time_usecs ( 0 ) , m_current_time_ticks ( 0 ) , m_next_time_ticks ( 0 ) , m_first_run ( true ) , m_sleep_until ( 0 ) , m_cycle_timer_prev ( 0 ) , m_cycle_timer_ticks_prev ( 0 ) , m_current_shadow_idx ( 0 ) , m_Thread ( NULL ) , m_realtime ( rt ) , m_priority ( prio ) , m_update_lock( new Util::PosixMutex("CTRUPD") ) , m_busreset_functor ( NULL) , m_unhandled_busreset ( false ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this); double bw_rel = IEEE1394SERVICE_CYCLETIMER_DLL_BANDWIDTH_HZ*((double)update_period_us)/1e6; m_dll_coeff_b = bw_rel * (DLL_SQRT2 * DLL_2PI); m_dll_coeff_c = bw_rel * bw_rel * DLL_2PI * DLL_2PI; } CycleTimerHelper::~CycleTimerHelper() { if (m_Thread) { m_Thread->Stop(); delete m_Thread; } // unregister the bus reset handler if(m_busreset_functor) { m_Parent.remBusResetHandler( m_busreset_functor ); delete m_busreset_functor; } delete m_update_lock; } bool CycleTimerHelper::Start() { debugOutput( DEBUG_LEVEL_VERBOSE, "Start %p...\n", this); if(!initValues()) { debugFatal("(%p) Could not init values\n", this); return false; } m_Thread = new Util::PosixThread(this, "CTRHLP", m_realtime, m_priority, PTHREAD_CANCEL_DEFERRED); if(!m_Thread) { debugFatal("No thread\n"); return false; } // register the thread with the RT watchdog Util::Watchdog *watchdog = m_Parent.getWatchdog(); if(watchdog) { if(!watchdog->registerThread(m_Thread)) { debugWarning("could not register update thread with watchdog\n"); } } else { debugWarning("could not find valid watchdog\n"); } if (m_Thread->Start() != 0) { debugFatal("Could not start update thread\n"); return false; } return true; } bool CycleTimerHelper::initValues() { debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) Init values...\n", this ); Util::MutexLockHelper lock(*m_update_lock); // initialize the 'prev ctr' values uint64_t local_time; int maxtries2 = 10; do { debugOutput( DEBUG_LEVEL_VERBOSE, "Read CTR...\n" ); if(!m_Parent.readCycleTimerReg(&m_cycle_timer_prev, &local_time)) { debugError("Could not read cycle timer register\n"); return false; } if (m_cycle_timer_prev == 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "Bogus CTR: %08X on try %02d\n", m_cycle_timer_prev, maxtries2); } debugOutput( DEBUG_LEVEL_VERBOSE, " read : CTR: %11u, local: %17" PRIu64 "\n", m_cycle_timer_prev, local_time); debugOutput(DEBUG_LEVEL_VERBOSE, " ctr : 0x%08X %11" PRIu64 " (%03us %04ucy %04uticks)\n", (uint32_t)m_cycle_timer_prev, (uint64_t)CYCLE_TIMER_TO_TICKS(m_cycle_timer_prev), (unsigned int)CYCLE_TIMER_GET_SECS( m_cycle_timer_prev ), (unsigned int)CYCLE_TIMER_GET_CYCLES( m_cycle_timer_prev ), (unsigned int)CYCLE_TIMER_GET_OFFSET( m_cycle_timer_prev ) ); } while (m_cycle_timer_prev == 0 && maxtries2--); m_cycle_timer_ticks_prev = CYCLE_TIMER_TO_TICKS(m_cycle_timer_prev); #if IEEE1394SERVICE_USE_CYCLETIMER_DLL debugOutput( DEBUG_LEVEL_VERBOSE, "requesting DLL re-init...\n" ); Util::SystemTimeSource::SleepUsecRelative(1000); // some time to settle if(!initDLL()) { debugError("(%p) Could not init DLL\n", this); return false; } // make the DLL re-init itself as if it were started up m_first_run = true; #endif debugOutput( DEBUG_LEVEL_VERBOSE, "ready...\n" ); return true; } bool CycleTimerHelper::Init() { debugOutput( DEBUG_LEVEL_VERBOSE, "Initialize %p...\n", this); // register a bus reset handler m_busreset_functor = new Util::MemberFunctor0< CycleTimerHelper*, void (CycleTimerHelper::*)() > ( this, &CycleTimerHelper::busresetHandler, false ); if ( !m_busreset_functor ) { debugFatal( "(%p) Could not create busreset handler\n", this ); return false; } m_Parent.addBusResetHandler( m_busreset_functor ); #ifdef DEBUG m_last_loop_entry = 0; m_successive_short_loops = 0; #endif return true; } void CycleTimerHelper::busresetHandler() { debugOutput( DEBUG_LEVEL_VERBOSE, "Bus reset...\n" ); m_unhandled_busreset = true; // whenever a bus reset occurs, the root node can change, // and the CTR timer can be reset. We should hence reinit // the DLL if(!initValues()) { debugError("(%p) Could not re-init values\n", this); } m_unhandled_busreset = false; } bool CycleTimerHelper::setThreadParameters(bool rt, int priority) { debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) switch to: (rt=%d, prio=%d)...\n", this, rt, priority); if (priority > THREAD_MAX_RTPRIO) priority = THREAD_MAX_RTPRIO; // cap the priority m_realtime = rt; m_priority = priority; #if IEEE1394SERVICE_USE_CYCLETIMER_DLL if (m_Thread) { if (m_realtime) { m_Thread->AcquireRealTime(m_priority); } else { m_Thread->DropRealTime(); } } #endif return true; } #if IEEE1394SERVICE_USE_CYCLETIMER_DLL float CycleTimerHelper::getRate() { float rate = (float)(diffTicks((uint64_t)m_next_time_ticks, (uint64_t)m_current_time_ticks)); rate /= (float)(m_next_time_usecs - m_current_time_usecs); return rate; } float CycleTimerHelper::getNominalRate() { float rate = ((double)TICKS_PER_SECOND) / 1000000.0; return rate; } /* * call with lock held */ bool CycleTimerHelper::initDLL() { uint32_t cycle_timer; uint64_t local_time; double bw_rel = m_dll_coeff_b / (DLL_SQRT2 * DLL_2PI); double bw_abs = bw_rel / (m_usecs_per_update / 1e6); if (bw_rel > 0.5) { double bw_max = 0.5 / (m_usecs_per_update / 1e6); debugWarning("Specified DLL bandwidth too high (%f > %f), reducing to max." " Increase the DLL update rate to increase the max DLL bandwidth\n", bw_abs, bw_max); bw_rel = 0.49; bw_abs = bw_rel / (m_usecs_per_update / 1e6); m_dll_coeff_b = bw_rel * (DLL_SQRT2 * DLL_2PI); m_dll_coeff_c = bw_rel * bw_rel * DLL_2PI * DLL_2PI; } if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) { debugError("Could not read cycle timer register\n"); return false; } #if DEBUG_EXTREME_ENABLE uint64_t cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer); #endif debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " read : CTR: %11u, local: %17" PRIu64 "\n", cycle_timer, local_time); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " ctr : 0x%08X %11" PRIu64 " (%03us %04ucy %04uticks)\n", (uint32_t)cycle_timer, (uint64_t)cycle_timer_ticks, (unsigned int)TICKS_TO_SECS( (uint64_t)cycle_timer_ticks ), (unsigned int)TICKS_TO_CYCLES( (uint64_t)cycle_timer_ticks ), (unsigned int)TICKS_TO_OFFSET( (uint64_t)cycle_timer_ticks ) ); m_sleep_until = local_time + m_usecs_per_update; m_dll_e2 = m_ticks_per_update; m_current_time_usecs = local_time; m_next_time_usecs = m_current_time_usecs + m_usecs_per_update; m_current_time_ticks = CYCLE_TIMER_TO_TICKS( cycle_timer ); m_next_time_ticks = addTicks( (uint64_t)m_current_time_ticks, (uint64_t)m_dll_e2); debugOutput(DEBUG_LEVEL_VERBOSE, " (%p) First run\n", this); debugOutput(DEBUG_LEVEL_VERBOSE, " DLL bandwidth: %f Hz (rel: %f)\n", bw_abs, bw_rel); debugOutput(DEBUG_LEVEL_VERBOSE, " usecs/update: %u, ticks/update: %u, m_dll_e2: %f\n", m_usecs_per_update, m_ticks_per_update, m_dll_e2); debugOutput(DEBUG_LEVEL_VERBOSE, " usecs current: %f, next: %f\n", m_current_time_usecs, m_next_time_usecs); debugOutput(DEBUG_LEVEL_VERBOSE, " ticks current: %f, next: %f\n", m_current_time_ticks, m_next_time_ticks); return true; } bool CycleTimerHelper::Execute() { debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, "Execute %p...\n", this); #ifdef DEBUG uint64_t now = m_Parent.getCurrentTimeAsUsecs(); int diff = now - m_last_loop_entry; if(diff < 100) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p) short loop detected (%d usec), cnt: %d\n", this, diff, m_successive_short_loops); m_successive_short_loops++; if(m_successive_short_loops > 100) { debugError("Shutting down runaway thread\n"); return false; } } else { // reset the counter m_successive_short_loops = 0; } m_last_loop_entry = now; #endif if (!m_first_run) { // wait for the next update period //#if DEBUG_EXTREME_ENABLE #ifdef DEBUG ffado_microsecs_t now = Util::SystemTimeSource::getCurrentTimeAsUsecs(); int sleep_time = m_sleep_until - now; debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, "(%p) Sleep until %" PRId64 "/%f (now: %" PRId64 ", diff=%d) ...\n", this, m_sleep_until, m_next_time_usecs, now, sleep_time); #endif Util::SystemTimeSource::SleepUsecAbsolute(m_sleep_until); debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, " (%p) back...\n", this); } else { // Since getCycleTimerTicks() is called below, // m_shadow_vars[m_current_shadow_idx] must contain valid data. On // the first run through, however, it won't because the contents of // m_shadow_vars[] are only set later on in this function. Thus // set up some vaguely realistic values to prevent unnecessary // delays when reading the cycle timer for the first time. struct compute_vars new_vars; new_vars.ticks = (uint64_t)(m_current_time_ticks); new_vars.usecs = (uint64_t)m_current_time_usecs; new_vars.rate = getRate(); m_shadow_vars[0] = new_vars; } uint32_t cycle_timer; uint64_t local_time; int64_t usecs_late; int ntries=10; uint64_t cycle_timer_ticks; int64_t err_ticks; bool not_good; // if the difference between the predicted value at readout time and the // actual value seems to be too large, retry reading the cycle timer // some host controllers return bogus values on some reads // (looks like a non-atomic update of the register) do { debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, "(%p) reading cycle timer register...\n", this); if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) { debugError("Could not read cycle timer register\n"); return false; } usecs_late = local_time - m_sleep_until; cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer); // calculate the CTR_TICKS we expect to read at "local_time" // then calculate the difference with what we actually read, // taking wraparound into account. If these deviate too much // from eachother then read the register again (bogus read). int64_t expected_ticks = getCycleTimerTicks(local_time); err_ticks = diffTicks(cycle_timer_ticks, expected_ticks); // check for unrealistic CTR reads (NEC controller does that sometimes) not_good = (-err_ticks > 1*TICKS_PER_CYCLE || err_ticks > 1*TICKS_PER_CYCLE); if(not_good) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) have to retry CTR read, diff unrealistic: diff: %" PRId64 ", max: +/- %u (try: %d) %" PRId64 "\n", this, err_ticks, 1*TICKS_PER_CYCLE, ntries, expected_ticks); // sleep half a cycle to make sure the hardware moved on Util::SystemTimeSource::SleepUsecRelative(USECS_PER_CYCLE / 2); } } while(not_good && --ntries && !m_first_run && !m_unhandled_busreset); // grab the lock after sleeping, otherwise we can't be interrupted by // the busreset thread (lower prio) // also grab it after reading the CTR register such that the jitter between // wakeup and read is as small as possible Util::MutexLockHelper lock(*m_update_lock); // the difference between the measured and the expected time int64_t diff_ticks = diffTicks(cycle_timer_ticks, (int64_t)m_next_time_ticks); // // simulate a random scheduling delay between (0-10ms) // ffado_microsecs_t tmp = Util::SystemTimeSource::SleepUsecRandom(10000); // debugOutput( DEBUG_LEVEL_VERBOSE, " (%p) random sleep of %u usecs...\n", this, tmp); if(m_unhandled_busreset) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) Skipping DLL update due to unhandled busreset\n", this); m_sleep_until += m_usecs_per_update; // keep the thread running return true; } debugOutputExtreme( DEBUG_LEVEL_ULTRA_VERBOSE, " read : CTR: %11u, local: %17" PRIu64 "\n", cycle_timer, local_time); debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, " ctr : 0x%08X %11" PRIu64 " (%03us %04ucy %04uticks)\n", (uint32_t)cycle_timer, (uint64_t)cycle_timer_ticks, (unsigned int)TICKS_TO_SECS( (uint64_t)cycle_timer_ticks ), (unsigned int)TICKS_TO_CYCLES( (uint64_t)cycle_timer_ticks ), (unsigned int)TICKS_TO_OFFSET( (uint64_t)cycle_timer_ticks ) ); if (m_first_run) { if(!initDLL()) { debugError("(%p) Could not init DLL\n", this); return false; } m_first_run = false; } else if (diff_ticks > m_ticks_per_update * 20) { debugOutput(DEBUG_LEVEL_VERBOSE, "re-init dll due to too large tick diff: %" PRId64 " >> %f\n", diff_ticks, (float)(m_ticks_per_update * 20)); if(!initDLL()) { debugError("(%p) Could not init DLL\n", this); return false; } } else { // calculate next sleep time m_sleep_until += m_usecs_per_update; // correct for the latency between the wakeup and the actual CTR // read. The only time we can trust is the time returned by the // CTR read kernel call, since that (should be) atomically read // together with the ctr register itself. // if we are usecs_late usecs late // the cycle timer has ticked approx ticks_late ticks too much // if we are woken up early (which shouldn't happen according to POSIX) // the cycle timer has ticked approx -ticks_late too little int64_t ticks_late = (usecs_late * TICKS_PER_SECOND) / 1000000LL; // the corrected difference between predicted and actual ctr // i.e. DLL error signal int64_t diff_ticks_corr; if (ticks_late >= 0) { diff_ticks_corr = diff_ticks - ticks_late; debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, "diff_ticks_corr=%" PRId64 ", diff_ticks = %" PRId64 ", ticks_late = %" PRId64 "\n", diff_ticks_corr, diff_ticks, ticks_late); } else { debugError("Early wakeup, should not happen!\n"); // recover diff_ticks_corr = diff_ticks + ticks_late; } #ifdef DEBUG // makes no sense if not running realtime if(m_realtime && usecs_late > 1000) { debugOutput(DEBUG_LEVEL_VERBOSE, "Rather late wakeup: %" PRId64 " usecs\n", usecs_late); } #endif // update the x-axis values m_current_time_ticks = m_next_time_ticks; // decide what coefficients to use // it should be ok to not do this in tick space // since diff_ticks_corr should not be near wrapping // (otherwise we are out of range. we need a few calls // w/o wrapping for this to work. That should not be // an issue as long as the update interval is smaller // than the wrapping interval.) // and coeff_b < 1, hence tmp is not near wrapping double diff_ticks_corr_d = (double)diff_ticks_corr; double step_ticks = (m_dll_coeff_b * diff_ticks_corr_d); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "diff_ticks_corr=%f, step_ticks=%f\n", diff_ticks_corr_d, step_ticks); // the same goes for m_dll_e2, which should be approx equal // to the ticks/usec rate (= 24.576) hence also not near // wrapping step_ticks += m_dll_e2; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "add %f ticks to step_ticks => step_ticks=%f\n", m_dll_e2, step_ticks); // it can't be that we have to update to a value in the past if(step_ticks < 0) { debugError("negative step: %f! (correcting to nominal)\n", step_ticks); // recover to an estimated value step_ticks = (double)m_ticks_per_update; } if(step_ticks > TICKS_PER_SECOND) { debugWarning("rather large step: %f ticks (> 1sec)\n", step_ticks); } // now add the step ticks with wrapping. m_next_time_ticks = (double)(addTicks((uint64_t)m_current_time_ticks, (uint64_t)step_ticks)); // update the DLL state m_dll_e2 += m_dll_coeff_c * diff_ticks_corr_d; // update the y-axis values m_current_time_usecs = m_next_time_usecs; m_next_time_usecs += m_usecs_per_update; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " usecs: current: %f next: %f usecs_late=%" PRId64 " ticks_late=%" PRId64 "\n", m_current_time_usecs, m_next_time_usecs, usecs_late, ticks_late); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " ticks: current: %f next: %f diff=%" PRId64 "\n", m_current_time_ticks, m_next_time_ticks, diff_ticks); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " ticks: current: %011" PRIu64 " (%03us %04ucy %04uticks)\n", (uint64_t)m_current_time_ticks, (unsigned int)TICKS_TO_SECS( (uint64_t)m_current_time_ticks ), (unsigned int)TICKS_TO_CYCLES( (uint64_t)m_current_time_ticks ), (unsigned int)TICKS_TO_OFFSET( (uint64_t)m_current_time_ticks ) ); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " ticks: next : %011" PRIu64 " (%03us %04ucy %04uticks)\n", (uint64_t)m_next_time_ticks, (unsigned int)TICKS_TO_SECS( (uint64_t)m_next_time_ticks ), (unsigned int)TICKS_TO_CYCLES( (uint64_t)m_next_time_ticks ), (unsigned int)TICKS_TO_OFFSET( (uint64_t)m_next_time_ticks ) ); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " state: local: %11" PRIu64 ", dll_e2: %f, rate: %f\n", local_time, m_dll_e2, getRate()); } // prepare the new compute vars struct compute_vars new_vars; new_vars.ticks = (uint64_t)(m_current_time_ticks); new_vars.usecs = (uint64_t)m_current_time_usecs; new_vars.rate = getRate(); // get the next index unsigned int next_idx = (m_current_shadow_idx + 1) % CTRHELPER_NB_SHADOW_VARS; // update the next index position m_shadow_vars[next_idx] = new_vars; // then we can update the current index m_current_shadow_idx = next_idx; #ifdef DEBUG // do some verification // we re-read a valid ctr timestamp // then we use the attached system time to calculate // the DLL generated timestamp and we check what the // difference is if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) { debugError("Could not read cycle timer register (verify)\n"); return true; // true since this is a check only } cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer); // only check when successful int64_t time_diff = local_time - new_vars.usecs; double y_step_in_ticks = ((double)time_diff) * new_vars.rate; int64_t y_step_in_ticks_int = (int64_t)y_step_in_ticks; uint64_t offset_in_ticks_int = new_vars.ticks; uint32_t dll_time; if (y_step_in_ticks_int > 0) { dll_time = addTicks(offset_in_ticks_int, y_step_in_ticks_int); } else { dll_time = substractTicks(offset_in_ticks_int, -y_step_in_ticks_int); } int32_t ctr_diff = cycle_timer_ticks-dll_time; debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%p) CTR DIFF: HW %010" PRIu64 " - DLL %010u = %010d (%s)\n", this, cycle_timer_ticks, dll_time, ctr_diff, (ctr_diff>0?"lag":"lead")); #endif return true; } uint32_t CycleTimerHelper::getCycleTimerTicks() { uint64_t now = m_Parent.getCurrentTimeAsUsecs(); return getCycleTimerTicks(now); } uint32_t CycleTimerHelper::getCycleTimerTicks(uint64_t now) { uint32_t retval; struct compute_vars *my_vars; // get pointer and copy the contents // no locking should be needed since we have more than one // of these vars available, and our use will always be finished before // m_current_shadow_idx changes since this thread's priority should // be higher than the one of the writer thread. Even if not, we only have to ensure // that the used dataset is consistent. We can use an older dataset if it's consistent // since it will also provide a fairly decent extrapolation. my_vars = m_shadow_vars + m_current_shadow_idx; int64_t time_diff = now - my_vars->usecs; double y_step_in_ticks = ((double)time_diff) * my_vars->rate; int64_t y_step_in_ticks_int = (int64_t)y_step_in_ticks; uint64_t offset_in_ticks_int = my_vars->ticks; if (y_step_in_ticks_int > 0) { retval = addTicks(offset_in_ticks_int, y_step_in_ticks_int); /* debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "y_step_in_ticks_int > 0: %d, time_diff: %f, rate: %f, retval: %u\n", y_step_in_ticks_int, time_diff, my_vars.rate, retval);*/ } else { retval = substractTicks(offset_in_ticks_int, -y_step_in_ticks_int); // this can happen if the update thread was woken up earlier than it should have been /* debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "y_step_in_ticks_int <= 0: %d, time_diff: %f, rate: %f, retval: %u\n", y_step_in_ticks_int, time_diff, my_vars.rate, retval);*/ } return retval; } uint32_t CycleTimerHelper::getCycleTimer() { uint64_t now = m_Parent.getCurrentTimeAsUsecs(); return getCycleTimer(now); } uint32_t CycleTimerHelper::getCycleTimer(uint64_t now) { uint32_t ticks = getCycleTimerTicks(now); uint32_t result = TICKS_TO_CYCLE_TIMER(ticks); #ifdef DEBUG if(CYCLE_TIMER_TO_TICKS(result) != ticks) { debugWarning("Bad ctr conversion"); } #endif return result; } uint64_t CycleTimerHelper::getSystemTimeForCycleTimerTicks(uint32_t ticks) { uint64_t retval; struct compute_vars *my_vars; // get pointer and copy the contents // no locking should be needed since we have more than one // of these vars available, and our use will always be finished before // m_current_shadow_idx changes since this thread's priority should // be higher than the one of the writer thread. Even if not, we only have to ensure // that the used dataset is consistent. We can use an older dataset if it's consistent // since it will also provide a fairly decent extrapolation. my_vars = m_shadow_vars + m_current_shadow_idx; // the number of ticks the request is ahead of the current CTR position int64_t ticks_diff = diffTicks(ticks, my_vars->ticks); // to how much time does this correspond? double x_step_in_usec = ((double)ticks_diff) / my_vars->rate; int64_t x_step_in_usec_int = (int64_t)x_step_in_usec; retval = my_vars->usecs + x_step_in_usec_int; return retval; } uint64_t CycleTimerHelper::getSystemTimeForCycleTimer(uint32_t ctr) { uint32_t ticks = CYCLE_TIMER_TO_TICKS(ctr); return getSystemTimeForCycleTimerTicks(ticks); } #else float CycleTimerHelper::getRate() { return getNominalRate(); } float CycleTimerHelper::getNominalRate() { float rate = ((double)TICKS_PER_SECOND) / 1000000.0; return rate; } bool CycleTimerHelper::Execute() { usleep(1000*1000); return true; } uint32_t CycleTimerHelper::getCycleTimerTicks() { return CYCLE_TIMER_TO_TICKS(getCycleTimer()); } uint32_t CycleTimerHelper::getCycleTimerTicks(uint64_t now) { debugWarning("untested code\n"); #warning Untested code uint32_t cycle_timer; uint64_t local_time; readCycleTimerWithRetry(&cycle_timer, &local_time, 10); int64_t ticks = CYCLE_TIMER_TO_TICKS(cycle_timer); int delta_t = now - local_time; // how far ahead is the request? ticks += delta_t * getRate(); // add ticks if (ticks >= TICKS_PER_SECOND * 128) ticks -= TICKS_PER_SECOND * 128; else if (ticks < 0) ticks += TICKS_PER_SECOND * 128; return ticks; } uint32_t CycleTimerHelper::getCycleTimer() { uint32_t cycle_timer; uint64_t local_time; readCycleTimerWithRetry(&cycle_timer, &local_time, 10); return cycle_timer; } uint32_t CycleTimerHelper::getCycleTimer(uint64_t now) { return TICKS_TO_CYCLE_TIMER(getCycleTimerTicks(now)); } uint64_t CycleTimerHelper::getSystemTimeForCycleTimerTicks(uint32_t ticks) { debugWarning("not implemented!\n"); return 0; } uint64_t CycleTimerHelper::getSystemTimeForCycleTimer(uint32_t ctr) { uint32_t ticks = CYCLE_TIMER_TO_TICKS(ctr); return getSystemTimeForCycleTimerTicks(ticks); } #endif bool CycleTimerHelper::readCycleTimerWithRetry(uint32_t *cycle_timer, uint64_t *local_time, int ntries) { bool good=false; int maxtries = ntries; do { // the ctr read can return 0 sometimes. if that happens, reread the ctr. int maxtries2=ntries; do { if(!m_Parent.readCycleTimerReg(cycle_timer, local_time)) { debugError("Could not read cycle timer register\n"); return false; } if (*cycle_timer == 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "Bogus CTR: %08X on try %02d\n", *cycle_timer, maxtries2); } } while (*cycle_timer == 0 && maxtries2--); // catch bogus ctr reads (can happen) uint64_t cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(*cycle_timer); if (diffTicks(cycle_timer_ticks, m_cycle_timer_ticks_prev) < 0) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "non-monotonic CTR (try %02d): %" PRIu64 " -> %" PRIu64 "\n", maxtries, m_cycle_timer_ticks_prev, cycle_timer_ticks); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " : %08X -> %08X\n", m_cycle_timer_prev, *cycle_timer); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " current: %011" PRIu64 " (%03us %04ucy %04uticks)\n", cycle_timer_ticks, (unsigned int)TICKS_TO_SECS( cycle_timer_ticks ), (unsigned int)TICKS_TO_CYCLES( cycle_timer_ticks ), (unsigned int)TICKS_TO_OFFSET( cycle_timer_ticks ) ); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " prev : %011" PRIu64 " (%03us %04ucy %04uticks)\n", m_cycle_timer_ticks_prev, (unsigned int)TICKS_TO_SECS( m_cycle_timer_ticks_prev ), (unsigned int)TICKS_TO_CYCLES( m_cycle_timer_ticks_prev ), (unsigned int)TICKS_TO_OFFSET( m_cycle_timer_ticks_prev ) ); } else { good = true; m_cycle_timer_prev = *cycle_timer; m_cycle_timer_ticks_prev = cycle_timer_ticks; } } while (!good && maxtries--); return true; } void CycleTimerHelper::setVerboseLevel(int l) { setDebugLevel(l); } libffado-2.4.5/src/libieee1394/CycleTimerHelper.h0000644000175000001440000001201014206145246020735 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __CYCLETIMERHELPER_H__ #define __CYCLETIMERHELPER_H__ /** * Implements a DLL based mechanism to track the cycle timer * register of the Ieee1394Service pointed to by the 'parent'. * * A DLL mechanism is performance-wise better suited, since it * does not require an OS call. Hence we run a thread to update * the DLL at regular time intervals, and then use the DLL to * generate a cycle timer estimate for the parent to pass on * to it's clients. * * The idea is to make reading the cycle timer real-time safe, * which isn't (necessarily)the case for the direct raw1394 call, * since it's a kernel call that could block (although the current * implementation is RT safe). * * This also allows us to run on systems not having the * raw1394_read_cycle_timer kernel call. We can always do a normal * read of our own cycle timer register, but that's not very accurate. * The accuracy is improved by this DLL mechanism. Still not as good * as when using the raw1394_read_cycle_timer call, but anyway. * * On the long run this code will also allow us to map system time * on to 1394 time for the current host controller, hence enabling * different clock domains to operate together. */ #include "libutil/Thread.h" #include "libutil/SystemTimeSource.h" #include "cycletimer.h" #include "libutil/Functors.h" #include "libutil/Mutex.h" #include "debugmodule/debugmodule.h" class Ieee1394Service; class CycleTimerHelper : public Util::RunnableInterface { public: CycleTimerHelper(Ieee1394Service &, unsigned int); CycleTimerHelper(Ieee1394Service &, unsigned int, bool rt, int prio); ~CycleTimerHelper(); virtual bool Init(); virtual bool Execute(); bool setThreadParameters(bool rt, int priority); bool Start(); /** * @brief get the current cycle timer value (in ticks) * @note thread safe */ uint32_t getCycleTimerTicks(); /** * @brief get the cycle timer value for a specific time instant (in ticks) * @note thread safe */ uint32_t getCycleTimerTicks(uint64_t now); /** * @brief get the current cycle timer value (in CTR format) * @note thread safe */ uint32_t getCycleTimer(); /** * @brief get the cycle timer value for a specific time instant (in CTR format) * @note thread safe */ uint32_t getCycleTimer(uint64_t now); /** * @brief get the system time for a specific cycle timer value (in ticks) * @note thread safe */ uint64_t getSystemTimeForCycleTimerTicks(uint32_t ticks); /** * @brief get the system time for a specific cycle timer value (in CTR format) * @note thread safe */ uint64_t getSystemTimeForCycleTimer(uint32_t ctr); float getRate(); float getNominalRate(); /** * @brief handle a bus reset */ void busresetHandler(); void setVerboseLevel(int l); private: bool readCycleTimerWithRetry(uint32_t *cycle_timer, uint64_t *local_time, int ntries); bool initValues(); #if IEEE1394SERVICE_USE_CYCLETIMER_DLL bool initDLL(); #endif Ieee1394Service &m_Parent; // parameters uint32_t m_ticks_per_update; uint32_t m_usecs_per_update; float m_avg_wakeup_delay; // state variables double m_dll_e2; double m_current_time_usecs; double m_next_time_usecs; double m_current_time_ticks; double m_next_time_ticks; bool m_first_run; ffado_microsecs_t m_sleep_until; uint32_t m_cycle_timer_prev; uint64_t m_cycle_timer_ticks_prev; double m_dll_coeff_b; double m_dll_coeff_c; // cached vars used for computation struct compute_vars { uint64_t usecs; uint64_t ticks; double rate; }; #define CTRHELPER_NB_SHADOW_VARS 8 struct compute_vars m_shadow_vars[CTRHELPER_NB_SHADOW_VARS]; volatile unsigned int m_current_shadow_idx; // Threading Util::Thread * m_Thread; bool m_realtime; unsigned int m_priority; Util::Mutex* m_update_lock; // busreset handling Util::Functor* m_busreset_functor; bool m_unhandled_busreset; #ifdef DEBUG uint64_t m_last_loop_entry; int m_successive_short_loops; #endif // debug stuff DECLARE_DEBUG_MODULE; }; #endif libffado-2.4.5/src/libieee1394/IEC61883.cpp0000644000175000001440000000171514206145246017114 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "IEC61883.h" IMPL_DEBUG_MODULE( IEC61883, IEC61883, DEBUG_LEVEL_NORMAL ); IEC61883::IEC61883() { } IEC61883::~IEC61883() { } libffado-2.4.5/src/libieee1394/IEC61883.h0000644000175000001440000001344114206145246016560 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_IEC61883__ #define __FFADO_IEC61883__ #include "../debugmodule/debugmodule.h" /* * This is shamelessly stolen from iec61883-private, * but I need these functions! * FIXME: this will only work until somebody decides to change * these in libiec61883. */ #ifdef __cplusplus extern "C" { #endif /** * Plug Control Registers **/ /* maximum number of PCRs allowed within the standard * MPR/PCR addresses defined in IEC-61883. * This refers to the number of output or input PCRs-- * not the MPRs and not the combined total. */ #define IEC61883_PCR_MAX 31 /* standard CSR offsets for plugs */ #define CSR_O_MPR 0x900 #define CSR_O_PCR_0 0x904 #define CSR_I_MPR 0x980 #define CSR_I_PCR_0 0x984 #if ( __BYTE_ORDER == __BIG_ENDIAN ) struct iec61883_oMPR { unsigned int data_rate:2; unsigned int bcast_channel:6; unsigned int non_persist_ext:8; unsigned int persist_ext:8; unsigned int reserved:3; unsigned int n_plugs:5; }; struct iec61883_iMPR { unsigned int data_rate:2; unsigned int reserved:6; unsigned int non_persist_ext:8; unsigned int persist_ext:8; unsigned int reserved2:3; unsigned int n_plugs:5; }; struct iec61883_oPCR { unsigned int online:1; unsigned int bcast_connection:1; unsigned int n_p2p_connections:6; unsigned int reserved:2; unsigned int channel:6; unsigned int data_rate:2; unsigned int overhead_id:4; unsigned int payload:10; }; struct iec61883_iPCR { unsigned int online:1; unsigned int bcast_connection:1; unsigned int n_p2p_connections:6; unsigned int reserved:2; unsigned int channel:6; unsigned int reserved2:16; }; #else struct iec61883_oMPR { unsigned int n_plugs:5; unsigned int reserved:3; unsigned int persist_ext:8; unsigned int non_persist_ext:8; unsigned int bcast_channel:6; unsigned int data_rate:2; }; struct iec61883_iMPR { unsigned int n_plugs:5; unsigned int reserved2:3; unsigned int persist_ext:8; unsigned int non_persist_ext:8; unsigned int reserved:6; unsigned int data_rate:2; }; struct iec61883_oPCR { unsigned int payload:10; unsigned int overhead_id:4; unsigned int data_rate:2; unsigned int channel:6; unsigned int reserved:2; unsigned int n_p2p_connections:6; unsigned int bcast_connection:1; unsigned int online:1; }; struct iec61883_iPCR { unsigned int reserved2:16; unsigned int channel:6; unsigned int reserved:2; unsigned int n_p2p_connections:6; unsigned int bcast_connection:1; unsigned int online:1; }; #endif /** * iec61883_plug_get - Read a node's plug register. * @h: A raw1394 handle. * @n: The node id of the node to read * @a: The CSR offset address (relative to base) of the register to read. * @value: A pointer to a quadlet where the plug register's value will be stored. * * This function handles bus to host endian conversion. It returns 0 for * suceess or -1 for error (errno available). **/ int iec61883_plug_get(raw1394handle_t h, nodeid_t n, nodeaddr_t a, quadlet_t *value); /** * iec61883_plug_set - Write a node's plug register. * @h: A raw1394 handle. * @n: The node id of the node to read * @a: The CSR offset address (relative to CSR base) of the register to write. * @value: A quadlet containing the new register value. * * This uses a compare/swap lock operation to safely write the * new register value, as required by IEC 61883-1. * This function handles host to bus endian conversion. It returns 0 for success * or -1 for error (errno available). **/ int iec61883_plug_set(raw1394handle_t h, nodeid_t n, nodeaddr_t a, quadlet_t value); /** * High level plug access macros */ #define iec61883_get_oMPR(h,n,v) iec61883_plug_get((h), (n), CSR_O_MPR, (quadlet_t *)(v)) #define iec61883_set_oMPR(h,n,v) iec61883_plug_set((h), (n), CSR_O_MPR, *((quadlet_t *)&(v))) #define iec61883_get_oPCR0(h,n,v) iec61883_plug_get((h), (n), CSR_O_PCR_0, (quadlet_t *)(v)) #define iec61883_set_oPCR0(h,n,v) iec61883_plug_set((h), (n), CSR_O_PCR_0, *((quadlet_t *)&(v))) #define iec61883_get_oPCRX(h,n,v,x) iec61883_plug_get((h), (n), CSR_O_PCR_0+(4*(x)), (quadlet_t *)(v)) #define iec61883_set_oPCRX(h,n,v,x) iec61883_plug_set((h), (n), CSR_O_PCR_0+(4*(x)), *((quadlet_t *)&(v))) #define iec61883_get_iMPR(h,n,v) iec61883_plug_get((h), (n), CSR_I_MPR, (quadlet_t *)(v)) #define iec61883_set_iMPR(h,n,v) iec61883_plug_set((h), (n), CSR_I_MPR, *((quadlet_t *)&(v))) #define iec61883_get_iPCR0(h,n,v) iec61883_plug_get((h), (n), CSR_I_PCR_0, (quadlet_t *)(v)) #define iec61883_set_iPCR0(h,n,v) iec61883_plug_set((h), (n), CSR_I_PCR_0, *((quadlet_t *)&(v))) #define iec61883_get_iPCRX(h,n,v,x) iec61883_plug_get((h), (n), CSR_I_PCR_0+(4*(x)), (quadlet_t *)(v)) #define iec61883_set_iPCRX(h,n,v,x) iec61883_plug_set((h), (n), CSR_I_PCR_0+(4*(x)), *((quadlet_t *)&(v))) #ifdef __cplusplus } #endif class IEC61883 { public: IEC61883(); virtual ~IEC61883(); protected: DECLARE_DEBUG_MODULE; }; #endif /* __FFADO_IEC61883__ */ libffado-2.4.5/src/libieee1394/IsoHandlerManager.cpp0000644000175000001440000022016514206145246021427 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "IsoHandlerManager.h" #include "ieee1394service.h" #include "cycletimer.h" #include "libstreaming/generic/StreamProcessor.h" #include "libutil/Atomic.h" #include "libutil/PosixThread.h" #include "libutil/SystemTimeSource.h" #include "libutil/Watchdog.h" #include "libutil/Configuration.h" #include #include #include IMPL_DEBUG_MODULE( IsoHandlerManager, IsoHandlerManager, DEBUG_LEVEL_NORMAL ); IMPL_DEBUG_MODULE( IsoHandlerManager::IsoTask, IsoTask, DEBUG_LEVEL_NORMAL ); IMPL_DEBUG_MODULE( IsoHandlerManager::IsoHandler, IsoHandler, DEBUG_LEVEL_NORMAL ); using namespace Streaming; // --- ISO Thread --- // IsoHandlerManager::IsoTask::IsoTask(IsoHandlerManager& manager, enum IsoHandler::EHandlerType t) : m_manager( manager ) , m_SyncIsoHandler ( NULL ) , m_handlerType( t ) , m_running( false ) , m_in_busreset( false ) , m_activity_wait_timeout_nsec (ISOHANDLERMANAGER_ISO_TASK_WAIT_TIMEOUT_USECS * 1000LL) { } IsoHandlerManager::IsoTask::~IsoTask() { sem_destroy(&m_activity_semaphore); } bool IsoHandlerManager::IsoTask::Init() { request_update = 0; int i; for (i=0; i < ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT; i++) { m_IsoHandler_map_shadow[i] = NULL; m_poll_fds_shadow[i].events = 0; } m_poll_nfds_shadow = 0; #ifdef DEBUG m_last_loop_entry = 0; m_successive_short_loops = 0; #endif sem_init(&m_activity_semaphore, 0, 0); m_running = true; return true; } void IsoHandlerManager::IsoTask::requestShadowMapUpdate() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) enter\n", this); INC_ATOMIC(&request_update); // get the thread going again signalActivity(); debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) exit\n", this); } bool IsoHandlerManager::IsoTask::handleBusReset() { bool retval = true; if(!m_running) { // nothing to do here return true; } m_in_busreset = true; requestShadowMapUpdate(); unsigned int i, max; max = m_manager.m_IsoHandlers.size(); for (i = 0; i < max; i++) { IsoHandler *h = m_manager.m_IsoHandlers.at(i); assert(h); // skip the handlers not intended for us if(h->getType() != m_handlerType) continue; if (!h->handleBusReset()) { debugWarning("Failed to handle busreset on %p\n", h); retval = false; } } // re-enable processing m_in_busreset = false; requestShadowMapUpdate(); return retval; } // updates the internal stream map // note that this should be executed with the guarantee that // nobody will modify the parent data structures void IsoHandlerManager::IsoTask::updateShadowMapHelper() { debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) updating shadow vars...\n", this); // we are handling a busreset if(m_in_busreset) { m_poll_nfds_shadow = 0; return; } unsigned int i, cnt, max; max = m_manager.m_IsoHandlers.size(); m_SyncIsoHandler = NULL; for (i = 0, cnt = 0; i < max; i++) { // FIXME: This is a very crude guard against some other thread // deleting handlers while this function is running. While this // didn't tend to happen with the old kernel FireWire stack, delays // in shutdown experienced in the new stack mean it can happen that // a handler disappears during the running of this function. This // test should prevent "out of range" exceptions in most cases. // However, it is racy: if the deletion happens between this // conditional and the following at() call, an out of range // condition can still happen. if (i>=m_manager.m_IsoHandlers.size()) continue; IsoHandler *h = m_manager.m_IsoHandlers.at(i); assert(h); // skip the handlers not intended for us if(h->getType() != m_handlerType) continue; // update the state of the handler // FIXME: maybe this is not the best place to do this // it might be better to eliminate the 'requestShadowMapUpdate' // entirely and replace it with a mechanism that implements all // actions on the m_manager.m_IsoHandlers in the loop h->updateState(); // rebuild the map if (h->isEnabled()) { m_IsoHandler_map_shadow[cnt] = h; m_poll_fds_shadow[cnt].fd = h->getFileDescriptor(); m_poll_fds_shadow[cnt].revents = 0; m_poll_fds_shadow[cnt].events = POLLIN; cnt++; // FIXME: need a more generic approach here if( m_SyncIsoHandler == NULL && h->getType() == IsoHandler::eHT_Transmit) { m_SyncIsoHandler = h; } debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) %s handler %p added\n", this, h->getTypeString(), h); } else { debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) %s handler %p skipped (disabled)\n", this, h->getTypeString(), h); } if(cnt > ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT) { debugWarning("Too much ISO Handlers in thread...\n"); break; } } // FIXME: need a more generic approach here // if there are no active transmit handlers, // use the first receive handler if( m_SyncIsoHandler == NULL && m_poll_nfds_shadow) { m_SyncIsoHandler = m_IsoHandler_map_shadow[0]; } m_poll_nfds_shadow = cnt; debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) updated shadow vars...\n", this); } bool IsoHandlerManager::IsoTask::Execute() { debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%p, %s) Execute\n", this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); int err; unsigned int i; unsigned int m_poll_timeout = 10; #ifdef DEBUG uint64_t now = Util::SystemTimeSource::getCurrentTimeAsUsecs(); int diff = now - m_last_loop_entry; if(diff < 100) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p, %s) short loop detected (%d usec), cnt: %d\n", this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive"), diff, m_successive_short_loops); m_successive_short_loops++; if(m_successive_short_loops > 10000) { debugError("Shutting down runaway thread\n"); m_running = false; return false; } } else { // reset the counter m_successive_short_loops = 0; } m_last_loop_entry = now; #endif // if some other thread requested a shadow map update, do it if(request_update) { updateShadowMapHelper(); DEC_ATOMIC(&request_update); // ack the update assert(request_update >= 0); } // bypass if no handlers are registered if (m_poll_nfds_shadow == 0) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p, %s) bypass iterate since no handlers to poll\n", this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); usleep(m_poll_timeout * 1000); return true; } // FIXME: what can happen is that poll() returns, but not all clients are // ready. there might be some busy waiting behavior that still has to be solved. // setup the poll here // we should prevent a poll() where no events are specified, since that will only time-out bool no_one_to_poll = true; while(no_one_to_poll) { for (i = 0; i < m_poll_nfds_shadow; i++) { short events = 0; IsoHandler *h = m_IsoHandler_map_shadow[i]; // we should only poll on a transmit handler // that has a client that is ready to send // something. Otherwise it will end up in // busy wait looping since the packet function // will defer processing (also avoids the // AGAIN problem) if (h->canIterateClient()) { events = POLLIN | POLLPRI; no_one_to_poll = false; } m_poll_fds_shadow[i].events = events; } if(no_one_to_poll) { debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "(%p, %s) No one to poll, waiting for something to happen\n", this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); // wait for something to happen switch(waitForActivity()) { case IsoHandlerManager::IsoTask::eAR_Error: debugError("Error while waiting for activity\n"); return false; case IsoHandlerManager::IsoTask::eAR_Interrupted: // FIXME: what to do here? debugWarning("Interrupted while waiting for activity\n"); break; case IsoHandlerManager::IsoTask::eAR_Timeout: // FIXME: what to do here? debugWarning("Timeout while waiting for activity\n"); no_one_to_poll = false; // exit the loop to be able to detect failing handlers break; case IsoHandlerManager::IsoTask::eAR_Activity: // do nothing debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "(%p, %s) something happened\n", this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); break; } } } // Use a shadow map of the fd's such that we don't have to update // the fd map everytime we run poll(). err = poll (m_poll_fds_shadow, m_poll_nfds_shadow, m_poll_timeout); uint32_t ctr_at_poll_return = m_manager.get1394Service().getCycleTimer(); if (err < 0) { if (errno == EINTR) { debugOutput(DEBUG_LEVEL_VERBOSE, "Ignoring poll return due to signal\n"); return true; } debugFatal("poll error: %s\n", strerror (errno)); m_running = false; return false; } // find handlers that have died uint64_t ctr_at_poll_return_ticks = CYCLE_TIMER_TO_TICKS(ctr_at_poll_return); bool handler_died = false; for (i = 0; i < m_poll_nfds_shadow; i++) { // figure out if a handler has died if (!m_IsoHandler_map_shadow[i]->isEnabled()) { // This handler is already dead. handler_died = true; continue; } // this is the time of the last packet we saw in the iterate() handler uint32_t last_packet_seen = m_IsoHandler_map_shadow[i]->getLastPacketTime(); if (last_packet_seen == 0xFFFFFFFF) { // this was not iterated yet, so can't be dead debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "(%p, %s) handler %d didn't see any packets yet\n", this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive"), i); continue; } uint64_t last_packet_seen_ticks = CYCLE_TIMER_TO_TICKS(last_packet_seen); // we use a relatively large value to distinguish between "death" and xrun int64_t max_diff_ticks = TICKS_PER_SECOND * 2; int64_t measured_diff_ticks = diffTicks(ctr_at_poll_return_ticks, last_packet_seen_ticks); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "(%p, %s) check handler %d: diff = %" PRId64 ", max = %" PRId64 ", now: %08X, last: %08X\n", this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive"), i, measured_diff_ticks, max_diff_ticks, ctr_at_poll_return, last_packet_seen); if(measured_diff_ticks > max_diff_ticks) { debugWarning("(%p, %s) Handler died: now: %08X, last: %08X, diff: %" PRId64 " (max: %" PRId64 ")\n", this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive"), ctr_at_poll_return, last_packet_seen, measured_diff_ticks, max_diff_ticks); m_IsoHandler_map_shadow[i]->notifyOfDeath(); handler_died = true; } } if(handler_died) { m_running = false; // One or more handlers have died, however it can be restarted again, // so keep looping. The xrun handling code will eventually time out if // we are not able to recover. return true; } // iterate the handlers for (i = 0; i < m_poll_nfds_shadow; i++) { #ifdef DEBUG if(m_poll_fds_shadow[i].revents) { debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "(%p, %s) received events: %08X for (%d/%d, %p, %s)\n", this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive"), m_poll_fds_shadow[i].revents, i, m_poll_nfds_shadow, m_IsoHandler_map_shadow[i], m_IsoHandler_map_shadow[i]->getTypeString()); } #endif // if we get here, it means two things: // 1) the kernel can accept or provide packets (poll returned POLLIN) // 2) the client can provide or accept packets (since we enabled polling) if(m_poll_fds_shadow[i].revents & (POLLIN)) { m_IsoHandler_map_shadow[i]->iterate(ctr_at_poll_return); } else { // there might be some error condition if (m_poll_fds_shadow[i].revents & POLLERR) { debugWarning("(%p) error on fd for %d\n", this, i); } if (m_poll_fds_shadow[i].revents & POLLHUP) { debugWarning("(%p) hangup on fd for %d\n", this, i); } } } return true; } enum IsoHandlerManager::IsoTask::eActivityResult IsoHandlerManager::IsoTask::waitForActivity() { debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "(%p, %s) waiting for activity\n", this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); struct timespec ts; int result; // sem_timedwait() cannot be set up to use any clock rather than // CLOCK_REALTIME. Therefore we get the time from CLOCK_REALTIME here. // Doing this rather than Util::SystemTimeSource::clockGettime() doesn't // pose a problem here because the resulting time is only used with // sem_timedwait() to implement timeout functionality. if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { debugError("clock_gettime failed\n"); return eAR_Error; } ts.tv_nsec += m_activity_wait_timeout_nsec; while(ts.tv_nsec >= 1000000000LL) { ts.tv_sec += 1; ts.tv_nsec -= 1000000000LL; } result = sem_timedwait(&m_activity_semaphore, &ts); if(result != 0) { if (errno == ETIMEDOUT) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) sem_timedwait() timed out (result=%d)\n", this, result); return eAR_Timeout; } else if (errno == EINTR) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) sem_timedwait() interrupted by signal (result=%d)\n", this, result); return eAR_Interrupted; } else if (errno == EINVAL) { debugError("(%p) sem_timedwait error (result=%d errno=EINVAL)\n", this, result); debugError("(%p) timeout_nsec=%lld ts.sec=%" PRId64 " ts.nsec=%" PRId64 "\n", this, m_activity_wait_timeout_nsec, (int64_t)ts.tv_sec, (int64_t)ts.tv_nsec); return eAR_Error; } else { debugError("(%p) sem_timedwait error (result=%d errno=%d)\n", this, result, errno); debugError("(%p) timeout_nsec=%lld ts.sec=%" PRId64 " ts.nsec=%" PRId64 "\n", this, m_activity_wait_timeout_nsec, (int64_t)ts.tv_sec, (int64_t)ts.tv_nsec); return eAR_Error; } } debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%p, %s) got activity\n", this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); return eAR_Activity; } void IsoHandlerManager::IsoTask::signalActivity() { // signal the activity cond var sem_post(&m_activity_semaphore); debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%p, %s) activity\n", this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); } void IsoHandlerManager::IsoTask::setVerboseLevel(int i) { setDebugLevel(i); debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", i ); } // -- the ISO handler manager -- // IsoHandlerManager::IsoHandlerManager(Ieee1394Service& service) : m_State(E_Created) , m_service( service ) , m_realtime(false), m_priority(0) , m_IsoThreadTransmit ( NULL ) , m_IsoTaskTransmit ( NULL ) , m_IsoThreadReceive ( NULL ) , m_IsoTaskReceive ( NULL ) { } IsoHandlerManager::IsoHandlerManager(Ieee1394Service& service, bool run_rt, int rt_prio) : m_State(E_Created) , m_service( service ) , m_realtime(run_rt), m_priority(rt_prio) , m_IsoThreadTransmit ( NULL ) , m_IsoTaskTransmit ( NULL ) , m_IsoThreadReceive ( NULL ) , m_IsoTaskReceive ( NULL ) , m_MissedCyclesOK ( false ) { } IsoHandlerManager::~IsoHandlerManager() { stopHandlers(); pruneHandlers(); if(m_IsoHandlers.size() > 0) { debugError("Still some handlers in use\n"); } if (m_IsoThreadTransmit) { m_IsoThreadTransmit->Stop(); delete m_IsoThreadTransmit; } if (m_IsoThreadReceive) { m_IsoThreadReceive->Stop(); delete m_IsoThreadReceive; } if (m_IsoTaskTransmit) { delete m_IsoTaskTransmit; } if (m_IsoTaskReceive) { delete m_IsoTaskReceive; } } bool IsoHandlerManager::handleBusReset() { debugOutput( DEBUG_LEVEL_NORMAL, "bus reset...\n"); // A few things can happen on bus reset: // 1) no devices added/removed => streams are still valid, but might have to be restarted // 2) a device was removed => some streams become invalid // 3) a device was added => same as 1, new device is ignored if (!m_IsoTaskTransmit) { debugError("No xmit task\n"); return false; } if (!m_IsoTaskReceive) { debugError("No receive task\n"); return false; } if (!m_IsoTaskTransmit->handleBusReset()) { debugWarning("could no handle busreset on xmit\n"); } if (!m_IsoTaskReceive->handleBusReset()) { debugWarning("could no handle busreset on recv\n"); } return true; } void IsoHandlerManager::requestShadowMapUpdate() { if(m_IsoTaskTransmit) m_IsoTaskTransmit->requestShadowMapUpdate(); if(m_IsoTaskReceive) m_IsoTaskReceive->requestShadowMapUpdate(); } bool IsoHandlerManager::setThreadParameters(bool rt, int priority) { debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) switch to: (rt=%d, prio=%d)...\n", this, rt, priority); if (priority > THREAD_MAX_RTPRIO) priority = THREAD_MAX_RTPRIO; // cap the priority if (priority < THREAD_MIN_RTPRIO) priority = THREAD_MIN_RTPRIO; // cap the priority m_realtime = rt; m_priority = priority; // grab the options from the parent Util::Configuration *config = m_service.getConfiguration(); int ihm_iso_prio_increase = ISOHANDLERMANAGER_ISO_PRIO_INCREASE; int ihm_iso_prio_increase_xmit = ISOHANDLERMANAGER_ISO_PRIO_INCREASE_XMIT; int ihm_iso_prio_increase_recv = ISOHANDLERMANAGER_ISO_PRIO_INCREASE_RECV; if(config) { config->getValueForSetting("ieee1394.isomanager.prio_increase", ihm_iso_prio_increase); config->getValueForSetting("ieee1394.isomanager.prio_increase_xmit", ihm_iso_prio_increase_xmit); config->getValueForSetting("ieee1394.isomanager.prio_increase_recv", ihm_iso_prio_increase_recv); } if (m_IsoThreadTransmit) { if (m_realtime) { m_IsoThreadTransmit->AcquireRealTime(m_priority + ihm_iso_prio_increase + ihm_iso_prio_increase_xmit); } else { m_IsoThreadTransmit->DropRealTime(); } } if (m_IsoThreadReceive) { if (m_realtime) { m_IsoThreadReceive->AcquireRealTime(m_priority + ihm_iso_prio_increase + ihm_iso_prio_increase_recv); } else { m_IsoThreadReceive->DropRealTime(); } } return true; } bool IsoHandlerManager::init() { debugOutput( DEBUG_LEVEL_VERBOSE, "Initializing ISO manager %p...\n", this); // check state if(m_State != E_Created) { debugError("Manager already initialized...\n"); return false; } // grab the options from the parent Util::Configuration *config = m_service.getConfiguration(); int ihm_iso_prio_increase = ISOHANDLERMANAGER_ISO_PRIO_INCREASE; int ihm_iso_prio_increase_xmit = ISOHANDLERMANAGER_ISO_PRIO_INCREASE_XMIT; int ihm_iso_prio_increase_recv = ISOHANDLERMANAGER_ISO_PRIO_INCREASE_RECV; int64_t isotask_activity_timeout_usecs = ISOHANDLERMANAGER_ISO_TASK_WAIT_TIMEOUT_USECS; if(config) { config->getValueForSetting("ieee1394.isomanager.prio_increase", ihm_iso_prio_increase); config->getValueForSetting("ieee1394.isomanager.prio_increase_xmit", ihm_iso_prio_increase_xmit); config->getValueForSetting("ieee1394.isomanager.prio_increase_recv", ihm_iso_prio_increase_recv); config->getValueForSetting("ieee1394.isomanager.isotask_activity_timeout_usecs", isotask_activity_timeout_usecs); } // create threads to iterate our ISO handlers debugOutput( DEBUG_LEVEL_VERBOSE, "Create iso thread for %p transmit...\n", this); m_IsoTaskTransmit = new IsoTask( *this, IsoHandler::eHT_Transmit ); if(!m_IsoTaskTransmit) { debugFatal("No task\n"); return false; } m_IsoTaskTransmit->setVerboseLevel(getDebugLevel()); m_IsoTaskTransmit->m_activity_wait_timeout_nsec = isotask_activity_timeout_usecs * 1000LL; m_IsoThreadTransmit = new Util::PosixThread(m_IsoTaskTransmit, "ISOXMT", m_realtime, m_priority + ihm_iso_prio_increase + ihm_iso_prio_increase_xmit, PTHREAD_CANCEL_DEFERRED); if(!m_IsoThreadTransmit) { debugFatal("No thread\n"); return false; } m_IsoThreadTransmit->setVerboseLevel(getDebugLevel()); debugOutput( DEBUG_LEVEL_VERBOSE, "Create iso thread for %p receive...\n", this); m_IsoTaskReceive = new IsoTask( *this, IsoHandler::eHT_Receive ); if(!m_IsoTaskReceive) { debugFatal("No task\n"); return false; } m_IsoTaskReceive->setVerboseLevel(getDebugLevel()); m_IsoThreadReceive = new Util::PosixThread(m_IsoTaskReceive, "ISORCV", m_realtime, m_priority + ihm_iso_prio_increase + ihm_iso_prio_increase_recv, PTHREAD_CANCEL_DEFERRED); if(!m_IsoThreadReceive) { debugFatal("No thread\n"); return false; } m_IsoThreadReceive->setVerboseLevel(getDebugLevel()); // register the thread with the RT watchdog Util::Watchdog *watchdog = m_service.getWatchdog(); if(watchdog) { if(!watchdog->registerThread(m_IsoThreadTransmit)) { debugWarning("could not register iso transmit thread with watchdog\n"); } if(!watchdog->registerThread(m_IsoThreadReceive)) { debugWarning("could not register iso receive thread with watchdog\n"); } } else { debugWarning("could not find valid watchdog\n"); } if (m_IsoThreadTransmit->Start() != 0) { debugFatal("Could not start ISO Transmit thread\n"); return false; } if (m_IsoThreadReceive->Start() != 0) { debugFatal("Could not start ISO Receive thread\n"); return false; } m_State=E_Running; return true; } void IsoHandlerManager::signalActivityTransmit() { assert(m_IsoTaskTransmit); m_IsoTaskTransmit->signalActivity(); } void IsoHandlerManager::signalActivityReceive() { assert(m_IsoTaskReceive); m_IsoTaskReceive->signalActivity(); } bool IsoHandlerManager::registerHandler(IsoHandler *handler) { debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n"); assert(handler); handler->setVerboseLevel(getDebugLevel()); m_IsoHandlers.push_back(handler); requestShadowMapUpdate(); return true; } bool IsoHandlerManager::unregisterHandler(IsoHandler *handler) { debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n"); assert(handler); for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); it != m_IsoHandlers.end(); ++it ) { if ( *it == handler ) { m_IsoHandlers.erase(it); requestShadowMapUpdate(); return true; } } debugFatal("Could not find handler (%p)\n", handler); return false; //not found } /** * Registers an StreamProcessor with the IsoHandlerManager. * * If nescessary, an IsoHandler is created to handle this stream. * Once an StreamProcessor is registered to the handler, it will be included * in the ISO streaming cycle (i.e. receive/transmit of it will occur). * * @param stream the stream to register * @return true if registration succeeds * * \todo : currently there is a one-to-one mapping * between streams and handlers, this is not ok for * multichannel receive */ bool IsoHandlerManager::registerStream(StreamProcessor *stream) { debugOutput( DEBUG_LEVEL_VERBOSE, "Registering %s stream %p\n", stream->getTypeString(), 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) { // grab the options from the parent Util::Configuration *config = m_service.getConfiguration(); int receive_mode_setting = DEFAULT_ISO_RECEIVE_MODE; int bufferfill_mode_threshold = BUFFERFILL_MODE_THRESHOLD; int min_interrupts_per_period = MINIMUM_INTERRUPTS_PER_PERIOD; int max_nb_buffers_recv = MAX_RECV_NB_BUFFERS; int min_packetsize_recv = MIN_RECV_PACKET_SIZE; if(config) { config->getValueForSetting("ieee1394.isomanager.iso_receive_mode", receive_mode_setting); config->getValueForSetting("ieee1394.isomanager.bufferfill_mode_threshold", bufferfill_mode_threshold); config->getValueForSetting("ieee1394.isomanager.min_interrupts_per_period", min_interrupts_per_period); config->getValueForSetting("ieee1394.isomanager.max_nb_buffers_recv", max_nb_buffers_recv); config->getValueForSetting("ieee1394.isomanager.min_packetsize_recv", min_packetsize_recv); } // setup the optimal parameters for the raw1394 ISO buffering unsigned int packets_per_period = stream->getPacketsPerPeriod(); // reserve space for the 1394 header too (might not be necessary) unsigned int max_packet_size = stream->getMaxPacketSize() + 8; unsigned int page_size = getpagesize(); enum raw1394_iso_dma_recv_mode receive_mode = RAW1394_DMA_PACKET_PER_BUFFER; switch(receive_mode_setting) { case 0: if(packets_per_period < (unsigned)bufferfill_mode_threshold) { debugOutput( DEBUG_LEVEL_VERBOSE, "Using packet-per-buffer mode (auto) [%d, %d]\n", packets_per_period, bufferfill_mode_threshold); receive_mode = RAW1394_DMA_PACKET_PER_BUFFER; } else { debugOutput( DEBUG_LEVEL_VERBOSE, "Using bufferfill mode (auto) [%d, %d]\n", packets_per_period, bufferfill_mode_threshold); receive_mode = RAW1394_DMA_BUFFERFILL; } break; case 1: debugOutput( DEBUG_LEVEL_VERBOSE, "Using packet-per-buffer mode (config)\n"); receive_mode = RAW1394_DMA_PACKET_PER_BUFFER; break; case 2: debugOutput( DEBUG_LEVEL_VERBOSE, "Using bufferfill mode (config)\n"); receive_mode = RAW1394_DMA_BUFFERFILL; break; default: debugWarning("Bogus receive mode setting in config: %d\n", receive_mode_setting); } // Ensure we don't request a packet size bigger than the // kernel-enforced maximum which is currently 1 page. // NOTE: PP: this is not really true AFAICT if (max_packet_size > page_size) { debugError("max packet size (%u) > page size (%u)\n", max_packet_size, page_size); return false; } if (max_packet_size < (unsigned)min_packetsize_recv) { debugError("min packet size (%u) < MIN_RECV_PACKET_SIZE (%u), using min value\n", max_packet_size, min_packetsize_recv); max_packet_size = min_packetsize_recv; } // apparently a too small value causes issues too if(max_packet_size < 200) max_packet_size = 200; // the interrupt/wakeup interval prediction of raw1394 is a mess... int irq_interval = (packets_per_period-1) / min_interrupts_per_period; if(irq_interval <= 0) irq_interval=1; // the receive buffer size doesn't matter for the latency, // it does seem to be confined to a certain region for correct // operation. However it is not clear how many. int buffers = max_nb_buffers_recv; // ensure at least 2 hardware interrupts per ISO buffer wraparound if(irq_interval > buffers/2) { irq_interval = buffers/2; } // create the actual handler debugOutput( DEBUG_LEVEL_VERBOSE, " creating IsoRecvHandler\n"); h = new IsoHandler(*this, IsoHandler::eHT_Receive, buffers, max_packet_size, irq_interval); if(!h) { debugFatal("Could not create IsoRecvHandler\n"); return false; } h->setReceiveMode(receive_mode); } else if (stream->getType()==StreamProcessor::ePT_Transmit) { // grab the options from the parent Util::Configuration *config = m_service.getConfiguration(); int min_interrupts_per_period = MINIMUM_INTERRUPTS_PER_PERIOD; int max_nb_buffers_xmit = MAX_XMIT_NB_BUFFERS; int max_packetsize_xmit = MAX_XMIT_PACKET_SIZE; int min_packetsize_xmit = MIN_XMIT_PACKET_SIZE; if(config) { config->getValueForSetting("ieee1394.isomanager.min_interrupts_per_period", min_interrupts_per_period); config->getValueForSetting("ieee1394.isomanager.max_nb_buffers_xmit", max_nb_buffers_xmit); config->getValueForSetting("ieee1394.isomanager.max_packetsize_xmit", max_packetsize_xmit); config->getValueForSetting("ieee1394.isomanager.min_packetsize_xmit", min_packetsize_xmit); } // setup the optimal parameters for the raw1394 ISO buffering // reserve space for the 1394 header too (might not be necessary) unsigned int max_packet_size = stream->getMaxPacketSize() + 8; if (max_packet_size > (unsigned)max_packetsize_xmit) { debugError("max packet size (%u) > MAX_XMIT_PACKET_SIZE (%u)\n", max_packet_size, max_packetsize_xmit); return false; } if (max_packet_size < (unsigned)min_packetsize_xmit) { debugError("min packet size (%u) < MIN_XMIT_PACKET_SIZE (%u), using min value\n", max_packet_size, min_packetsize_xmit); max_packet_size = min_packetsize_xmit; } int buffers = max_nb_buffers_xmit; unsigned int packets_per_period = stream->getPacketsPerPeriod(); int irq_interval = (packets_per_period-1) / min_interrupts_per_period; if(irq_interval <= 0) irq_interval=1; // ensure at least 2 hardware interrupts per ISO buffer wraparound if(irq_interval > buffers/2) { irq_interval = buffers/2; } 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()); // 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, " %zd streams, %zd handlers registered\n", m_StreamProcessors.size(), m_IsoHandlers.size()); return true; } bool IsoHandlerManager::unregisterStream(StreamProcessor *stream) { debugOutput( DEBUG_LEVEL_VERBOSE, "Unregistering %s stream %p\n", stream->getTypeString(), 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; } } int IsoHandlerManager::getPacketLatencyForStream(Streaming::StreamProcessor *stream) { for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); it != m_IsoHandlers.end(); ++it ) { if((*it)->isStreamRegistered(stream)) { return (*it)->getIrqInterval(); } } debugError("Stream %p has no attached handler\n", stream); return 0; } IsoHandlerManager::IsoHandler * IsoHandlerManager::getHandlerForStream(Streaming::StreamProcessor *stream) { for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); it != m_IsoHandlers.end(); ++it ) { if((*it)->isStreamRegistered(stream)) { return (*it); } } debugError("Stream %p has no attached handler\n", stream); return NULL; } void IsoHandlerManager::dumpInfoForStream(Streaming::StreamProcessor *stream) { IsoHandler *h = getHandlerForStream(stream); if (h) { #ifdef DEBUG debugOutputShort( DEBUG_LEVEL_NORMAL, " Packets, Dropped, Skipped : %d, %d, %d\n", h->m_packets, h->m_dropped, h->m_skipped); #else debugOutputShort( DEBUG_LEVEL_NORMAL, " Packets : %d\n", h->m_packets); #endif } else { debugError("No handler for stream %p??\n", stream); } } void IsoHandlerManager::setIsoStartCycleForStream(Streaming::StreamProcessor *stream, signed int cycle) { // Permit the direct manipulation of the m_switch_on_cycle field from // the stream's handler. This is usually used to set it to -1 so the // kernel (at least with the ieee1394 stack) starts the streaming as // soon as possible, something that is required for some interfaces (eg: // RME). Note that as of 20 Dec 2010 it seems that ordinarily // m_switch_on_cycle remains fixed at 0 (its initialised value) because // requestEnable() doesn't set it. This allows the override configured // by this function to take effect. IsoHandler *h = getHandlerForStream(stream); h->setIsoStartCycle(cycle); } bool IsoHandlerManager::startHandlerForStream(Streaming::StreamProcessor *stream) { return startHandlerForStream(stream, -1); } bool IsoHandlerManager::startHandlerForStream(Streaming::StreamProcessor *stream, int cycle) { // check state if(m_State != E_Running) { debugError("Incorrect state, expected E_Running, got %s\n", eHSToString(m_State)); return false; } for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); it != m_IsoHandlers.end(); ++it ) { if((*it)->isStreamRegistered(stream)) { debugOutput( DEBUG_LEVEL_VERBOSE, " starting handler %p for stream %p\n", *it, stream); if(!(*it)->requestEnable(cycle)) { debugOutput( DEBUG_LEVEL_VERBOSE, " could not request enable for handler %p)\n",*it); return false; } if((*it)->getType() == IsoHandler::eHT_Transmit) { m_IsoTaskTransmit->requestShadowMapUpdate(); } else { m_IsoTaskReceive->requestShadowMapUpdate(); } debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " requested enable for handler %p\n", *it); return true; } } debugError("Stream %p has no attached handler\n", stream); return false; } bool IsoHandlerManager::stopHandlerForStream(Streaming::StreamProcessor *stream) { // check state if(m_State != E_Running) { debugError("Incorrect state, expected E_Running, got %s\n", eHSToString(m_State)); return false; } for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); it != m_IsoHandlers.end(); ++it ) { if((*it)->isStreamRegistered(stream)) { debugOutput( DEBUG_LEVEL_VERBOSE, " stopping handler %p for stream %p\n", *it, stream); if(!(*it)->requestDisable()) { debugOutput( DEBUG_LEVEL_VERBOSE, " could not request disable for handler %p\n",*it); return false; } if((*it)->getType() == IsoHandler::eHT_Transmit) { m_IsoTaskTransmit->requestShadowMapUpdate(); } else { m_IsoTaskReceive->requestShadowMapUpdate(); } debugOutput(DEBUG_LEVEL_VERBOSE, " requested disable for handler %p\n", *it); 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)->requestDisable()) { debugOutput( DEBUG_LEVEL_VERBOSE, " could not request disable for handler %p\n",*it); return false; } if((*it)->getType() == IsoHandler::eHT_Transmit) { m_IsoTaskTransmit->requestShadowMapUpdate(); } else { m_IsoTaskReceive->requestShadowMapUpdate(); } debugOutput(DEBUG_LEVEL_VERBOSE, " requested disable for handler %p\n", *it); } if (!retval) { m_State=E_Error; } return retval; } bool IsoHandlerManager::reset() { debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n"); // check state if(m_State == E_Error) { debugFatal("Resetting from error condition not yet supported...\n"); return false; } // if not in an error condition, reset means stop the handlers return stopHandlers(); } void IsoHandlerManager::setVerboseLevel(int i) { setDebugLevel(i); // propagate the debug level for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); it != m_IsoHandlers.end(); ++it ) { (*it)->setVerboseLevel(i); } if(m_IsoThreadTransmit) m_IsoThreadTransmit->setVerboseLevel(i); if(m_IsoTaskTransmit) m_IsoTaskTransmit->setVerboseLevel(i); if(m_IsoThreadReceive) m_IsoThreadReceive->setVerboseLevel(i); if(m_IsoTaskReceive) m_IsoTaskReceive->setVerboseLevel(i); setDebugLevel(i); debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", i ); } void IsoHandlerManager::dumpInfo() { #ifdef DEBUG unsigned int i=0; debugOutputShort( DEBUG_LEVEL_NORMAL, "Dumping IsoHandlerManager Stream handler information...\n"); debugOutputShort( DEBUG_LEVEL_NORMAL, " State: %d\n",(int)m_State); for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); it != m_IsoHandlers.end(); ++it ) { debugOutputShort( DEBUG_LEVEL_NORMAL, " IsoHandler %d (%p)\n",i++,*it); (*it)->dumpInfo(); } #endif } const char * IsoHandlerManager::eHSToString(enum eHandlerStates s) { switch (s) { default: return "Invalid"; case E_Created: return "Created"; case E_Running: return "Running"; case E_Error: return "Error"; } } // ISOHANDLER /* the C callbacks */ enum raw1394_iso_disposition IsoHandlerManager::IsoHandler::iso_transmit_handler(raw1394handle_t handle, unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, int cycle, unsigned int dropped1) { IsoHandler *xmitHandler = static_cast(raw1394_get_userdata(handle)); assert(xmitHandler); unsigned int skipped = (dropped1 & 0xFFFF0000) >> 16; unsigned int dropped = dropped1 & 0xFFFF; return xmitHandler->getPacket(data, length, tag, sy, cycle, dropped, skipped); } enum raw1394_iso_disposition IsoHandlerManager::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); } IsoHandlerManager::IsoHandler::IsoHandler(IsoHandlerManager& manager, enum EHandlerType t) : m_manager( manager ) , m_type ( t ) , m_handle( NULL ) , m_buf_packets( 400 ) , m_max_packet_size( 1024 ) , m_irq_interval( -1 ) , m_last_cycle( -1 ) , m_last_now( 0xFFFFFFFF ) , m_last_packet_handled_at( 0xFFFFFFFF ) , m_receive_mode ( RAW1394_DMA_PACKET_PER_BUFFER ) , m_Client( 0 ) , m_speed( RAW1394_ISO_SPEED_400 ) , m_State( eHS_Stopped ) , m_NextState( eHS_Stopped ) , m_switch_on_cycle(0) #ifdef DEBUG , m_packets ( 0 ) , m_dropped( 0 ) , m_skipped( 0 ) , m_min_ahead( 7999 ) #endif { pthread_mutex_init(&m_disable_lock, NULL); } IsoHandlerManager::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( NULL ) , m_buf_packets( buf_packets ) , m_max_packet_size( max_packet_size ) , m_irq_interval( irq ) , m_last_cycle( -1 ) , m_last_now( 0xFFFFFFFF ) , m_last_packet_handled_at( 0xFFFFFFFF ) , m_receive_mode ( RAW1394_DMA_PACKET_PER_BUFFER ) , m_Client( 0 ) , m_speed( RAW1394_ISO_SPEED_400 ) , m_State( eHS_Stopped ) , m_NextState( eHS_Stopped ) , m_switch_on_cycle(0) #ifdef DEBUG , m_packets ( 0 ) , m_dropped( 0 ) , m_skipped( 0 ) , m_min_ahead( 7999 ) #endif { pthread_mutex_init(&m_disable_lock, NULL); } IsoHandlerManager::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( NULL ) , m_buf_packets( buf_packets ) , m_max_packet_size( max_packet_size ) , m_irq_interval( irq ) , m_last_cycle( -1 ) , m_last_now( 0xFFFFFFFF ) , m_last_packet_handled_at( 0xFFFFFFFF ) , m_receive_mode ( RAW1394_DMA_PACKET_PER_BUFFER ) , m_Client( 0 ) , m_speed( speed ) , m_State( eHS_Stopped ) , m_NextState( eHS_Stopped ) , m_switch_on_cycle(0) #ifdef DEBUG , m_packets( 0 ) , m_dropped( 0 ) , m_skipped( 0 ) , m_min_ahead( 7999 ) #endif , m_deferred_cycles( 0 ) { pthread_mutex_init(&m_disable_lock, NULL); } IsoHandlerManager::IsoHandler::~IsoHandler() { // Don't call until libraw1394's raw1394_new_handle() function has been // fixed to correctly initialise the iso_packet_infos field. Bug is // confirmed present in libraw1394 1.2.1. In any case, // raw1394_destroy_handle() will do any iso system shutdown required. // raw1394_iso_shutdown(m_handle); // Typically, by the time this function is called the IsoTask thread would // have called disable() on the handler (in the FW_ISORCV/FW_ISOXMT // threads). However, the raw1394_destroy_handle() call therein takes // upwards of 20 milliseconds to complete under the new kernel FireWire // stack, and may not have completed by the time ~IsoHandler() is called by // the "jackd" thread. Thus, wait for the lock before testing the state // of the handle so any in-progress disable() is complete. if (pthread_mutex_trylock(&m_disable_lock) == EBUSY) { debugOutput(DEBUG_LEVEL_VERBOSE, "waiting for disable lock\n"); pthread_mutex_lock(&m_disable_lock); } pthread_mutex_unlock(&m_disable_lock); if(m_handle) { if (m_State == eHS_Running) { debugError("BUG: Handler still running!\n"); disable(); } } pthread_mutex_destroy(&m_disable_lock); } bool IsoHandlerManager::IsoHandler::canIterateClient() { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "checking...\n"); if (!isEnabled()) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "not enabled...\n"); return false; } if(m_Client) { bool result; if (m_type == eHT_Receive) { result = m_Client->canProducePacket(); } else { result = m_Client->canConsumePacket(); } debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " returns %d\n", result); return result; } else { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " no client\n"); } return false; } bool IsoHandlerManager::IsoHandler::iterate() { return iterate(m_manager.get1394Service().getCycleTimer()); } bool IsoHandlerManager::IsoHandler::iterate(uint32_t cycle_timer_now) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p, %s) Iterating ISO handler at %08X...\n", this, getTypeString(), cycle_timer_now); m_last_now = cycle_timer_now; if(m_State == eHS_Running) { assert(m_handle); #if ISOHANDLER_FLUSH_BEFORE_ITERATE // this flushes all packets received since the poll() returned // from kernel to userspace such that they are processed by this // iterate. Doing so might result in lower latency capability // and/or better reliability if(m_type == eHT_Receive) { raw1394_iso_recv_flush(m_handle); } #endif if(raw1394_loop_iterate(m_handle)) { debugError( "IsoHandler (%p): Failed to iterate handler: %s\n", this, strerror(errno)); return false; } debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p, %s) done interating ISO handler...\n", this, getTypeString()); return true; } else { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) Not iterating a non-running handler...\n", this, getTypeString()); return false; } } /** * Bus reset handler * * @return ? */ bool IsoHandlerManager::IsoHandler::handleBusReset() { debugOutput( DEBUG_LEVEL_NORMAL, "bus reset...\n"); m_last_packet_handled_at = 0xFFFFFFFF; #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 m_Client->handleBusReset(); } /** * Call this if you find out that this handler has died for some * external reason. */ void IsoHandlerManager::IsoHandler::notifyOfDeath() { if(m_handle) { // Make sure the stream is fully disabled. Some controllers (Ricoh // R5C832) will leave the stream in a limbo state after an unscheduled // stop, making it impossible to restart the stream, so make sure all // disabling steps are taken. disable(); } // notify the client of the fact that we have died m_Client->handlerDied(); // wake ourselves up if(m_handle) raw1394_wake_up(m_handle); } void IsoHandlerManager::IsoHandler::dumpInfo() { int channel=-1; if (m_Client) channel=m_Client->getChannel(); debugOutputShort( DEBUG_LEVEL_NORMAL, " Handler type................: %s\n", getTypeString()); debugOutputShort( DEBUG_LEVEL_NORMAL, " Port, Channel...............: %2d, %2d\n", m_manager.get1394Service().getPort(), channel); debugOutputShort( DEBUG_LEVEL_NORMAL, " Buffer, MaxPacketSize, IRQ..: %4d, %4d, %4d\n", m_buf_packets, m_max_packet_size, m_irq_interval); if (this->getType() == eHT_Transmit) { debugOutputShort( DEBUG_LEVEL_NORMAL, " Speed ..................: %2d\n", m_speed); #ifdef DEBUG debugOutputShort( DEBUG_LEVEL_NORMAL, " Min ISOXMT bufferfill : %04d\n", m_min_ahead); #endif } #ifdef DEBUG debugOutputShort( DEBUG_LEVEL_NORMAL, " Last cycle, dropped.........: %4d, %4u, %4u\n", m_last_cycle, m_dropped, m_skipped); #endif } void IsoHandlerManager::IsoHandler::setVerboseLevel(int l) { setDebugLevel(l); debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); } bool IsoHandlerManager::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 IsoHandlerManager::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; } // ISO packet interface enum raw1394_iso_disposition IsoHandlerManager::IsoHandler::putPacket( unsigned char *data, unsigned int length, unsigned char channel, unsigned char tag, unsigned char sy, unsigned int cycle, unsigned int dropped) { // keep track of dropped cycles int dropped_cycles = 0; if (m_last_cycle != (int)cycle && m_last_cycle != -1 && m_manager.m_MissedCyclesOK == false) { dropped_cycles = diffCycles(cycle, m_last_cycle) - 1; #ifdef DEBUG if (dropped_cycles < 0) { debugWarning("(%p) dropped < 1 (%d), cycle: %d, last_cycle: %d, dropped: %d\n", this, dropped_cycles, cycle, m_last_cycle, dropped); } if (dropped_cycles > 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%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; } #endif } m_last_cycle = cycle; // the m_last_now value is set when the iterate() function is called. uint32_t now_cycles = CYCLE_TIMER_GET_CYCLES(m_last_now); // two cases can occur: // (1) this packet has been received before iterate() was called (normal case). // (2) this packet has been received after iterate() was called. // happens when the kernel flushes more packets while we are already processing. // // In case (1) now_cycles is a small number of cycles larger than cycle. In // case (2) now_cycles is a small number of cycles smaller than cycle. // hence abs(diffCycles(now_cycles, cycles)) has to be 'small' // we can calculate the time of arrival for this packet as // 'now' + diffCycles(cycles, now_cycles) * TICKS_PER_CYCLE // in its properly wrapped version int64_t diff_cycles = diffCycles(cycle, now_cycles); int64_t tmp = CYCLE_TIMER_TO_TICKS(m_last_now); tmp += diff_cycles * (int64_t)TICKS_PER_CYCLE; uint64_t pkt_ctr_ticks = wrapAtMinMaxTicks(tmp); uint32_t pkt_ctr = TICKS_TO_CYCLE_TIMER(pkt_ctr_ticks); #ifdef DEBUG if( (now_cycles < cycle) && diffCycles(now_cycles, cycle) < 0 // ignore this on dropped cycles, since it's normal // that now is ahead on the received packets (as we miss packets) && dropped_cycles == 0) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Special non-unwrapping happened\n"); } #endif #if ISOHANDLER_CHECK_CTR_RECONSTRUCTION // add a seconds field uint32_t now = m_manager.get1394Service().getCycleTimer(); uint32_t now_secs_ref = CYCLE_TIMER_GET_SECS(now); // causality results in the fact that 'now' is always after 'cycle' // or at best, equal (if this handler was called within 125us after // the packet was on the wire). if(CYCLE_TIMER_GET_CYCLES(now) < cycle) { // the cycle field has wrapped, substract one second if(now_secs_ref == 0) { now_secs_ref = 127; } else { now_secs_ref -= 1; } } uint32_t pkt_ctr_ref = cycle << 12; pkt_ctr_ref |= (now_secs_ref & 0x7F) << 25; if((pkt_ctr & ~0x0FFFL) != pkt_ctr_ref) { debugWarning("reconstructed CTR counter discrepancy\n"); debugWarning(" ingredients: %X, %X, %X, %X, %X, %d, %ld, %ld, %" PRId64 "\n", cycle, pkt_ctr_ref, pkt_ctr, now, m_last_now, now_secs_ref, (long int)CYCLE_TIMER_GET_SECS(now), (long int)CYCLE_TIMER_GET_SECS(m_last_now), tmp); debugWarning(" diffcy = %" PRId64 " \n", diff_cycles); } #endif m_last_packet_handled_at = pkt_ctr; // leave the offset field (for now?) debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, "received packet: length=%d, channel=%d, cycle=%d, at %08X\n", length, channel, cycle, pkt_ctr); m_packets++; #ifdef DEBUG if (length > m_max_packet_size) { debugWarning("(%p, %s) packet too large: len=%u max=%u\n", this, getTypeString(), length, m_max_packet_size); } if(m_last_cycle == -1) { debugOutput(DEBUG_LEVEL_VERBOSE, "Handler for %s SP %p is alive (cycle = %u)\n", getTypeString(), this, cycle); } #endif // iterate the client if required if(m_Client) return m_Client->putPacket(data, length, channel, tag, sy, pkt_ctr, dropped_cycles); return RAW1394_ISO_OK; } enum raw1394_iso_disposition IsoHandlerManager::IsoHandler::getPacket(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, int cycle, unsigned int dropped, unsigned int skipped) { uint32_t pkt_ctr; if (cycle < 0) { // mark invalid pkt_ctr = 0xFFFFFFFF; } else { // the m_last_now value is set when the iterate() function is called. uint32_t now_cycles = CYCLE_TIMER_GET_CYCLES(m_last_now); // two cases can occur: // (1) this packet has been received before iterate() was called (normal case). // (2) this packet has been received after iterate() was called. // happens when the kernel flushes more packets while we are already processing. // // In case (1) now_cycles is a small number of cycles larger than cycle. In // case (2) now_cycles is a small number of cycles smaller than cycle. // hence abs(diffCycles(now_cycles, cycles)) has to be 'small' // we can calculate the time of arrival for this packet as // 'now' + diffCycles(cycles, now_cycles) * TICKS_PER_CYCLE // in its properly wrapped version int64_t diff_cycles = diffCycles(cycle, now_cycles); int64_t tmp = CYCLE_TIMER_TO_TICKS(m_last_now); tmp += diff_cycles * (int64_t)TICKS_PER_CYCLE; uint64_t pkt_ctr_ticks = wrapAtMinMaxTicks(tmp); pkt_ctr = TICKS_TO_CYCLE_TIMER(pkt_ctr_ticks); //debugOutput(DEBUG_LEVEL_VERBOSE, "cy=%d, now_cy=%d, diff_cy=%lld, tmp=%lld, pkt_ctr_ticks=%lld, pkt_ctr=%d\n", // cycle, now_cycles, diff_cycles, tmp, pkt_ctr_ticks, pkt_ctr); #if ISOHANDLER_CHECK_CTR_RECONSTRUCTION // add a seconds field uint32_t now = m_manager.get1394Service().getCycleTimer(); uint32_t now_secs_ref = CYCLE_TIMER_GET_SECS(now); // causality results in the fact that 'now' is always after 'cycle' if(CYCLE_TIMER_GET_CYCLES(now) > (unsigned int)cycle) { // the cycle field has wrapped, add one second now_secs_ref += 1; // no need for this: if(now_secs_ref == 128) { now_secs_ref = 0; } } uint32_t pkt_ctr_ref = cycle << 12; pkt_ctr_ref |= (now_secs_ref & 0x7F) << 25; if(((pkt_ctr & ~0x0FFFL) != pkt_ctr_ref) && (m_packets > m_buf_packets)) { debugWarning("reconstructed CTR counter discrepancy\n"); debugWarning(" ingredients: %X, %X, %X, %X, %X, %d, %ld, %ld, %" PRId64 "\n", cycle, pkt_ctr_ref, pkt_ctr, now, m_last_now, now_secs_ref, (long int)CYCLE_TIMER_GET_SECS(now), (long int)CYCLE_TIMER_GET_SECS(m_last_now), tmp); debugWarning(" diffcy = %" PRId64 " \n", diff_cycles); } #endif } if (m_packets < m_buf_packets) { // these are still prebuffer packets m_last_packet_handled_at = 0xFFFFFFFF; } else { m_last_packet_handled_at = pkt_ctr; } debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, "sending packet: length=%d, cycle=%d, at %08X\n", *length, cycle, pkt_ctr); m_packets++; #ifdef DEBUG if(m_last_cycle == -1) { debugOutput(DEBUG_LEVEL_VERBOSE, "Handler for %s SP %p is alive. cycle=%d state=%i\n", getTypeString(), this, cycle, m_State); } #endif if (m_last_cycle == -1) m_deferred_cycles = 0; // keep track of dropped cycles int dropped_cycles = 0; if (m_last_cycle != cycle && m_last_cycle != -1) { dropped_cycles = diffCycles(cycle, m_last_cycle) - 1; // correct for skipped packets // since those are not dropped, but only delayed dropped_cycles -= skipped; // Correct for cycles previously seen but deferred if (dropped_cycles == 0) m_deferred_cycles = 0; else dropped_cycles -= m_deferred_cycles; #ifdef DEBUG if(skipped) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "(%p) skipped %d cycles, cycle: %d, last_cycle: %d, dropped: %d\n", this, skipped, cycle, m_last_cycle, dropped); m_skipped += skipped; } if (dropped_cycles < 0) { debugWarning("(%p) dropped < 1 (%d), cycle: %d, last_cycle: %d, dropped: %d, skipped: %d\n", this, dropped_cycles, cycle, m_last_cycle, dropped, skipped); } if (dropped_cycles > 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) dropped %d packets on cycle %u (last_cycle=%u, dropped=%d, skipped: %d)\n", this, dropped_cycles, cycle, m_last_cycle, dropped, skipped); m_dropped += dropped_cycles - skipped; } #endif } #ifdef DEBUG // if (cycle >= 0) { // int ahead = diffCycles(cycle, now_cycles); // if (ahead < m_min_ahead) m_min_ahead = ahead; // } if (dropped > 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) OHCI issue on cycle %u (dropped_cycles=%d, last_cycle=%u, dropped=%d, skipped: %d)\n", this, cycle, dropped_cycles, m_last_cycle, dropped, skipped); } #endif if(m_Client) { enum raw1394_iso_disposition retval; retval = m_Client->getPacket(data, length, tag, sy, pkt_ctr, dropped_cycles, skipped, m_max_packet_size); #ifdef DEBUG if (*length > m_max_packet_size) { debugWarning("(%p, %s) packet too large: len=%u max=%u\n", this, getTypeString(), *length, m_max_packet_size); } #endif if (cycle >= 0) { if (retval!=RAW1394_ISO_DEFER && retval!=RAW1394_ISO_AGAIN) { m_last_cycle = cycle; } else m_deferred_cycles++; } return retval; } if (cycle >= 0) m_last_cycle = cycle; *tag = 0; *sy = 0; *length = 0; return RAW1394_ISO_OK; } bool IsoHandlerManager::IsoHandler::enable(int cycle) { debugOutput( DEBUG_LEVEL_VERBOSE, "start on cycle %d\n", cycle); // check the state if(m_State != eHS_Stopped) { debugError("Incorrect state, expected eHS_Stopped, got %d\n",(int)m_State); return false; } assert(m_handle == NULL); // create a 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)); // Reset housekeeping data before preparing and starting the handler. // If only done afterwards, the transmit handler could be called before // these have been reset, leading to problems in getPacket(). #ifdef DEBUG m_min_ahead = 7999; #endif m_packets = 0; m_last_cycle = -1; // indicate that the first iterate() still has to occur. m_last_now = 0xFFFFFFFF; m_last_packet_handled_at = 0xFFFFFFFF; // prepare the handler, allocate the resources debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing iso handler (%p, client=%p)\n", this, m_Client); dumpInfo(); if (getType() == eHT_Receive) { if(raw1394_iso_recv_init(m_handle, iso_receive_handler, m_buf_packets, m_max_packet_size, m_Client->getChannel(), m_receive_mode, m_irq_interval)) { debugFatal("Could not do receive initialization (PACKET_PER_BUFFER)!\n" ); debugFatal(" %s\n",strerror(errno)); return false; } 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_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; } if(raw1394_iso_xmit_start(m_handle, cycle, 0)) { debugFatal("Could not start xmit handler (%s)\n", strerror(errno)); dumpInfo(); return false; } } m_State = eHS_Running; m_NextState = eHS_Running; return true; } bool IsoHandlerManager::IsoHandler::disable() { signed int i, have_lock = 0; debugOutput( DEBUG_LEVEL_VERBOSE, "(%p, %s) enter...\n", this, (m_type==eHT_Receive?"Receive":"Transmit")); i = pthread_mutex_trylock(&m_disable_lock); if (i == 0) have_lock = 1; else if (i == EBUSY) { // Some other thread is disabling this handler, a process which can // take considerable time when using the new kernel FireWire stack. // Wait until it is finished before returning so the present caller // can act knowing that the disable has occurred and is complete // (which is what normally would be expected). debugOutput( DEBUG_LEVEL_VERBOSE, "waiting for disable lock\n"); pthread_mutex_lock(&m_disable_lock); debugOutput( DEBUG_LEVEL_VERBOSE, "now have disable lock\n"); if (m_State == eHS_Stopped) { debugOutput( DEBUG_LEVEL_VERBOSE, "another disable() has completed\n"); pthread_mutex_unlock(&m_disable_lock); return true; } have_lock = 1; } // check state if(m_State != eHS_Running) { debugError("Incorrect state, expected eHS_Running, got %d\n",(int)m_State); if (have_lock) pthread_mutex_unlock(&m_disable_lock); return false; } assert(m_handle != NULL); debugOutput( DEBUG_LEVEL_VERBOSE, "(%p, %s) wake up handle...\n", this, (m_type==eHT_Receive?"Receive":"Transmit")); // wake up any waiting reads/polls raw1394_wake_up(m_handle); debugOutput( DEBUG_LEVEL_VERBOSE, "(%p, %s) stop...\n", this, (m_type==eHT_Receive?"Receive":"Transmit")); // stop iso traffic raw1394_iso_stop(m_handle); // deallocate resources // 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); // When running on the new kernel FireWire stack, this call can take of // the order of 20 milliseconds to return, in which time other threads // may wish to test the state of the handler and call this function // themselves. The m_disable_lock mutex is used to work around this. raw1394_destroy_handle(m_handle); m_handle = NULL; m_State = eHS_Stopped; m_NextState = eHS_Stopped; m_Client->packetsStopped(); if (have_lock) pthread_mutex_unlock(&m_disable_lock); return true; } // functions to request enable or disable at the next opportunity bool IsoHandlerManager::IsoHandler::requestEnable(int cycle) { if (m_State == eHS_Running) { debugError("Enable requested on enabled stream '%s'\n", getTypeString()); return false; } if (m_State != eHS_Stopped) { debugError("Enable requested on stream '%s' with state: %d\n", getTypeString(), m_State); return false; } m_NextState = eHS_Running; return true; } bool IsoHandlerManager::IsoHandler::requestDisable() { if (m_State == eHS_Stopped) { // Don't treat this as an error condition because during a user // shutdown the stream would have been disabled by // stopHandlerForStream(). Therefore when requestDisable() is // subnsequently called by IsoHandlerManager::stopHandlers() in the // IsoHandlerManager destructor with the stream disabled the // condition is not an error. // // For now print a warning, but this might be removed in future if // the above framework remains in place. debugWarning("Disable requested on disabled stream\n"); return true; } if (m_State != eHS_Running) { debugError("Disable requested on stream with state=%d\n", m_State); return false; } m_NextState = eHS_Stopped; return true; } // Explicitly preset m_switch_on_cycle since requestEnable doesn't do this // and thus all enables requested via that route always occur on cycle 0. void IsoHandlerManager::IsoHandler::setIsoStartCycle(signed int cycle) { m_switch_on_cycle = cycle; } void IsoHandlerManager::IsoHandler::updateState() { // execute state changes requested if(m_State != m_NextState) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) handler needs state update from %d => %d\n", this, m_State, m_NextState); if(m_State == eHS_Stopped && m_NextState == eHS_Running) { debugOutput(DEBUG_LEVEL_VERBOSE, "handler has to be enabled\n"); enable(m_switch_on_cycle); } else if(m_State == eHS_Running && m_NextState == eHS_Stopped) { debugOutput(DEBUG_LEVEL_VERBOSE, "handler has to be disabled\n"); disable(); } else { debugError("Unknown state transition\n"); } } } /** * @brief convert a EHandlerType to a string * @param t the type * @return a char * describing the state */ const char * IsoHandlerManager::IsoHandler::eHTToString(enum EHandlerType t) { switch (t) { case eHT_Receive: return "Receive"; case eHT_Transmit: return "Transmit"; default: return "error: unknown type"; } } libffado-2.4.5/src/libieee1394/IsoHandlerManager.h0000644000175000001440000003534114206145246021074 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_ISOHANDLERMANAGER__ #define __FFADO_ISOHANDLERMANAGER__ #include "config.h" #include "debugmodule/debugmodule.h" #include "libutil/Thread.h" #include #include #include #include class Ieee1394Service; //class IsoHandler; //enum IsoHandler::EHandlerType; namespace Streaming { class StreamProcessor; typedef std::vector StreamProcessorVector; typedef std::vector::iterator StreamProcessorVectorIterator; } /*! \brief The ISO Handler management class This class manages the use of ISO handlers by ISO streams. You can register an Streaming::StreamProcessor with an IsoHandlerManager. This manager will assign an IsoHandler to the stream. If nescessary the manager allocates a new handler. If there is already a handler that can handle the Streaming::StreamProcessor (e.g. in case of multichannel receive), it can be assigned. */ class IsoHandlerManager { friend class IsoTask; //// /*! \brief The Base Class for ISO Handlers These classes perform the actual ISO communication through libraw1394. They are different from Streaming::StreamProcessors because one handler can provide multiple streams with packets in case of ISO multichannel receive. */ class IsoHandler { public: enum EHandlerType { eHT_Receive, eHT_Transmit }; IsoHandler(IsoHandlerManager& manager, enum EHandlerType t); IsoHandler(IsoHandlerManager& manager, enum EHandlerType t, unsigned int buf_packets, unsigned int max_packet_size, int irq); IsoHandler(IsoHandlerManager& manager, enum EHandlerType t, unsigned int buf_packets, unsigned int max_packet_size, int irq, enum raw1394_iso_speed speed); ~IsoHandler(); private: // the ISO callback interface static enum raw1394_iso_disposition iso_receive_handler(raw1394handle_t handle, unsigned char *data, unsigned int length, unsigned char channel, unsigned char tag, unsigned char sy, unsigned int cycle, unsigned int dropped); enum raw1394_iso_disposition putPacket(unsigned char *data, unsigned int length, unsigned char channel, unsigned char tag, unsigned char sy, unsigned int cycle, unsigned int dropped); static enum raw1394_iso_disposition iso_transmit_handler(raw1394handle_t handle, unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, int cycle, unsigned int dropped); enum raw1394_iso_disposition getPacket(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, int cycle, unsigned int dropped, unsigned int skipped); public: /** * Iterate the handler, transporting ISO packets to the client(s) * @return true if success */ bool iterate(); /** * Iterate the handler, transporting ISO packets to the client(s) * @param ctr_now the CTR time at which the iterate call is done. * @return true if success */ bool iterate(uint32_t ctr_now); int getFileDescriptor() { return raw1394_get_fd(m_handle);}; bool init(); void setVerboseLevel(int l); // the enable/disable functions should only be used from within the loop that iterates() // but not from within the iterate callback. use the requestEnable / requestDisable functions // for that bool enable() {return enable(-1);}; bool enable(int cycle); bool disable(); // functions to request enable or disable at the next opportunity bool requestEnable(int cycle = -1); bool requestDisable(); // Manually set the start cycle for the iso handler void setIsoStartCycle(signed int cycle = -1); /** * updates the internal state if required */ void updateState(); enum EHandlerType getType() {return m_type;}; const char *getTypeString() {return eHTToString(m_type); }; // pretty printing const char *eHTToString(enum EHandlerType); bool isEnabled() {return m_State == eHS_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 getIrqInterval() { return m_irq_interval;}; void dumpInfo(); bool inUse() {return (m_Client != 0) ;}; bool isStreamRegistered(Streaming::StreamProcessor *s) {return (m_Client == s);}; bool registerStream(Streaming::StreamProcessor *); bool unregisterStream(Streaming::StreamProcessor *); bool canIterateClient(); // FIXME: implement with functor /** * @brief get last cycle number seen by handler * @return cycle number */ int getLastCycle() {return m_last_cycle;}; /** * @brief returns the CTR value saved at the last iterate() call * @return CTR value saved at last iterate() call */ uint32_t getLastIterateTime() {return m_last_now;}; /** * @brief returns the CTR value saved at the last iterate handler call * @return CTR value saved at last iterate handler call */ uint32_t getLastPacketTime() {return m_last_packet_handled_at;}; /** * @brief set iso receive mode. doesn't have any effect if the stream is running * @param m receive mode */ void setReceiveMode(enum raw1394_iso_dma_recv_mode m) {m_receive_mode = m;} void notifyOfDeath(); bool handleBusReset(); private: IsoHandlerManager& m_manager; enum EHandlerType m_type; raw1394handle_t m_handle; unsigned int m_buf_packets; unsigned int m_max_packet_size; int m_irq_interval; int m_last_cycle; uint32_t m_last_now; uint32_t m_last_packet_handled_at; enum raw1394_iso_dma_recv_mode m_receive_mode; Streaming::StreamProcessor *m_Client; // FIXME: implement with functors enum raw1394_iso_speed m_speed; // the state machine enum EHandlerStates { eHS_Stopped, eHS_Running, }; enum EHandlerStates m_State; enum EHandlerStates m_NextState; int m_switch_on_cycle; pthread_mutex_t m_disable_lock; public: unsigned int m_packets; #ifdef DEBUG unsigned int m_dropped; unsigned int m_skipped; int m_min_ahead; #endif unsigned int m_deferred_cycles; protected: DECLARE_DEBUG_MODULE; }; typedef std::vector IsoHandlerVector; typedef std::vector::iterator IsoHandlerVectorIterator; //// // threads that will handle the packet framing // one thread per direction, as a compromise for one per // channel and one for all class IsoTask : public Util::RunnableInterface { friend class IsoHandlerManager; public: IsoTask(IsoHandlerManager& manager, enum IsoHandler::EHandlerType); virtual ~IsoTask(); private: bool Init(); bool Execute(); /** * @brief requests the thread to sync it's stream map with the manager */ void requestShadowMapUpdate(); enum eActivityResult { eAR_Activity, eAR_Timeout, eAR_Interrupted, eAR_Error }; /** * @brief signals that something happened in one of the clients of this task */ void signalActivity(); /** * @brief wait until something happened in one of the clients of this task */ enum eActivityResult waitForActivity(); /** * @brief This should be called when a busreset has happened. */ bool handleBusReset(); void setVerboseLevel(int i); protected: IsoHandlerManager& m_manager; // the event request structure int32_t request_update; // static allocation due to RT constraints // this is the map used by the actual thread // it is a shadow of the m_StreamProcessors vector struct pollfd m_poll_fds_shadow[ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT]; IsoHandler * m_IsoHandler_map_shadow[ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT]; unsigned int m_poll_nfds_shadow; IsoHandler * m_SyncIsoHandler; // updates the streams map void updateShadowMapHelper(); #ifdef DEBUG uint64_t m_last_loop_entry; int m_successive_short_loops; #endif enum IsoHandler::EHandlerType m_handlerType; bool m_running; bool m_in_busreset; // activity signaling sem_t m_activity_semaphore; long long int m_activity_wait_timeout_nsec; // debug stuff DECLARE_DEBUG_MODULE; }; //// the IsoHandlerManager itself 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 void dumpInfoForStream(Streaming::StreamProcessor *); ///< print some info about the stream's handler 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(); /** * @brief signals that something happened in one of the clients */ void signalActivityTransmit(); void signalActivityReceive(); ///> disables the handler attached to the stream bool stopHandlerForStream(Streaming::StreamProcessor *); ///> starts the handler attached to the specific stream bool startHandlerForStream(Streaming::StreamProcessor *); ///> starts the handler attached to the specific stream on a specific cycle bool startHandlerForStream(Streaming::StreamProcessor *, int cycle); ///> Directly tells the handler attached to the stream to start on ///> the given cycle regardless of what is passed to ///> startHandlerForStream(). void setIsoStartCycleForStream(Streaming::StreamProcessor *stream, signed 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 *); /** * Enables the isohandler manager to ignore missed packets. This * behaviour is needed by some interfaces which don't send empty * placeholder packets when no data needs to be sent. */ void setMissedCyclesOK(bool ok) { m_MissedCyclesOK = ok; }; private: IsoHandler * getHandlerForStream(Streaming::StreamProcessor *stream); void requestShadowMapUpdate(); public: Ieee1394Service& get1394Service() {return m_service;}; /** * This should be called when a busreset has happened. */ bool handleBusReset(); // the state machine private: enum eHandlerStates { E_Created, E_Running, E_Error }; enum eHandlerStates m_State; const char *eHSToString(enum eHandlerStates); private: Ieee1394Service& m_service; // note: there is a disctinction between streams and handlers // because one handler can serve multiple streams (in case of // multichannel receive) // only streams are allowed to be registered externally. // we allocate a handler if we need one, otherwise the stream // is assigned to another handler // the collection of handlers IsoHandlerVector m_IsoHandlers; bool registerHandler(IsoHandler *); bool unregisterHandler(IsoHandler *); void pruneHandlers(); // the collection of streams Streaming::StreamProcessorVector m_StreamProcessors; // handler thread/task bool m_realtime; int m_priority; Util::Thread * m_IsoThreadTransmit; IsoTask * m_IsoTaskTransmit; Util::Thread * m_IsoThreadReceive; IsoTask * m_IsoTaskReceive; bool m_MissedCyclesOK; // debug stuff DECLARE_DEBUG_MODULE; }; #endif /* __FFADO_ISOHANDLERMANAGER__ */ libffado-2.4.5/src/libieee1394/configrom.cpp0000644000175000001440000005570714206145246020077 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "configrom.h" #include "ieee1394service.h" #include "vendor_model_ids.h" #include "libutil/SystemTimeSource.h" #include #include #include #include using namespace std; IMPL_DEBUG_MODULE( ConfigRom, ConfigRom, DEBUG_LEVEL_NORMAL ); static int busRead( struct csr1212_csr* csr, u_int64_t addr, u_int16_t length, void* buffer, void* private_data ); static int getMaxRom( u_int32_t* bus_info_data, void* private_data ); static struct csr1212_bus_ops configrom_csr1212_ops = { busRead, 0, 0, getMaxRom }; struct config_csr_info { Ieee1394Service* service; fb_nodeid_t nodeId; }; //------------------------------------------------------------- ConfigRom::ConfigRom( Ieee1394Service& ieee1394service, fb_nodeid_t nodeId ) : Control::Element(NULL, "ConfigRom") , m_1394Service( ieee1394service ) , m_nodeId( nodeId ) , m_avcDevice( false ) // FIXME: this does not seem veryu , m_guid( 0 ) , m_vendorName( "" ) , m_modelName( "" ) , m_vendorId( 0 ) , m_modelId( 0 ) , m_unit_specifier_id( 0 ) , m_unit_version( 0 ) , m_isIsoResourceManager( false ) , m_isCycleMasterCapable( false ) , m_isSupportIsoOperations( false ) , m_isBusManagerCapable( false ) , m_cycleClkAcc( 0 ) , m_maxRec( 0 ) , m_nodeVendorId( 0 ) , m_chipIdHi( 0 ) , m_chipIdLow( 0 ) , m_vendorNameKv( 0 ) , m_modelNameKv( 0 ) , m_csr( 0 ) { } ConfigRom::ConfigRom() : Control::Element(NULL, "ConfigRom") , m_1394Service( *(new Ieee1394Service()) ) , m_nodeId( -1 ) , m_avcDevice( false ) // FIXME: this does not seem veryu , m_guid( 0 ) , m_vendorName( "" ) , m_modelName( "" ) , m_vendorId( 0 ) , m_modelId( 0 ) , m_unit_specifier_id( 0 ) , m_unit_version( 0 ) , m_isIsoResourceManager( false ) , m_isCycleMasterCapable( false ) , m_isSupportIsoOperations( false ) , m_isBusManagerCapable( false ) , m_cycleClkAcc( 0 ) , m_maxRec( 0 ) , m_nodeVendorId( 0 ) , m_chipIdHi( 0 ) , m_chipIdLow( 0 ) , m_vendorNameKv( 0 ) , m_modelNameKv( 0 ) , m_csr( 0 ) { } Ieee1394Service& ConfigRom::get1394Service() { return m_1394Service; } bool ConfigRom::operator == ( const ConfigRom& rhs ) { return m_guid == rhs.m_guid; } bool ConfigRom::compareGUID( const ConfigRom& a, const ConfigRom& b ) { return a.getGuid() > b.getGuid(); } bool ConfigRom::initialize() { struct config_csr_info csr_info; csr_info.service = &m_1394Service; csr_info.nodeId = 0xffc0 | m_nodeId; m_csr = csr1212_create_csr( &configrom_csr1212_ops, 5 * sizeof(fb_quadlet_t), // XXX Why 5 ?!? &csr_info ); if (!m_csr || csr1212_parse_csr( m_csr ) != CSR1212_SUCCESS) { debugOutput(DEBUG_LEVEL_INFO, "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--; } // Ensure vendor string is null terminated 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--; } // Ensure model name string is null terminated buf[len] = '\0'; debugOutput( DEBUG_LEVEL_VERBOSE, "Model name: '%s'\n", buf); m_modelName = buf; delete[] buf; } m_guid = ((u_int64_t)CSR1212_BE32_TO_CPU(m_csr->bus_info_data[3]) << 32) | CSR1212_BE32_TO_CPU(m_csr->bus_info_data[4]); if ( m_vendorNameKv ) { csr1212_release_keyval( m_vendorNameKv ); m_vendorNameKv = 0; } if ( m_modelNameKv ) { csr1212_release_keyval( m_modelNameKv ); m_modelNameKv = 0; } if ( m_csr ) { csr1212_destroy_csr(m_csr); m_csr = 0; } return true; } static int busRead( struct csr1212_csr* csr, u_int64_t addr, u_int16_t length, void* buffer, void* private_data ) { struct config_csr_info* csr_info = (struct config_csr_info*) private_data; int nb_retries = 5; while ( nb_retries-- && !csr_info->service->read( csr_info->nodeId, addr, (size_t)length/4, ( quadlet_t* )buffer) ) {// failed, retry Util::SystemTimeSource::SleepUsecRelative(IEEE1394SERVICE_CONFIGROM_READ_WAIT_USECS); } Util::SystemTimeSource::SleepUsecRelative(IEEE1394SERVICE_CONFIGROM_READ_WAIT_USECS); if (nb_retries > -1) return 0; // success else return -1; // failure } static int getMaxRom( u_int32_t* bus_info_data, void* /*private_data*/) { return (CSR1212_BE32_TO_CPU( bus_info_data[2] ) >> 8) & 0x3; } void ConfigRom::processUnitDirectory( struct csr1212_csr* csr, struct csr1212_keyval* ud_kv, unsigned int *id ) { struct csr1212_dentry *dentry; struct csr1212_keyval *kv; unsigned int last_key_id = 0; debugOutput( DEBUG_LEVEL_VERBOSE, "process unit directory:\n" ); csr1212_for_each_dir_entry(csr, kv, ud_kv, dentry) { switch (kv->key.id) { case CSR1212_KV_ID_VENDOR: if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) { debugOutput( DEBUG_LEVEL_VERBOSE, "\tvendor_id = 0x%08x\n", kv->value.immediate); m_vendorId = kv->value.immediate; } break; case CSR1212_KV_ID_MODEL: debugOutput( DEBUG_LEVEL_VERBOSE, "\tmodel_id = 0x%08x\n", kv->value.immediate); m_modelId = kv->value.immediate; break; case CSR1212_KV_ID_SPECIFIER_ID: debugOutput( DEBUG_LEVEL_VERBOSE, "\tspecifier_id = 0x%08x\n", kv->value.immediate); m_unit_specifier_id = kv->value.immediate; break; case CSR1212_KV_ID_VERSION: debugOutput( DEBUG_LEVEL_VERBOSE, "\tversion = 0x%08x\n", kv->value.immediate); m_unit_version = kv->value.immediate; if ( m_unit_specifier_id == 0x0000a02d ) // XXX { m_avcDevice = true; // FIXME: disable this check for the moment if ( kv->value.immediate == 0x14001 ) { m_avcDevice = true; } } break; case CSR1212_KV_ID_DESCRIPTOR: if (kv->key.type == CSR1212_KV_TYPE_LEAF && CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 && CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 && CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 && CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 && CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) { switch (last_key_id) { case CSR1212_KV_ID_VENDOR: csr1212_keep_keyval(kv); m_vendorNameKv = kv; break; case CSR1212_KV_ID_MODEL: m_modelNameKv = kv; csr1212_keep_keyval(kv); break; } } /* else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) ... */ break; case CSR1212_KV_ID_DEPENDENT_INFO: if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) { /* This should really be done in SBP2 as this is * doing SBP2 specific parsing. */ processUnitDirectory(csr, kv, id); } break; default: break; } last_key_id = kv->key.id; } } void ConfigRom::processRootDirectory(struct csr1212_csr* csr) { unsigned int ud_id = 0; struct csr1212_dentry *dentry; struct csr1212_keyval *kv; unsigned int last_key_id = 0; csr1212_for_each_dir_entry(csr, kv, csr->root_kv, dentry) { switch (kv->key.id) { case CSR1212_KV_ID_VENDOR: debugOutput( DEBUG_LEVEL_VERBOSE, "vendor id = 0x%08x\n", kv->value.immediate); break; case CSR1212_KV_ID_NODE_CAPABILITIES: debugOutput( DEBUG_LEVEL_VERBOSE, "capabilities = 0x%08x\n", kv->value.immediate); break; case CSR1212_KV_ID_UNIT: processUnitDirectory(csr, kv, &ud_id); break; case CSR1212_KV_ID_DESCRIPTOR: if (last_key_id == CSR1212_KV_ID_VENDOR) { if (kv->key.type == CSR1212_KV_TYPE_LEAF && CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 && CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 && CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 && CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 && CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) { m_vendorNameKv = kv; csr1212_keep_keyval(kv); } } break; } last_key_id = kv->key.id; } } const fb_nodeid_t ConfigRom::getNodeId() const { return m_nodeId; } const fb_octlet_t ConfigRom::getGuid() const { return m_guid; } const std::string ConfigRom::getGuidString() const { char* buf; asprintf( &buf, "%08x%08x", ( unsigned int ) ( getGuid() >> 32 ), ( unsigned int ) ( getGuid() & 0xffffffff ) ); std::string result = buf; free( buf ); return result; } const std::string ConfigRom::getModelName() const { // HACK: // workarounds for devices that don't fill a correct model name switch(m_vendorId) { case FW_VENDORID_MOTU: switch(m_unit_specifier_id) { case 0x00000001: return "828MkI"; case 0x00000003: return "828MkII"; case 0x00000005: return "896HD"; case 0x00000009: return "Traveler"; case 0x0000000d: return "UltraLite"; case 0x0000000f: return "8pre"; case 0x00000015: return "828Mk3"; case 0x00000017: return "896Mk3"; case 0x00000019: return "UltraliteMk3"; case 0x0000001b: return "TravelerMk3"; case 0x00000021: return "V4HD subdevice 0"; case 0x00000022: return "V4HD subdevice 1"; case 0x00000023: return "V4HD subdevice 2"; case 0x00000024: return "V4HD subdevice 3"; case 0x00000030: return "UltraLiteMk3-hybrid"; default: return "unknown"; } break; default: break; } // Note: m_nodeVendorId is not the same as m_vendorId; some devices use // one, others use the other. switch (m_nodeVendorId) { case FW_VENDORID_RME: switch (m_unit_version) { case 0x0001: return "Fireface 800"; case 0x0002: return "Fireface 400"; default: return "unknown"; } break; default: break; } return m_modelName; } const std::string ConfigRom::getVendorName() const { // HACK: // workarounds for devices that don't fill a correct vendor name switch(m_vendorId) { case FW_VENDORID_MOTU: return "MOTU"; default: break; } switch (m_nodeVendorId) { case FW_VENDORID_RME: return "RME"; default: break; } 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%016" PRIX64 "...\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%016" PRIX64 "\n", guid); if ( guid == getGuid() ) { debugOutput( DEBUG_LEVEL_VERBOSE, "GUID matches ours\n"); if ( nodeId != getNodeId() ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Device with GUID 0x%016" PRIX64 " changed node id " "from %d to %d\n", getGuid(), getNodeId(), nodeId ); m_nodeId = nodeId; } else { debugOutput( DEBUG_LEVEL_VERBOSE, "Device with GUID 0x%016" PRIX64 " 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_VERBOSE, "Device with GUID 0x%016" PRIX64 " could not be found on " "the bus anymore (removed?)\n", getGuid() ); m_nodeId = INVALID_NODE_ID; return false; } void ConfigRom::printConfigRomDebug() const { using namespace std; debugOutput(DEBUG_LEVEL_NORMAL, "Config ROM\n" ); debugOutput(DEBUG_LEVEL_NORMAL, "\tCurrent Node Id:\t%d\n", getNodeId() ); debugOutput(DEBUG_LEVEL_NORMAL, "\tGUID:\t\t\t0x%016" PRIX64 "\n", getGuid()); debugOutput(DEBUG_LEVEL_NORMAL, "\tVendor Name:\t\t%s\n", getVendorName().c_str() ); debugOutput(DEBUG_LEVEL_NORMAL, "\tModel Name:\t\t%s\n", getModelName().c_str() ); debugOutput(DEBUG_LEVEL_NORMAL, "\tNode Vendor ID:\t\t0x%06x\n", getNodeVendorId() ); debugOutput(DEBUG_LEVEL_NORMAL, "\tModel Id:\t\t0x%08x\n", getModelId() ); debugOutput(DEBUG_LEVEL_NORMAL, "\tUnit Specifier ID:\t0x%06x\n", getUnitSpecifierId() ); debugOutput(DEBUG_LEVEL_NORMAL, "\tUnit version:\t\t0x%08x\n", getUnitVersion() ); debugOutput(DEBUG_LEVEL_NORMAL, "\tISO resource manager:\t%d\n", isIsoResourseManager() ); debugOutput(DEBUG_LEVEL_NORMAL, "\tCycle master capable:\t%d\n", isSupportsIsoOperations() ); debugOutput(DEBUG_LEVEL_NORMAL, "\tBus manager capable:\t%d\n", isBusManagerCapable() ); debugOutput(DEBUG_LEVEL_NORMAL, "\tCycle clock accuracy:\t%d\n", getCycleClockAccurancy() ); debugOutput(DEBUG_LEVEL_NORMAL, "\tMax rec:\t\t%d (max asy payload: %d bytes)\n", getMaxRec(), getAsyMaxPayload() ); } void ConfigRom::printConfigRom() const { using namespace std; printMessage("Config ROM\n" ); printMessage("\tCurrent Node Id:\t%d\n", getNodeId() ); printMessage("\tGUID:\t\t\t0x%016" PRIX64 "\n", getGuid()); printMessage("\tVendor Name:\t\t%s\n", getVendorName().c_str() ); printMessage("\tModel Name:\t\t%s\n", getModelName().c_str() ); printMessage("\tNode Vendor ID:\t\t0x%06x\n", getNodeVendorId() ); printMessage("\tModel Id:\t\t0x%08x\n", getModelId() ); printMessage("\tUnit Specifier ID:\t0x%06x\n", getUnitSpecifierId() ); printMessage("\tUnit version:\t\t0x%08x\n", getUnitVersion() ); printMessage("\tISO resource manager:\t%d\n", isIsoResourseManager() ); printMessage("\tCycle master capable:\t%d\n", isSupportsIsoOperations() ); printMessage("\tBus manager capable:\t%d\n", isBusManagerCapable() ); printMessage("\tCycle clock accuracy:\t%d\n", getCycleClockAccurancy() ); printMessage("\tMax rec:\t\t%d (max asy payload: %d bytes)\n", getMaxRec(), getAsyMaxPayload() ); } unsigned short ConfigRom::getAsyMaxPayload() const { // XXX use pow instead? return 1 << ( m_maxRec + 1 ); } bool ConfigRom::serialize( std::string path, Util::IOSerialize& ser ) { bool result; result = ser.write( path + "m_nodeId", m_nodeId ); result &= ser.write( path + "m_avcDevice", m_avcDevice ); result &= ser.write( path + "m_guid", m_guid ); result &= ser.write( path + "m_vendorName", std::string( m_vendorName ) ); result &= ser.write( path + "m_modelName", std::string( m_modelName ) ); result &= ser.write( path + "m_vendorId", m_vendorId ); result &= ser.write( path + "m_modelId", m_modelId ); result &= ser.write( path + "m_unit_specifier_id", m_unit_specifier_id ); result &= ser.write( path + "m_unit_version", m_unit_version ); result &= ser.write( path + "m_isIsoResourceManager", m_isIsoResourceManager ); result &= ser.write( path + "m_isCycleMasterCapable", m_isCycleMasterCapable ); result &= ser.write( path + "m_isSupportIsoOperations", m_isSupportIsoOperations ); result &= ser.write( path + "m_isBusManagerCapable", m_isBusManagerCapable ); result &= ser.write( path + "m_cycleClkAcc", m_cycleClkAcc ); result &= ser.write( path + "m_maxRec", m_maxRec ); result &= ser.write( path + "m_nodeVendorId", m_nodeVendorId ); result &= ser.write( path + "m_chipIdHi", m_chipIdHi ); result &= ser.write( path + "m_chipIdLow", m_chipIdLow ); return result; } ConfigRom* ConfigRom::deserialize( std::string path, Util::IODeserialize& deser, Ieee1394Service& ieee1394Service ) { ConfigRom* pConfigRom = new ConfigRom; if ( !pConfigRom ) { return 0; } pConfigRom->m_1394Service = ieee1394Service; bool result; result = deser.read( path + "m_nodeId", pConfigRom->m_nodeId ); result &= deser.read( path + "m_avcDevice", pConfigRom->m_avcDevice ); result &= deser.read( path + "m_guid", pConfigRom->m_guid ); result &= deser.read( path + "m_vendorName", pConfigRom->m_vendorName ); result &= deser.read( path + "m_modelName", pConfigRom->m_modelName ); result &= deser.read( path + "m_vendorId", pConfigRom->m_vendorId ); result &= deser.read( path + "m_modelId", pConfigRom->m_modelId ); result &= deser.read( path + "m_unit_specifier_id", pConfigRom->m_unit_specifier_id ); result &= deser.read( path + "m_unit_version", pConfigRom->m_unit_version ); result &= deser.read( path + "m_isIsoResourceManager", pConfigRom->m_isIsoResourceManager ); result &= deser.read( path + "m_isCycleMasterCapable", pConfigRom->m_isCycleMasterCapable ); result &= deser.read( path + "m_isSupportIsoOperations", pConfigRom->m_isSupportIsoOperations ); result &= deser.read( path + "m_isBusManagerCapable", pConfigRom->m_isBusManagerCapable ); result &= deser.read( path + "m_cycleClkAcc", pConfigRom->m_cycleClkAcc ); result &= deser.read( path + "m_maxRec", pConfigRom->m_maxRec ); result &= deser.read( path + "m_nodeVendorId", pConfigRom->m_nodeVendorId ); result &= deser.read( path + "m_chipIdHi", pConfigRom->m_chipIdHi ); result &= deser.read( path + "m_chipIdLow", pConfigRom->m_chipIdLow ); if ( !result ) { delete pConfigRom; return 0; } return pConfigRom; } bool ConfigRom::setNodeId( fb_nodeid_t nodeId ) { m_nodeId = nodeId; return true; } libffado-2.4.5/src/libieee1394/configrom.h0000644000175000001440000001146214206145246017532 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef CONFIGROM_H #define CONFIGROM_H #include "fbtypes.h" #include "csr1212.h" #include "libutil/serialize.h" #include "debugmodule/debugmodule.h" #include "libcontrol/Element.h" #include class Ieee1394Service; class ConfigRom : public Control::Element { public: ConfigRom( Ieee1394Service& ieee1394service, fb_nodeid_t nodeId ); virtual ~ConfigRom() {}; Ieee1394Service& get1394Service(); bool initialize(); bool operator == ( const ConfigRom& rhs ); const fb_nodeid_t getNodeId() const; const fb_octlet_t getGuid() const; const std::string getGuidString() const; const std::string getModelName() const; const std::string getVendorName() const; const unsigned int getModelId() const; // FIXME: isn't this the same as getNodeVendorId? const unsigned int getVendorId() const; const unsigned int getUnitSpecifierId() const; const unsigned int getUnitVersion() const; bool isIsoResourseManager() const { return m_isIsoResourceManager; } bool isCycleMasterCapable() const { return m_isCycleMasterCapable; } bool isSupportsIsoOperations() const { return m_isSupportIsoOperations; } bool isBusManagerCapable() const { return m_isBusManagerCapable; } fb_byte_t getCycleClockAccurancy() const { return m_cycleClkAcc; } fb_byte_t getMaxRec() const { return m_maxRec; } unsigned short getAsyMaxPayload() const; fb_quadlet_t getNodeVendorId() const { return m_nodeVendorId; } bool updatedNodeId(); bool setNodeId( fb_nodeid_t nodeId ); /** * @brief Compares the GUID of two ConfigRom's * * This function compares the GUID of two ConfigRom objects and returns true * if the GUID of @ref a is larger than the GUID of @ref b . This is intended * to be used with the STL sort() algorithm. * * Note that GUID's are converted to integers for this. * * @param a pointer to first ConfigRom * @param b pointer to second ConfigRom * * @returns true if the GUID of @ref a is larger than the GUID of @ref b . */ static bool compareGUID( const ConfigRom& a, const ConfigRom& b ); bool serialize( std::string path, Util::IOSerialize& ser ); static ConfigRom* deserialize( std::string path, Util::IODeserialize& deser, Ieee1394Service& ieee1394Service ); void printConfigRomDebug() const; void printConfigRom() const; void setVerboseLevel(int level) { setDebugLevel(level); Element::setVerboseLevel(level); } bool isPresentOnBus() { return m_nodeId != INVALID_NODE_ID; }; protected: void processUnitDirectory( struct csr1212_csr* csr, struct csr1212_keyval* ud_kv, unsigned int* id ); void processRootDirectory( struct csr1212_csr* csr ); Ieee1394Service& m_1394Service; fb_nodeid_t m_nodeId; bool m_avcDevice; fb_octlet_t m_guid; std::string m_vendorName; std::string m_modelName; unsigned int m_vendorId; unsigned int m_modelId; unsigned int m_unit_specifier_id; unsigned int m_unit_version; bool m_isIsoResourceManager; bool m_isCycleMasterCapable; bool m_isSupportIsoOperations; bool m_isBusManagerCapable; fb_byte_t m_cycleClkAcc; fb_byte_t m_maxRec; fb_quadlet_t m_nodeVendorId; fb_byte_t m_chipIdHi; fb_quadlet_t m_chipIdLow; /* only used during parsing */ struct csr1212_keyval* m_vendorNameKv; struct csr1212_keyval* m_modelNameKv; struct csr1212_csr* m_csr; private: ConfigRom( const ConfigRom& ); // do not allow copy ctor ConfigRom(); // ctor for deserialition DECLARE_DEBUG_MODULE; }; #endif /* CONFIGROM_H */ libffado-2.4.5/src/libieee1394/csr1212.c0000644000175000001440000012643514206145246016646 0ustar jwoitheusers/* * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * csr1212.c -- IEEE 1212 Control and Status Register support for Linux * * Copyright (C) 2003 Francois Retief * Steve Kinneberg * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* TODO List: * - Verify interface consistency: i.e., public functions that take a size * parameter expect size to be in bytes. * - Convenience functions for reading a block of data from a given offset. */ #ifndef __KERNEL__ #include #endif #include "csr1212.h" /* Permitted key type for each key id */ #define __I (1 << CSR1212_KV_TYPE_IMMEDIATE) #define __C (1 << CSR1212_KV_TYPE_CSR_OFFSET) #define __D (1 << CSR1212_KV_TYPE_DIRECTORY) #define __L (1 << CSR1212_KV_TYPE_LEAF) static const u_int8_t csr1212_key_id_type_map[0x30] = { 0, /* Reserved */ __D | __L, /* Descriptor */ __I | __D | __L, /* Bus_Dependent_Info */ __I | __D | __L, /* Vendor */ __I, /* Hardware_Version */ 0, 0, /* Reserved */ __D | __L, /* Module */ 0, 0, 0, 0, /* Reserved */ __I, /* Node_Capabilities */ __L, /* EUI_64 */ 0, 0, 0, /* Reserved */ __D, /* Unit */ __I, /* Specifier_ID */ __I, /* Version */ __I | __C | __D | __L, /* Dependent_Info */ __L, /* Unit_Location */ 0, /* Reserved */ __I, /* Model */ __D, /* Instance */ __L, /* Keyword */ __D, /* Feature */ __L, /* Extended_ROM */ __I, /* Extended_Key_Specifier_ID */ __I, /* Extended_Key */ __I | __C | __D | __L, /* Extended_Data */ __L, /* Modifiable_Descriptor */ __I, /* Directory_ID */ __I, /* Revision */ }; #undef __I #undef __C #undef __D #undef __L #define quads_to_bytes(_q) ((_q) * sizeof(u_int32_t)) #define bytes_to_quads(_b) (((_b) + sizeof(u_int32_t) - 1) / sizeof(u_int32_t)) static inline void free_keyval(struct csr1212_keyval *kv) { if ((kv->key.type == CSR1212_KV_TYPE_LEAF) && (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)) CSR1212_FREE(kv->value.leaf.data); CSR1212_FREE(kv); } static u_int16_t csr1212_crc16(const u_int32_t *buffer, size_t length) { int shift; u_int32_t data; u_int16_t sum, crc = 0; for (; length; length--) { data = CSR1212_BE32_TO_CPU(*buffer); buffer++; for (shift = 28; shift >= 0; shift -= 4 ) { sum = ((crc >> 12) ^ (data >> shift)) & 0xf; crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); } crc &= 0xffff; } return CSR1212_CPU_TO_BE16(crc); } #if 0 /* Microsoft computes the CRC with the bytes in reverse order. Therefore we * have a special version of the CRC algorithm to account for their buggy * software. */ static u_int16_t csr1212_msft_crc16(const u_int32_t *buffer, size_t length) { int shift; u_int32_t data; u_int16_t sum, crc = 0; for (; length; length--) { data = CSR1212_LE32_TO_CPU(*buffer); buffer++; for (shift = 28; shift >= 0; shift -= 4 ) { sum = ((crc >> 12) ^ (data >> shift)) & 0xf; crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); } crc &= 0xffff; } return CSR1212_CPU_TO_BE16(crc); } #endif static inline struct csr1212_dentry *csr1212_find_keyval(struct csr1212_keyval *dir, struct csr1212_keyval *kv) { struct csr1212_dentry *pos; for (pos = dir->value.directory.dentries_head; pos != NULL; pos = pos->next) { if (pos->kv == kv) return pos; } return NULL; } static inline struct csr1212_keyval *csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, u_int32_t offset) { struct csr1212_keyval *kv; for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next) { if (kv->offset == offset) return kv; } return NULL; } /* Creation Routines */ struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, size_t bus_info_size, void *private_data) { struct csr1212_csr *csr; csr = CSR1212_MALLOC(sizeof(*csr)); if (!csr) return NULL; csr->cache_head = csr1212_rom_cache_malloc(CSR1212_CONFIG_ROM_SPACE_OFFSET, CSR1212_CONFIG_ROM_SPACE_SIZE); if (!csr->cache_head) { CSR1212_FREE(csr); return NULL; } /* The keyval key id is not used for the root node, but a valid key id * that can be used for a directory needs to be passed to * csr1212_new_directory(). */ csr->root_kv = csr1212_new_directory(CSR1212_KV_ID_VENDOR); if (!csr->root_kv) { CSR1212_FREE(csr->cache_head); CSR1212_FREE(csr); return NULL; } csr->bus_info_data = csr->cache_head->data; csr->bus_info_len = bus_info_size; csr->crc_len = bus_info_size; csr->ops = ops; csr->private_data = private_data; csr->cache_tail = csr->cache_head; return csr; } void csr1212_init_local_csr(struct csr1212_csr *csr, const u_int32_t *bus_info_data, int max_rom) { static const int mr_map[] = { 4, 64, 1024, 0 }; #ifdef __KERNEL__ BUG_ON(max_rom & ~0x3); csr->max_rom = mr_map[max_rom]; #else if (max_rom & ~0x3) /* caller supplied invalid argument */ csr->max_rom = 0; else csr->max_rom = mr_map[max_rom]; #endif memcpy(csr->bus_info_data, bus_info_data, csr->bus_info_len); } static struct csr1212_keyval *csr1212_new_keyval(u_int8_t type, u_int8_t key) { struct csr1212_keyval *kv; if (key < 0x30 && ((csr1212_key_id_type_map[key] & (1 << type)) == 0)) return NULL; kv = CSR1212_MALLOC(sizeof(*kv)); if (!kv) return NULL; kv->key.type = type; kv->key.id = key; kv->associate = NULL; kv->refcnt = 1; kv->next = NULL; kv->prev = NULL; kv->offset = 0; kv->valid = 0; return kv; } struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value) { struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key); if (!kv) return NULL; kv->value.immediate = value; kv->valid = 1; return kv; } struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, size_t data_len) { struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key); if (!kv) return NULL; if (data_len > 0) { kv->value.leaf.data = CSR1212_MALLOC(data_len); if (!kv->value.leaf.data) { CSR1212_FREE(kv); return NULL; } if (data) memcpy(kv->value.leaf.data, data, data_len); } else { kv->value.leaf.data = NULL; } kv->value.leaf.len = bytes_to_quads(data_len); kv->offset = 0; kv->valid = 1; return kv; } struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, u_int32_t csr_offset) { struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key); if (!kv) return NULL; kv->value.csr_offset = csr_offset; kv->offset = 0; kv->valid = 1; return kv; } struct csr1212_keyval *csr1212_new_directory(u_int8_t key) { struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key); if (!kv) return NULL; kv->value.directory.len = 0; kv->offset = 0; kv->value.directory.dentries_head = NULL; kv->value.directory.dentries_tail = NULL; kv->valid = 1; return kv; } int csr1212_associate_keyval(struct csr1212_keyval *kv, struct csr1212_keyval *associate) { if (!kv || !associate) return CSR1212_EINVAL; if (kv->key.id == CSR1212_KV_ID_DESCRIPTOR || (associate->key.id != CSR1212_KV_ID_DESCRIPTOR && associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO && associate->key.id != CSR1212_KV_ID_EXTENDED_KEY && associate->key.id != CSR1212_KV_ID_EXTENDED_DATA && associate->key.id < 0x30)) return CSR1212_EINVAL; if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID && associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) return CSR1212_EINVAL; if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY && associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) return CSR1212_EINVAL; if (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY && kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) return CSR1212_EINVAL; if (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA && kv->key.id != CSR1212_KV_ID_EXTENDED_KEY) return CSR1212_EINVAL; if (kv->associate) csr1212_release_keyval(kv->associate); associate->refcnt++; kv->associate = associate; return CSR1212_SUCCESS; } int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, struct csr1212_keyval *kv) { struct csr1212_dentry *dentry; if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY) return CSR1212_EINVAL; dentry = CSR1212_MALLOC(sizeof(*dentry)); if (!dentry) return CSR1212_ENOMEM; dentry->kv = kv; kv->refcnt++; dentry->next = NULL; dentry->prev = dir->value.directory.dentries_tail; if (!dir->value.directory.dentries_head) dir->value.directory.dentries_head = dentry; if (dir->value.directory.dentries_tail) dir->value.directory.dentries_tail->next = dentry; dir->value.directory.dentries_tail = dentry; return CSR1212_SUCCESS; } struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec, u_int32_t key, u_int32_t value) { struct csr1212_keyval *kvs, *kvk, *kvv; kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec); kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key); kvv = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_DATA, value); if (!kvs || !kvk || !kvv) { if (kvs) free_keyval(kvs); if (kvk) free_keyval(kvk); if (kvv) free_keyval(kvv); return NULL; } /* Don't keep a local reference to the extended key or value. */ kvk->refcnt = 0; kvv->refcnt = 0; csr1212_associate_keyval(kvk, kvv); csr1212_associate_keyval(kvs, kvk); return kvs; } struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec, u_int32_t key, const void *data, size_t data_len) { struct csr1212_keyval *kvs, *kvk, *kvv; kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec); kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key); kvv = csr1212_new_leaf(CSR1212_KV_ID_EXTENDED_DATA, data, data_len); if (!kvs || !kvk || !kvv) { if (kvs) free_keyval(kvs); if (kvk) free_keyval(kvk); if (kvv) free_keyval(kvv); return NULL; } /* Don't keep a local reference to the extended key or value. */ kvk->refcnt = 0; kvv->refcnt = 0; csr1212_associate_keyval(kvk, kvv); csr1212_associate_keyval(kvs, kvk); return kvs; } struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, u_int32_t specifier_id, const void *data, size_t data_len) { struct csr1212_keyval *kv; kv = csr1212_new_leaf(CSR1212_KV_ID_DESCRIPTOR, NULL, data_len + CSR1212_DESCRIPTOR_LEAF_OVERHEAD); if (!kv) return NULL; CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, dtype); CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, specifier_id); if (data) { memcpy(CSR1212_DESCRIPTOR_LEAF_DATA(kv), data, data_len); } return kv; } struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth, u_int16_t cset, u_int16_t language, const void *data, size_t data_len) { struct csr1212_keyval *kv; char *lstr; kv = csr1212_new_descriptor_leaf(0, 0, NULL, data_len + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD); if (!kv) return NULL; CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, cwidth); CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, cset); CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language); lstr = (char*)CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv); /* make sure last quadlet is zeroed out */ *((u_int32_t*)&(lstr[(data_len - 1) & ~0x3])) = 0; /* don't copy the NUL terminator */ memcpy(lstr, data, data_len); return kv; } static int csr1212_check_minimal_ascii(const char *s) { static const char minimal_ascii_table[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x0C, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x21, 0x22, 0x00, 0x00, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, }; for (; *s; s++) { if (minimal_ascii_table[*s & 0x7F] != *s) return -1; /* failed */ } /* String conforms to minimal-ascii, as specified by IEEE 1212, * par. 7.4 */ return 0; } struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s) { /* Check if string conform to minimal_ascii format */ if (csr1212_check_minimal_ascii(s)) return NULL; /* IEEE 1212, par. 7.5.4.1 Textual descriptors (minimal ASCII) */ return csr1212_new_textual_descriptor_leaf(0, 0, 0, s, strlen(s)); } struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version, u_int8_t palette_depth, u_int8_t color_space, u_int16_t language, u_int16_t hscan, u_int16_t vscan, u_int32_t *palette, u_int32_t *pixels) { static const int pd[4] = { 0, 4, 16, 256 }; static const int cs[16] = { 4, 2 }; struct csr1212_keyval *kv; int palette_size; int pixel_size = (hscan * vscan + 3) & ~0x3; if (!pixels || (!palette && palette_depth) || (palette_depth & ~0x3) || (color_space & ~0xf)) return NULL; palette_size = pd[palette_depth] * cs[color_space]; kv = csr1212_new_descriptor_leaf(1, 0, NULL, palette_size + pixel_size + CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD); if (!kv) return NULL; CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version); CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth); CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space); CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language); CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan); CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan); if (palette_size) memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv), palette, palette_size); memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(kv), pixels, pixel_size); return kv; } struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size, u_int64_t address) { struct csr1212_keyval *kv; /* IEEE 1212, par. 7.5.4.3 Modifiable descriptors */ kv = csr1212_new_leaf(CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR, NULL, sizeof(u_int64_t)); if(!kv) return NULL; CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, max_size); CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, address); CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, address); return kv; } static int csr1212_check_keyword(const char *s) { for (; *s; s++) { if (('A' <= *s) && (*s <= 'Z')) continue; if (('0' <= *s) && (*s <= '9')) continue; if (*s == '-') continue; return -1; /* failed */ } /* String conforms to keyword, as specified by IEEE 1212, * par. 7.6.5 */ return CSR1212_SUCCESS; } struct csr1212_keyval *csr1212_new_keyword_leaf(int strc, const char *strv[]) { struct csr1212_keyval *kv; char *buffer; int i, data_len = 0; /* Check all keywords to see if they conform to restrictions: * Only the following characters is allowed ['A'..'Z','0'..'9','-'] * Each word is zero-terminated. * Also calculate the total length of the keywords. */ for (i = 0; i < strc; i++) { if (!strv[i] || csr1212_check_keyword(strv[i])) { return NULL; } data_len += strlen(strv[i]) + 1; /* Add zero-termination char. */ } /* IEEE 1212, par. 7.6.5 Keyword leaves */ kv = csr1212_new_leaf(CSR1212_KV_ID_KEYWORD, NULL, data_len); if (!kv) return NULL; buffer = (char *)kv->value.leaf.data; /* make sure last quadlet is zeroed out */ *((u_int32_t*)&(buffer[(data_len - 1) & ~0x3])) = 0; /* Copy keyword(s) into leaf data buffer */ for (i = 0; i < strc; i++) { int len = strlen(strv[i]) + 1; memcpy(buffer, strv[i], len); buffer += len; } return kv; } /* Destruction Routines */ void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, struct csr1212_keyval *kv) { struct csr1212_dentry *dentry; if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY) return; dentry = csr1212_find_keyval(dir, kv); if (!dentry) return; if (dentry->prev) dentry->prev->next = dentry->next; if (dentry->next) dentry->next->prev = dentry->prev; if (dir->value.directory.dentries_head == dentry) dir->value.directory.dentries_head = dentry->next; if (dir->value.directory.dentries_tail == dentry) dir->value.directory.dentries_tail = dentry->prev; CSR1212_FREE(dentry); csr1212_release_keyval(kv); } void csr1212_disassociate_keyval(struct csr1212_keyval *kv) { if (kv->associate) { csr1212_release_keyval(kv->associate); } kv->associate = NULL; } /* This function is used to free the memory taken by a keyval. If the given * keyval is a directory type, then any keyvals contained in that directory * will be destroyed as well if their respective refcnts are 0. By means of * list manipulation, this routine will descend a directory structure in a * non-recursive manner. */ void _csr1212_destroy_keyval(struct csr1212_keyval *kv) { struct csr1212_keyval *k, *a; struct csr1212_dentry dentry; struct csr1212_dentry *head, *tail; dentry.kv = kv; dentry.next = NULL; dentry.prev = NULL; head = &dentry; tail = head; while (head) { k = head->kv; while (k) { k->refcnt--; if (k->refcnt > 0) break; a = k->associate; if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) { /* If the current entry is a directory, then move all * the entries to the destruction list. */ if (k->value.directory.dentries_head) { tail->next = k->value.directory.dentries_head; k->value.directory.dentries_head->prev = tail; tail = k->value.directory.dentries_tail; } } free_keyval(k); k = a; } head = head->next; if (head) { if (head->prev && head->prev != &dentry) { CSR1212_FREE(head->prev); } head->prev = NULL; } else if (tail != &dentry) CSR1212_FREE(tail); } } void csr1212_destroy_csr(struct csr1212_csr *csr) { struct csr1212_csr_rom_cache *c, *oc; struct csr1212_cache_region *cr, *ocr; csr1212_release_keyval(csr->root_kv); c = csr->cache_head; while (c) { oc = c; cr = c->filled_head; while (cr) { ocr = cr; cr = cr->next; CSR1212_FREE(ocr); } c = c->next; CSR1212_FREE(oc); } CSR1212_FREE(csr); } /* CSR Image Creation */ static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize) { struct csr1212_csr_rom_cache *cache; u_int64_t csr_addr; if (!csr || !csr->ops || !csr->ops->allocate_addr_range || !csr->ops->release_addr || csr->max_rom < 1) return CSR1212_EINVAL; /* ROM size must be a multiple of csr->max_rom */ romsize = (romsize + (csr->max_rom - 1)) & ~(csr->max_rom - 1); csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom, csr->private_data); if (csr_addr == ~0ULL) { return CSR1212_ENOMEM; } if (csr_addr < CSR1212_REGISTER_SPACE_BASE) { /* Invalid address returned from allocate_addr_range(). */ csr->ops->release_addr(csr_addr, csr->private_data); return CSR1212_ENOMEM; } cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, romsize); if (!cache) { csr->ops->release_addr(csr_addr, csr->private_data); return CSR1212_ENOMEM; } cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, CSR1212_KV_ID_EXTENDED_ROM); if (!cache->ext_rom) { csr->ops->release_addr(csr_addr, csr->private_data); CSR1212_FREE(cache); return CSR1212_ENOMEM; } if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) != CSR1212_SUCCESS) { csr1212_release_keyval(cache->ext_rom); csr->ops->release_addr(csr_addr, csr->private_data); CSR1212_FREE(cache); return CSR1212_ENOMEM; } cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE; cache->ext_rom->value.leaf.len = -1; cache->ext_rom->value.leaf.data = cache->data; /* Add cache to tail of cache list */ cache->prev = csr->cache_tail; csr->cache_tail->next = cache; csr->cache_tail = cache; return CSR1212_SUCCESS; } static inline void csr1212_remove_cache(struct csr1212_csr *csr, struct csr1212_csr_rom_cache *cache) { if (csr->cache_head == cache) csr->cache_head = cache->next; if (csr->cache_tail == cache) csr->cache_tail = cache->prev; if (cache->prev) cache->prev->next = cache->next; if (cache->next) cache->next->prev = cache->prev; if (cache->ext_rom) { csr1212_detach_keyval_from_directory(csr->root_kv, cache->ext_rom); csr1212_release_keyval(cache->ext_rom); } CSR1212_FREE(cache); } static int csr1212_generate_layout_subdir(struct csr1212_keyval *dir, struct csr1212_keyval **layout_tail) { struct csr1212_dentry *dentry; struct csr1212_keyval *dkv; struct csr1212_keyval *last_extkey_spec = NULL; struct csr1212_keyval *last_extkey = NULL; int num_entries = 0; for (dentry = dir->value.directory.dentries_head; dentry; dentry = dentry->next) { for (dkv = dentry->kv; dkv; dkv = dkv->associate) { /* Special Case: Extended Key Specifier_ID */ if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { if (last_extkey_spec == NULL) { last_extkey_spec = dkv; } else if (dkv->value.immediate != last_extkey_spec->value.immediate) { last_extkey_spec = dkv; } else { continue; } /* Special Case: Extended Key */ } else if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY) { if (last_extkey == NULL) { last_extkey = dkv; } else if (dkv->value.immediate != last_extkey->value.immediate) { last_extkey = dkv; } else { continue; } } num_entries += 1; switch(dkv->key.type) { default: case CSR1212_KV_TYPE_IMMEDIATE: case CSR1212_KV_TYPE_CSR_OFFSET: break; case CSR1212_KV_TYPE_LEAF: case CSR1212_KV_TYPE_DIRECTORY: /* Remove from list */ if (dkv->prev && (dkv->prev->next == dkv)) dkv->prev->next = dkv->next; if (dkv->next && (dkv->next->prev == dkv)) dkv->next->prev = dkv->prev; //if (dkv == *layout_tail) // *layout_tail = dkv->prev; /* Special case: Extended ROM leafs */ if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) { dkv->value.leaf.len = -1; /* Don't add Extended ROM leafs in the layout list, * they are handled differently. */ break; } /* Add to tail of list */ dkv->next = NULL; dkv->prev = *layout_tail; (*layout_tail)->next = dkv; *layout_tail = dkv; break; } } } return num_entries; } size_t csr1212_generate_layout_order(struct csr1212_keyval *kv) { struct csr1212_keyval *ltail = kv; size_t agg_size = 0; while(kv) { switch(kv->key.type) { case CSR1212_KV_TYPE_LEAF: /* Add 1 quadlet for crc/len field */ agg_size += kv->value.leaf.len + 1; break; case CSR1212_KV_TYPE_DIRECTORY: kv->value.directory.len = csr1212_generate_layout_subdir(kv, <ail); /* Add 1 quadlet for crc/len field */ agg_size += kv->value.directory.len + 1; break; } kv = kv->next; } return quads_to_bytes(agg_size); } struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, struct csr1212_keyval *start_kv, int start_pos) { struct csr1212_keyval *kv = start_kv; struct csr1212_keyval *okv = start_kv; int pos = start_pos; int kv_len = 0, okv_len = 0; cache->layout_head = kv; while(kv && pos < cache->size) { /* Special case: Extended ROM leafs */ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { kv->offset = cache->offset + pos; } switch(kv->key.type) { case CSR1212_KV_TYPE_LEAF: kv_len = kv->value.leaf.len; break; case CSR1212_KV_TYPE_DIRECTORY: kv_len = kv->value.directory.len; break; default: /* Should never get here */ break; } pos += quads_to_bytes(kv_len + 1); if (pos <= cache->size) { okv = kv; okv_len = kv_len; kv = kv->next; } } cache->layout_tail = okv; cache->len = (okv->offset - cache->offset) + quads_to_bytes(okv_len + 1); return kv; } static void csr1212_generate_tree_subdir(struct csr1212_keyval *dir, u_int32_t *data_buffer) { struct csr1212_dentry *dentry; struct csr1212_keyval *last_extkey_spec = NULL; struct csr1212_keyval *last_extkey = NULL; int index = 0; for (dentry = dir->value.directory.dentries_head; dentry; dentry = dentry->next) { struct csr1212_keyval *a; for (a = dentry->kv; a; a = a->associate) { u_int32_t value = 0; /* Special Case: Extended Key Specifier_ID */ if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { if (last_extkey_spec == NULL) { last_extkey_spec = a; } else if (a->value.immediate != last_extkey_spec->value.immediate) { last_extkey_spec = a; } else { continue; } /* Special Case: Extended Key */ } else if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY) { if (last_extkey == NULL) { last_extkey = a; } else if (a->value.immediate != last_extkey->value.immediate) { last_extkey = a; } else { continue; } } switch(a->key.type) { case CSR1212_KV_TYPE_IMMEDIATE: value = a->value.immediate; break; case CSR1212_KV_TYPE_CSR_OFFSET: value = a->value.csr_offset; break; case CSR1212_KV_TYPE_LEAF: value = a->offset; value -= dir->offset + quads_to_bytes(1+index); value = bytes_to_quads(value); break; case CSR1212_KV_TYPE_DIRECTORY: value = a->offset; value -= dir->offset + quads_to_bytes(1+index); value = bytes_to_quads(value); break; default: /* Should never get here */ break; /* GDB breakpoint */ } value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) << CSR1212_KV_KEY_SHIFT; value |= (a->key.type & CSR1212_KV_KEY_TYPE_MASK) << (CSR1212_KV_KEY_SHIFT + CSR1212_KV_KEY_TYPE_SHIFT); data_buffer[index] = CSR1212_CPU_TO_BE32(value); index++; } } } void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache) { struct csr1212_keyval *kv, *nkv; struct csr1212_keyval_img *kvi; for (kv = cache->layout_head; kv != cache->layout_tail->next; kv = nkv) { kvi = (struct csr1212_keyval_img *) (cache->data + bytes_to_quads(kv->offset - cache->offset)); switch(kv->key.type) { default: case CSR1212_KV_TYPE_IMMEDIATE: case CSR1212_KV_TYPE_CSR_OFFSET: /* Should never get here */ break; /* GDB breakpoint */ case CSR1212_KV_TYPE_LEAF: /* Don't copy over Extended ROM areas, they are * already filled out! */ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) memcpy(kvi->data, kv->value.leaf.data, quads_to_bytes(kv->value.leaf.len)); kvi->length = CSR1212_CPU_TO_BE16(kv->value.leaf.len); kvi->crc = csr1212_crc16(kvi->data, kv->value.leaf.len); break; case CSR1212_KV_TYPE_DIRECTORY: csr1212_generate_tree_subdir(kv, kvi->data); kvi->length = CSR1212_CPU_TO_BE16(kv->value.directory.len); kvi->crc = csr1212_crc16(kvi->data, kv->value.directory.len); break; } nkv = kv->next; if (kv->prev) kv->prev->next = NULL; if (kv->next) kv->next->prev = NULL; kv->prev = NULL; kv->next = NULL; } } int csr1212_generate_csr_image(struct csr1212_csr *csr) { struct csr1212_bus_info_block_img *bi; struct csr1212_csr_rom_cache *cache; struct csr1212_keyval *kv; size_t agg_size; int ret; int init_offset; if (!csr) return CSR1212_EINVAL; cache = csr->cache_head; bi = (struct csr1212_bus_info_block_img*)cache->data; bi->length = bytes_to_quads(csr->bus_info_len) - 1; bi->crc_length = bi->length; bi->crc = csr1212_crc16(bi->data, bi->crc_length); csr->root_kv->next = NULL; csr->root_kv->prev = NULL; agg_size = csr1212_generate_layout_order(csr->root_kv); init_offset = csr->bus_info_len; for (kv = csr->root_kv, cache = csr->cache_head; kv; cache = cache->next) { if (!cache) { /* Estimate approximate number of additional cache * regions needed (it assumes that the cache holding * the first 1K Config ROM space always exists). */ int est_c = agg_size / (CSR1212_EXTENDED_ROM_SIZE - (2 * sizeof(u_int32_t))) + 1; /* Add additional cache regions, extras will be * removed later */ for (; est_c; est_c--) { ret = csr1212_append_new_cache(csr, CSR1212_EXTENDED_ROM_SIZE); if (ret != CSR1212_SUCCESS) return ret; } /* Need to re-layout for additional cache regions */ agg_size = csr1212_generate_layout_order(csr->root_kv); kv = csr->root_kv; cache = csr->cache_head; init_offset = csr->bus_info_len; } kv = csr1212_generate_positions(cache, kv, init_offset); agg_size -= cache->len; init_offset = sizeof(u_int32_t); } /* Remove unused, excess cache regions */ while (cache) { struct csr1212_csr_rom_cache *oc = cache; cache = cache->next; csr1212_remove_cache(csr, oc); } /* Go through the list backward so that when done, the correct CRC * will be calculated for the Extended ROM areas. */ for(cache = csr->cache_tail; cache; cache = cache->prev) { /* Only Extended ROM caches should have this set. */ if (cache->ext_rom) { int leaf_size; /* Make sure the Extended ROM leaf is a multiple of * max_rom in size. */ if (csr->max_rom < 1) return CSR1212_EINVAL; leaf_size = (cache->len + (csr->max_rom - 1)) & ~(csr->max_rom - 1); /* Zero out the unused ROM region */ memset(cache->data + bytes_to_quads(cache->len), 0x00, leaf_size - cache->len); /* Subtract leaf header */ leaf_size -= sizeof(u_int32_t); /* Update the Extended ROM leaf length */ cache->ext_rom->value.leaf.len = bytes_to_quads(leaf_size); } else { /* Zero out the unused ROM region */ memset(cache->data + bytes_to_quads(cache->len), 0x00, cache->size - cache->len); } /* Copy the data into the cache buffer */ csr1212_fill_cache(cache); if (cache != csr->cache_head) { /* Set the length and CRC of the extended ROM. */ struct csr1212_keyval_img *kvi = (struct csr1212_keyval_img*)cache->data; kvi->length = CSR1212_CPU_TO_BE16(bytes_to_quads(cache->len) - 1); kvi->crc = csr1212_crc16(kvi->data, bytes_to_quads(cache->len) - 1); } } return CSR1212_SUCCESS; } int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer, u_int32_t len) { struct csr1212_csr_rom_cache *cache; for (cache = csr->cache_head; cache; cache = cache->next) { if (offset >= cache->offset && (offset + len) <= (cache->offset + cache->size)) { memcpy(buffer, &cache->data[bytes_to_quads(offset - cache->offset)], len); return CSR1212_SUCCESS; } } return CSR1212_ENOENT; } /* Parse a chunk of data as a Config ROM */ static int csr1212_parse_bus_info_block(struct csr1212_csr *csr) { struct csr1212_bus_info_block_img *bi; struct csr1212_cache_region *cr; int i; int ret; /* IEEE 1212 says that the entire bus info block should be readable in * a single transaction regardless of the max_rom value. * Unfortunately, many IEEE 1394 devices do not abide by that, so the * bus info block will be read 1 quadlet at a time. The rest of the * ConfigROM will be read according to the max_rom field. */ for (i = 0; i < csr->bus_info_len; i += sizeof(csr1212_quad_t)) { ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, sizeof(csr1212_quad_t), &csr->cache_head->data[bytes_to_quads(i)], csr->private_data); if (ret != CSR1212_SUCCESS) return ret; } bi = (struct csr1212_bus_info_block_img*)csr->cache_head->data; csr->crc_len = quads_to_bytes(bi->crc_length); /* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that is not * always the case, so read the rest of the crc area 1 quadlet at a time. */ for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(csr1212_quad_t)) { ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, sizeof(csr1212_quad_t), &csr->cache_head->data[bytes_to_quads(i)], csr->private_data); if (ret != CSR1212_SUCCESS) return ret; } if (bytes_to_quads(csr->bus_info_len - sizeof(csr1212_quad_t)) != bi->length) return CSR1212_EINVAL; #if 0 /* Apparently there are too many differnt wrong implementations of the * CRC algorithm that verifying them is moot. */ if ((csr1212_crc16(bi->data, bi->crc_length) != bi->crc) && (csr1212_msft_crc16(bi->data, bi->crc_length) != bi->crc)) return CSR1212_EINVAL; #endif cr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region)); if (!cr) return CSR1212_ENOMEM; cr->next = NULL; cr->prev = NULL; cr->offset_start = 0; cr->offset_end = csr->crc_len + 4; csr->cache_head->filled_head = cr; csr->cache_head->filled_tail = cr; return CSR1212_SUCCESS; } static int csr1212_parse_dir_entry(struct csr1212_keyval *dir, csr1212_quad_t ki, u_int32_t kv_pos) { int ret = CSR1212_SUCCESS; struct csr1212_keyval *k = NULL; u_int32_t offset; switch(CSR1212_KV_KEY_TYPE(ki)) { case CSR1212_KV_TYPE_IMMEDIATE: k = csr1212_new_immediate(CSR1212_KV_KEY_ID(ki), CSR1212_KV_VAL(ki)); if (!k) { ret = CSR1212_ENOMEM; goto fail; } k->refcnt = 0; /* Don't keep local reference when parsing. */ break; case CSR1212_KV_TYPE_CSR_OFFSET: k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki), CSR1212_KV_VAL(ki)); if (!k) { ret = CSR1212_ENOMEM; goto fail; } k->refcnt = 0; /* Don't keep local reference when parsing. */ break; default: /* Compute the offset from 0xffff f000 0000. */ offset = quads_to_bytes(CSR1212_KV_VAL(ki)) + kv_pos; if (offset == kv_pos) { /* Uh-oh. Can't have a relative offset of 0 for Leaves * or Directories. The Config ROM image is most likely * messed up, so we'll just abort here. */ ret = CSR1212_EIO; goto fail; } k = csr1212_find_keyval_offset(dir, offset); if (k) break; /* Found it. */ if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY) { k = csr1212_new_directory(CSR1212_KV_KEY_ID(ki)); } else { k = csr1212_new_leaf(CSR1212_KV_KEY_ID(ki), NULL, 0); } if (!k) { ret = CSR1212_ENOMEM; goto fail; } k->refcnt = 0; /* Don't keep local reference when parsing. */ k->valid = 0; /* Contents not read yet so it's not valid. */ k->offset = offset; k->prev = dir; k->next = dir->next; dir->next->prev = k; dir->next = k; } ret = csr1212_attach_keyval_to_directory(dir, k); fail: if (ret != CSR1212_SUCCESS) { if (k) free_keyval(k); } return ret; } int csr1212_parse_keyval(struct csr1212_keyval *kv, struct csr1212_csr_rom_cache *cache) { struct csr1212_keyval_img *kvi; int i; int ret = CSR1212_SUCCESS; int kvi_len; kvi = (struct csr1212_keyval_img*)&cache->data[bytes_to_quads(kv->offset - cache->offset)]; kvi_len = CSR1212_BE16_TO_CPU(kvi->length); #if 0 /* Apparently there are too many differnt wrong implementations of the * CRC algorithm that verifying them is moot. */ if ((csr1212_crc16(kvi->data, kvi_len) != kvi->crc) && (csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc)) { ret = CSR1212_EINVAL; goto fail; } #endif switch(kv->key.type) { case CSR1212_KV_TYPE_DIRECTORY: for (i = 0; i < kvi_len; i++) { csr1212_quad_t ki = kvi->data[i]; /* Some devices put null entries in their unit * directories. If we come across such an entry, * then skip it. */ if (ki == 0x0) continue; ret = csr1212_parse_dir_entry(kv, ki, (kv->offset + quads_to_bytes(i + 1))); } kv->value.directory.len = kvi_len; break; case CSR1212_KV_TYPE_LEAF: if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { kv->value.leaf.data = CSR1212_MALLOC(quads_to_bytes(kvi_len)); if (!kv->value.leaf.data) { ret = CSR1212_ENOMEM; goto fail; } kv->value.leaf.len = kvi_len; memcpy(kv->value.leaf.data, kvi->data, quads_to_bytes(kvi_len)); } break; } kv->valid = 1; fail: return ret; } int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) { struct csr1212_cache_region *cr, *ncr, *newcr = NULL; struct csr1212_keyval_img *kvi = NULL; struct csr1212_csr_rom_cache *cache; int cache_index; u_int64_t addr; u_int32_t *cache_ptr; u_int16_t kv_len = 0; if (!csr || !kv || csr->max_rom < 1) return CSR1212_EINVAL; /* First find which cache the data should be in (or go in if not read * yet). */ for (cache = csr->cache_head; cache; cache = cache->next) { if (kv->offset >= cache->offset && kv->offset < (cache->offset + cache->size)) break; } if (!cache) { csr1212_quad_t q; u_int32_t cache_size; /* Only create a new cache for Extended ROM leaves. */ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) return CSR1212_EINVAL; if (csr->ops->bus_read(csr, CSR1212_REGISTER_SPACE_BASE + kv->offset, sizeof(csr1212_quad_t), &q, csr->private_data)) { return CSR1212_EIO; } kv->value.leaf.len = CSR1212_BE32_TO_CPU(q) >> 16; cache_size = (quads_to_bytes(kv->value.leaf.len + 1) + (csr->max_rom - 1)) & ~(csr->max_rom - 1); cache = csr1212_rom_cache_malloc(kv->offset, cache_size); if (!cache) return CSR1212_ENOMEM; kv->value.leaf.data = &cache->data[1]; csr->cache_tail->next = cache; cache->prev = csr->cache_tail; cache->next = NULL; csr->cache_tail = cache; cache->filled_head = CSR1212_MALLOC(sizeof(struct csr1212_cache_region)); if (!cache->filled_head) { return CSR1212_ENOMEM; } cache->filled_head->offset_start = 0; cache->filled_head->offset_end = sizeof(csr1212_quad_t); cache->filled_tail = cache->filled_head; cache->filled_head->next = NULL; cache->filled_head->prev = NULL; cache->data[0] = q; /* Don't read the entire extended ROM now. Pieces of it will * be read when entries inside it are read. */ return csr1212_parse_keyval(kv, cache); } cache_index = kv->offset - cache->offset; /* Now seach read portions of the cache to see if it is there. */ for (cr = cache->filled_head; cr; cr = cr->next) { if (cache_index < cr->offset_start) { newcr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region)); if (!newcr) return CSR1212_ENOMEM; newcr->offset_start = cache_index & ~(csr->max_rom - 1); newcr->offset_end = newcr->offset_start; newcr->next = cr; newcr->prev = cr->prev; cr->prev = newcr; cr = newcr; break; } else if ((cache_index >= cr->offset_start) && (cache_index < cr->offset_end)) { kvi = (struct csr1212_keyval_img*) (&cache->data[bytes_to_quads(cache_index)]); kv_len = quads_to_bytes(CSR1212_BE16_TO_CPU(kvi->length) + 1); break; } else if (cache_index == cr->offset_end) break; } if (!cr) { cr = cache->filled_tail; newcr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region)); if (!newcr) return CSR1212_ENOMEM; newcr->offset_start = cache_index & ~(csr->max_rom - 1); newcr->offset_end = newcr->offset_start; newcr->prev = cr; newcr->next = cr->next; cr->next = newcr; cr = newcr; cache->filled_tail = newcr; } while(!kvi || cr->offset_end < cache_index + kv_len) { cache_ptr = &cache->data[bytes_to_quads(cr->offset_end & ~(csr->max_rom - 1))]; addr = (CSR1212_CSR_ARCH_REG_SPACE_BASE + cache->offset + cr->offset_end) & ~(csr->max_rom - 1); if (csr->ops->bus_read(csr, addr, csr->max_rom, cache_ptr, csr->private_data)) { if (csr->max_rom == 4) /* We've got problems! */ return CSR1212_EIO; /* Apperently the max_rom value was a lie, set it to * do quadlet reads and try again. */ csr->max_rom = 4; continue; } cr->offset_end += csr->max_rom - (cr->offset_end & (csr->max_rom - 1)); if (!kvi && (cr->offset_end > cache_index)) { kvi = (struct csr1212_keyval_img*) (&cache->data[bytes_to_quads(cache_index)]); kv_len = quads_to_bytes(CSR1212_BE16_TO_CPU(kvi->length) + 1); } if ((kv_len + (kv->offset - cache->offset)) > cache->size) { /* The Leaf or Directory claims its length extends * beyond the ConfigROM image region and thus beyond the * end of our cache region. Therefore, we abort now * rather than seg faulting later. */ return CSR1212_EIO; } ncr = cr->next; if (ncr && (cr->offset_end >= ncr->offset_start)) { /* consolidate region entries */ ncr->offset_start = cr->offset_start; if (cr->prev) cr->prev->next = cr->next; ncr->prev = cr->prev; if (cache->filled_head == cr) cache->filled_head = ncr; CSR1212_FREE(cr); cr = ncr; } } return csr1212_parse_keyval(kv, cache); } int csr1212_parse_csr(struct csr1212_csr *csr) { 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; /* * There has been a buggy firmware with bus_info_block.max_rom > 0 * spotted which actually only supported quadlet read requests to the * config ROM. Therefore read everything quadlet by quadlet regardless * of what the bus info block says. This mirrors a similar change * made in the Linux kernel around 4 Jan 2009. See * http://git.kernel.org/linus/0bed1819687b50a7 * The other hunks in that diff are cleanups - removal of things * which aren't needed now that max_rom is fixed at 4. In time it * may be worthwhile merging them too. */ csr->max_rom = 4; 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; } libffado-2.4.5/src/libieee1394/csr1212.h0000644000175000001440000007210614206145246016646 0ustar jwoitheusers/* * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * csr1212.h -- IEEE 1212 Control and Status Register support for Linux * * Copyright (C) 2003 Francois Retief * Steve Kinneberg * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __CSR1212_H__ #define __CSR1212_H__ #ifdef __cplusplus extern "C" { #endif /* Compatibility layer */ #ifdef __KERNEL__ #include #include #include #include #include #include #define CSR1212_MALLOC(size) vmalloc((size)) #define CSR1212_FREE(ptr) vfree(ptr) #define CSR1212_BE16_TO_CPU(quad) be16_to_cpu(quad) #define CSR1212_CPU_TO_BE16(quad) cpu_to_be16(quad) #define CSR1212_BE32_TO_CPU(quad) be32_to_cpu(quad) #define CSR1212_CPU_TO_BE32(quad) cpu_to_be32(quad) #define CSR1212_BE64_TO_CPU(quad) be64_to_cpu(quad) #define CSR1212_CPU_TO_BE64(quad) cpu_to_be64(quad) #define CSR1212_LE16_TO_CPU(quad) le16_to_cpu(quad) #define CSR1212_CPU_TO_LE16(quad) cpu_to_le16(quad) #define CSR1212_LE32_TO_CPU(quad) le32_to_cpu(quad) #define CSR1212_CPU_TO_LE32(quad) cpu_to_le32(quad) #define CSR1212_LE64_TO_CPU(quad) le64_to_cpu(quad) #define CSR1212_CPU_TO_LE64(quad) cpu_to_le64(quad) #include #define CSR1212_SUCCESS (0) #define CSR1212_EINVAL (-EINVAL) #define CSR1212_ENOMEM (-ENOMEM) #define CSR1212_ENOENT (-ENOENT) #define CSR1212_EIO (-EIO) #define CSR1212_EBUSY (-EBUSY) #else /* Userspace */ #include #include #define CSR1212_MALLOC(size) calloc(1,size) #define CSR1212_FREE(ptr) free(ptr) #include #if __BYTE_ORDER == __LITTLE_ENDIAN #include #define CSR1212_BE16_TO_CPU(quad) bswap_16(quad) #define CSR1212_CPU_TO_BE16(quad) bswap_16(quad) #define CSR1212_BE32_TO_CPU(quad) bswap_32(quad) #define CSR1212_CPU_TO_BE32(quad) bswap_32(quad) #define CSR1212_BE64_TO_CPU(quad) bswap_64(quad) #define CSR1212_CPU_TO_BE64(quad) bswap_64(quad) #define CSR1212_LE16_TO_CPU(quad) (quad) #define CSR1212_CPU_TO_LE16(quad) (quad) #define CSR1212_LE32_TO_CPU(quad) (quad) #define CSR1212_CPU_TO_LE32(quad) (quad) #define CSR1212_LE64_TO_CPU(quad) (quad) #define CSR1212_CPU_TO_LE64(quad) (quad) #else #define CSR1212_BE16_TO_CPU(quad) (quad) #define CSR1212_CPU_TO_BE16(quad) (quad) #define CSR1212_BE32_TO_CPU(quad) (quad) #define CSR1212_CPU_TO_BE32(quad) (quad) #define CSR1212_BE64_TO_CPU(quad) (quad) #define CSR1212_CPU_TO_BE64(quad) (quad) #define CSR1212_LE16_TO_CPU(quad) bswap_16(quad) #define CSR1212_CPU_TO_LE16(quad) bswap_16(quad) #define CSR1212_LE32_TO_CPU(quad) bswap_32(quad) #define CSR1212_CPU_TO_LE32(quad) bswap_32(quad) #define CSR1212_LE64_TO_CPU(quad) bswap_64(quad) #define CSR1212_CPU_TO_LE64(quad) bswap_64(quad) #endif #include #define CSR1212_SUCCESS (0) #define CSR1212_EINVAL (EINVAL) #define CSR1212_ENOMEM (ENOMEM) #define CSR1212_ENOENT (ENOENT) #define CSR1212_EIO (EIO) #define CSR1212_EBUSY (EBUSY) #endif #define CSR1212_KV_VAL_MASK 0xffffff #define CSR1212_KV_KEY_SHIFT 24 #define CSR1212_KV_KEY_TYPE_SHIFT 6 #define CSR1212_KV_KEY_ID_MASK 0x3f #define CSR1212_KV_KEY_TYPE_MASK 0x3 /* After shift */ /* CSR 1212 key types */ #define CSR1212_KV_TYPE_IMMEDIATE 0 #define CSR1212_KV_TYPE_CSR_OFFSET 1 #define CSR1212_KV_TYPE_LEAF 2 #define CSR1212_KV_TYPE_DIRECTORY 3 /* CSR 1212 key ids */ #define CSR1212_KV_ID_DESCRIPTOR 0x01 #define CSR1212_KV_ID_BUS_DEPENDENT_INFO 0x02 #define CSR1212_KV_ID_VENDOR 0x03 #define CSR1212_KV_ID_HARDWARE_VERSION 0x04 #define CSR1212_KV_ID_MODULE 0x07 #define CSR1212_KV_ID_NODE_CAPABILITIES 0x0C #define CSR1212_KV_ID_EUI_64 0x0D #define CSR1212_KV_ID_UNIT 0x11 #define CSR1212_KV_ID_SPECIFIER_ID 0x12 #define CSR1212_KV_ID_VERSION 0x13 #define CSR1212_KV_ID_DEPENDENT_INFO 0x14 #define CSR1212_KV_ID_UNIT_LOCATION 0x15 #define CSR1212_KV_ID_MODEL 0x17 #define CSR1212_KV_ID_INSTANCE 0x18 #define CSR1212_KV_ID_KEYWORD 0x19 #define CSR1212_KV_ID_FEATURE 0x1A #define CSR1212_KV_ID_EXTENDED_ROM 0x1B #define CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID 0x1C #define CSR1212_KV_ID_EXTENDED_KEY 0x1D #define CSR1212_KV_ID_EXTENDED_DATA 0x1E #define CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR 0x1F #define CSR1212_KV_ID_DIRECTORY_ID 0x20 #define CSR1212_KV_ID_REVISION 0x21 /* IEEE 1212 Address space map */ #define CSR1212_ALL_SPACE_BASE (0x000000000000ULL) #define CSR1212_ALL_SPACE_SIZE (1ULL << 48) #define CSR1212_ALL_SPACE_END (CSR1212_ALL_SPACE_BASE + CSR1212_ALL_SPACE_SIZE) #define CSR1212_MEMORY_SPACE_BASE (0x000000000000ULL) #define CSR1212_MEMORY_SPACE_SIZE ((256ULL * (1ULL << 40)) - (512ULL * (1ULL << 20))) #define CSR1212_MEMORY_SPACE_END (CSR1212_MEMORY_SPACE_BASE + CSR1212_MEMORY_SPACE_SIZE) #define CSR1212_PRIVATE_SPACE_BASE (0xffffe0000000ULL) #define CSR1212_PRIVATE_SPACE_SIZE (256ULL * (1ULL << 20)) #define CSR1212_PRIVATE_SPACE_END (CSR1212_PRIVATE_SPACE_BASE + CSR1212_PRIVATE_SPACE_SIZE) #define CSR1212_REGISTER_SPACE_BASE (0xfffff0000000ULL) #define CSR1212_REGISTER_SPACE_SIZE (256ULL * (1ULL << 20)) #define CSR1212_REGISTER_SPACE_END (CSR1212_REGISTER_SPACE_BASE + CSR1212_REGISTER_SPACE_SIZE) #define CSR1212_CSR_ARCH_REG_SPACE_BASE (0xfffff0000000ULL) #define CSR1212_CSR_ARCH_REG_SPACE_SIZE (512) #define CSR1212_CSR_ARCH_REG_SPACE_END (CSR1212_CSR_ARCH_REG_SPACE_BASE + CSR1212_CSR_ARCH_REG_SPACE_SIZE) #define CSR1212_CSR_ARCH_REG_SPACE_OFFSET (CSR1212_CSR_ARCH_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) #define CSR1212_CSR_BUS_DEP_REG_SPACE_BASE (0xfffff0000200ULL) #define CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE (512) #define CSR1212_CSR_BUS_DEP_REG_SPACE_END (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE + CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE) #define CSR1212_CSR_BUS_DEP_REG_SPACE_OFFSET (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) #define CSR1212_CONFIG_ROM_SPACE_BASE (0xfffff0000400ULL) #define CSR1212_CONFIG_ROM_SPACE_SIZE (1024) #define CSR1212_CONFIG_ROM_SPACE_END (CSR1212_CONFIG_ROM_SPACE_BASE + CSR1212_CONFIG_ROM_SPACE_SIZE) #define CSR1212_CONFIG_ROM_SPACE_OFFSET (CSR1212_CONFIG_ROM_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) #define CSR1212_UNITS_SPACE_BASE (0xfffff0000800ULL) #define CSR1212_UNITS_SPACE_SIZE ((256ULL * (1ULL << 20)) - 2048) #define CSR1212_UNITS_SPACE_END (CSR1212_UNITS_SPACE_BASE + CSR1212_UNITS_SPACE_SIZE) #define CSR1212_UNITS_SPACE_OFFSET (CSR1212_UNITS_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) #define CSR1212_EXTENDED_ROM_SIZE (0x10000 * sizeof(u_int32_t)) /* Config ROM image structures */ struct csr1212_bus_info_block_img { u_int8_t length; u_int8_t crc_length; u_int16_t crc; /* Must be last */ u_int32_t data[0]; /* older gcc can't handle [] which is standard */ }; #define CSR1212_KV_KEY(quad) (CSR1212_BE32_TO_CPU(quad) >> CSR1212_KV_KEY_SHIFT) #define CSR1212_KV_KEY_TYPE(quad) (CSR1212_KV_KEY(quad) >> CSR1212_KV_KEY_TYPE_SHIFT) #define CSR1212_KV_KEY_ID(quad) (CSR1212_KV_KEY(quad) & CSR1212_KV_KEY_ID_MASK) #define CSR1212_KV_VAL(quad) (CSR1212_BE32_TO_CPU(quad) & CSR1212_KV_VAL_MASK) #define CSR1212_SET_KV_KEY(quad, key) ((quad) = \ CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | ((key) << CSR1212_KV_KEY_SHIFT))) #define CSR1212_SET_KV_VAL(quad, val) ((quad) = \ CSR1212_CPU_TO_BE32((CSR1212_KV_KEY(quad) << CSR1212_KV_KEY_SHIFT) | (val))) #define CSR1212_SET_KV_TYPEID(quad, type, id) ((quad) = \ CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | \ (((((type) & CSR1212_KV_KEY_TYPE_MASK) << CSR1212_KV_KEY_TYPE_SHIFT) | \ ((id) & CSR1212_KV_KEY_ID_MASK)) << CSR1212_KV_KEY_SHIFT))) typedef u_int32_t csr1212_quad_t; struct csr1212_keyval_img { u_int16_t length; u_int16_t crc; /* Must be last */ csr1212_quad_t data[0]; /* older gcc can't handle [] which is standard */ }; struct csr1212_leaf { int len; u_int32_t *data; }; struct csr1212_dentry { struct csr1212_dentry *next, *prev; struct csr1212_keyval *kv; }; struct csr1212_directory { int len; struct csr1212_dentry *dentries_head, *dentries_tail; }; struct csr1212_keyval { struct { u_int8_t type; u_int8_t id; } key; union { u_int32_t immediate; u_int32_t csr_offset; struct csr1212_leaf leaf; struct csr1212_directory directory; } value; struct csr1212_keyval *associate; int refcnt; /* used in generating and/or parsing CSR image */ struct csr1212_keyval *next, *prev; /* flat list of CSR elements */ u_int32_t offset; /* position in CSR from 0xffff f000 0000 */ u_int8_t valid; /* flag indicating keyval has valid data*/ }; struct csr1212_cache_region { struct csr1212_cache_region *next, *prev; u_int32_t offset_start; /* inclusive */ u_int32_t offset_end; /* exclusive */ }; struct csr1212_csr_rom_cache { struct csr1212_csr_rom_cache *next, *prev; struct csr1212_cache_region *filled_head, *filled_tail; struct csr1212_keyval *layout_head, *layout_tail; size_t size; u_int32_t offset; struct csr1212_keyval *ext_rom; size_t len; /* Must be last */ u_int32_t data[0]; /* older gcc can't handle [] which is standard */ }; struct csr1212_csr { size_t bus_info_len; /* bus info block length in bytes */ size_t crc_len; /* crc length in bytes */ u_int32_t *bus_info_data; /* bus info data incl bus name and EUI */ void *private_data; /* private_data, bus specific data */ struct csr1212_bus_ops *ops; struct csr1212_keyval *root_kv; int max_rom; /* max bytes readable in Config ROM region */ /* Items below used for image parsing and generation */ struct csr1212_csr_rom_cache *cache_head, *cache_tail; }; struct csr1212_bus_ops { /* This function is used by csr1212 to read additional information * from remote nodes when parsing a Config ROM (i.e., read Config ROM * entries located in the Units Space. Must return 0 on success * anything else indicates an error. */ int (*bus_read) (struct csr1212_csr *csr, u_int64_t addr, u_int16_t length, void *buffer, void *private_data); /* This function is used by csr1212 to allocate a region in units space * in the event that Config ROM entries don't all fit in the predefined * 1K region. The void *private_data parameter is private_data member of struct * csr1212_csr. */ u_int64_t (*allocate_addr_range) (u_int64_t size, u_int32_t alignment, void *private_data); /* This function is used by csr1212 to release a region in units space * that is no longer needed. */ void (*release_addr) (u_int64_t addr, void *private_data); /* This function is used by csr1212 to determine the max read request * supported by a remote node when reading the ConfigROM space. Must * return 0, 1, or 2 per IEEE 1212. */ int (*get_max_rom) (u_int32_t *bus_info, void *private_data); }; /* Descriptor Leaf manipulation macros */ #define CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT 24 #define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK 0xffffff #define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u_int32_t)) #define CSR1212_DESCRIPTOR_LEAF_TYPE(kv) \ (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) >> CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) #define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) \ (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) & \ CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK) #define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ (&((kv)->value.leaf.data[1])) #define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \ ((kv)->value.leaf.data[0] = \ CSR1212_CPU_TO_BE32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \ ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT))) #define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \ ((kv)->value.leaf.data[0] = \ CSR1212_CPU_TO_BE32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \ CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \ ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK))) /* Text Descriptor Leaf manipulation macros */ #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT 28 #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK 0xf /* after shift */ #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT 16 #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK 0xfff /* after shift */ #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u_int32_t)) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) \ (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) \ ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) \ (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) & \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv) \ (&((kv)->value.leaf.data[2])) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, width) \ ((kv)->value.leaf.data[1] = \ ((kv)->value.leaf.data[1] & \ CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK << \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT))) | \ CSR1212_CPU_TO_BE32(((width) & \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK) << \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT)) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, char_set) \ ((kv)->value.leaf.data[1] = \ ((kv)->value.leaf.data[1] & \ CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK << \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT))) | \ CSR1212_CPU_TO_BE32(((char_set) & \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) << \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT)) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \ ((kv)->value.leaf.data[1] = \ ((kv)->value.leaf.data[1] & \ CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \ CSR1212_CPU_TO_BE32(((language) & \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) /* Icon Descriptor Leaf manipulation macros */ #define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK 0xffffff #define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT 30 #define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK 0x3 /* after shift */ #define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT 16 #define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK 0xf /* after shift */ #define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff #define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT 16 #define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK 0xffff /* after shift */ #define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK 0xffff #define CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD (3 * sizeof(u_int32_t)) #define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION(kv) \ (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[2]) & \ CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK) #define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv) \ (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \ CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT) #define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv) \ ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \ CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT) & \ CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK) #define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE(kv) \ (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) & \ CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK) #define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN(kv) \ ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) >> \ CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_SHIFT) & \ CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_MASK) #define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN(kv) \ (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) & \ CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK) #define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv) \ (&((kv)->value.leaf.data[5])) static inline u_int32_t *CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(struct csr1212_keyval *kv) { static const int pd[4] = { 0, 4, 16, 256 }; static const int cs[16] = { 4, 2 }; int ps = pd[CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv)]; return &kv->value.leaf.data[5 + (ps * cs[CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv)]) / sizeof(u_int32_t)]; } #define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version) \ ((kv)->value.leaf.data[2] = \ ((kv)->value.leaf.data[2] & \ CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK))) | \ CSR1212_CPU_TO_BE32(((version) & \ CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK))) #define CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth) \ ((kv)->value.leaf.data[3] = \ ((kv)->value.leaf.data[3] & \ CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK << \ CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT))) | \ CSR1212_CPU_TO_BE32(((palette_depth) & \ CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK) << \ CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT)) #define CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space) \ ((kv)->value.leaf.data[3] = \ ((kv)->value.leaf.data[3] & \ CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK << \ CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT))) | \ CSR1212_CPU_TO_BE32(((color_space) & \ CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK) << \ CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT)) #define CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \ ((kv)->value.leaf.data[3] = \ ((kv)->value.leaf.data[3] & \ CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \ CSR1212_CPU_TO_BE32(((language) & \ CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK))) #define CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan) \ ((kv)->value.leaf.data[4] = \ ((kv)->value.leaf.data[4] & \ CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK << \ CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT))) | \ CSR1212_CPU_TO_BE32(((hscan) & \ CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK) << \ CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT)) #define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan) \ ((kv)->value.leaf.data[4] = \ (((kv)->value.leaf.data[4] & \ CSR1212_CPU_TO_BE32(~CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK))) | \ CSR1212_CPU_TO_BE32(((vscan) & \ CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK))) /* Modifiable Descriptor Leaf manipulation macros */ #define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT 16 #define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK 0xffff #define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_SHIFT 32 #define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK 0xffff #define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK 0xffffffffULL #define CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE(kv) \ CSR1212_BE16_TO_CPU((kv)->value.leaf.data[0] >> CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE_SHIFT) #define CSR1212_MODIFIABLE_DESCRIPTOR_ADDRESS(kv) \ (CSR1212_BE16_TO_CPU(((u_int64_t)((kv)->value.leaf.data[0])) << \ CSR1212_MODIFIABLE_DESCRIPTOR_ADDR_HI_SHIFT) | \ CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1])) #define CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, size) \ ((kv)->value.leaf.data[0] = \ ((kv)->value.leaf.data[0] & \ CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK << \ CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT))) | \ CSR1212_CPU_TO_BE32(((size) & \ CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK) << \ CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT)) #define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, addr) \ ((kv)->value.leaf.data[0] = \ ((kv)->value.leaf.data[0] & \ CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK))) | \ CSR1212_CPU_TO_BE32(((addr) & \ CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK))) #define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, addr) \ ((kv)->value.leaf.data[1] = \ CSR1212_CPU_TO_BE32(addr & CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK)) /* The following 2 function are for creating new Configuration ROM trees. The * first function is used for both creating local trees and parsing remote * trees. The second function adds pertinent information to local Configuration * ROM trees - namely data for the bus information block. */ extern struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, size_t bus_info_size, void *private_data); extern void csr1212_init_local_csr(struct csr1212_csr *csr, const u_int32_t *bus_info_data, int max_rom); /* The following function destroys a Configuration ROM tree and release all * memory taken by the tree. */ extern void csr1212_destroy_csr(struct csr1212_csr *csr); /* The following set of functions are fore creating new keyvals for placement in * a Configuration ROM tree. Code that creates new keyvals with these functions * must release those keyvals with csr1212_release_keyval() when they are no * longer needed. */ extern struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value); extern struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, size_t data_len); extern struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, u_int32_t csr_offset); extern struct csr1212_keyval *csr1212_new_directory(u_int8_t key); extern struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec, u_int32_t key, u_int32_t value); extern struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec, u_int32_t key, const void *data, size_t data_len); extern struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, u_int32_t specifier_id, const void *data, size_t data_len); extern struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth, u_int16_t cset, u_int16_t language, const void *data, size_t data_len); extern struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s); extern struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version, u_int8_t palette_depth, u_int8_t color_space, u_int16_t language, u_int16_t hscan, u_int16_t vscan, u_int32_t *palette, u_int32_t *pixels); extern struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size, u_int64_t address); extern struct csr1212_keyval *csr1212_new_keyword_leaf(int strc, const char *strv[]); /* The following functions manage association between keyvals. Typically, * Descriptor Leaves and Directories will be associated with another keyval and * it is desirable for the Descriptor keyval to be place immediately after the * keyval that it is associated with.*/ extern int csr1212_associate_keyval(struct csr1212_keyval *kv, struct csr1212_keyval *associate); extern void csr1212_disassociate_keyval(struct csr1212_keyval *kv); /* The following functions manage the association of a keyval and directories. * A keyval may be attached to more than one directory. */ extern int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, struct csr1212_keyval *kv); extern void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, struct csr1212_keyval *kv); /* The following functions create a Configuration ROM image from the tree of * keyvals provided. csr1212_generate_csr_image() creates a complete image in * the list of caches available via csr->cache_head. The other functions are * provided should there be a need to create a flat image without restrictions * placed by IEEE 1212. */ extern struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, struct csr1212_keyval *start_kv, int start_pos); extern size_t csr1212_generate_layout_order(struct csr1212_keyval *kv); extern void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache); extern int csr1212_generate_csr_image(struct csr1212_csr *csr); /* This is a convience function for reading a block of data out of one of the * caches in the csr->cache_head list. */ extern int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer, u_int32_t len); /* The following functions are in place for parsing Configuration ROM images. * csr1212_parse_keyval() is used should there be a need to directly parse a * Configuration ROM directly. */ extern int csr1212_parse_keyval(struct csr1212_keyval *kv, struct csr1212_csr_rom_cache *cache); extern int csr1212_parse_csr(struct csr1212_csr *csr); /* These are internal functions referenced by inline functions below. */ extern int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv); extern void _csr1212_destroy_keyval(struct csr1212_keyval *kv); /* This function allocates a new cache which may be used for either parsing or * generating sub-sets of Configuration ROM images. */ static inline struct csr1212_csr_rom_cache *csr1212_rom_cache_malloc(u_int32_t offset, size_t size) { struct csr1212_csr_rom_cache *cache; cache = (struct csr1212_csr_rom_cache*)CSR1212_MALLOC(sizeof(struct csr1212_csr_rom_cache) + size); if (!cache) return NULL; cache->next = NULL; cache->prev = NULL; cache->filled_head = NULL; cache->filled_tail = NULL; cache->layout_head = NULL; cache->layout_tail = NULL; cache->offset = offset; cache->size = size; cache->ext_rom = NULL; return cache; } /* This function ensures that a keyval contains data when referencing a keyval * created by parsing a Configuration ROM. */ static inline struct csr1212_keyval *csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) { if (!kv) return NULL; if (!kv->valid) if (_csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS) return NULL; return kv; } /* This function increments the reference count for a keyval should there be a * need for code to retain a keyval that has been parsed. */ static inline void csr1212_keep_keyval(struct csr1212_keyval *kv) { kv->refcnt++; } /* This function decrements a keyval's reference count and will destroy the * keyval when there are no more users of the keyval. This should be called by * any code that calls csr1212_keep_keyval() or any of the keyval creation * routines csr1212_new_*(). */ static inline void csr1212_release_keyval(struct csr1212_keyval *kv) { if (kv->refcnt > 1) kv->refcnt--; else _csr1212_destroy_keyval(kv); } /* * This macro allows for looping over the keyval entries in a directory and it * ensures that keyvals from remote ConfigROMs are parsed properly. * * _csr is a struct csr1212_csr * that points to CSR associated with dir. * _kv is a struct csr1212_keyval * that'll point to the current keyval (loop index). * _dir is a struct csr1212_keyval * that points to the directory to be looped. * _pos is a struct csr1212_dentry * that is used internally for indexing. * * kv will be NULL upon exit of the loop. */ #define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos) \ for (csr1212_get_keyval((_csr), (_dir)), \ _pos = (_dir)->value.directory.dentries_head, \ _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL; \ (_kv) && (_pos); \ (_kv->associate == NULL) ? \ ((_pos = _pos->next), \ (_kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : \ NULL)) : \ (_kv = csr1212_get_keyval((_csr), _kv->associate))) #ifdef __cplusplus } #endif #endif /* __CSR1212_H__ */ libffado-2.4.5/src/libieee1394/cycletimer.h0000644000175000001440000004204714206145246017712 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* Definitions and utility macro's to handle the ISO cycle timer */ #ifndef __CYCLETIMER_H__ #define __CYCLETIMER_H__ #include "debugmodule/debugmodule.h" #include #define CSR_CYCLE_TIME 0x200 #define CSR_REGISTER_BASE 0xfffff0000000ULL #define CYCLES_PER_SECOND 8000U #define TICKS_PER_CYCLE 3072U #define TICKS_PER_HALFCYCLE (3072U/2U) #define TICKS_PER_SECOND 24576000UL #define TICKS_PER_USEC (24.576000) #define USECS_PER_TICK (1.0/TICKS_PER_USEC) #define USECS_PER_CYCLE (125U) #define CYCLE_TIMER_GET_SECS(x) ((((x) & 0xFE000000UL) >> 25)) #define CYCLE_TIMER_GET_CYCLES(x) ((((x) & 0x01FFF000UL) >> 12)) #define CYCLE_TIMER_GET_OFFSET(x) ((((x) & 0x00000FFFUL))) #define CYCLE_TIMER_SET_SECS(v, x) (((v) & ~0xFE000000UL) | (((x) & 0x7F) << 25)) #define CYCLE_TIMER_SET_CYCLES(v, x) ((((v) & ~0x01FFF000UL) | (((x) & 0x1FFF) << 12))) #define CYCLE_TIMER_SET_OFFSET(v, x) ((((v) & ~0x00000FFFUL) | ((x) & 0xFFF))) #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: %" PRIu64 "\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: %" PRId64 "\n",x); } #endif return (int64_t)x; } /** * @brief Wraps both at minimum and maximum value for ticks * * The input value is wrapped to the maximum value of the cycle * timer, in ticks (128sec * 24576000 ticks/sec), and * to the minimum value of the cycle timer, in ticks (= 0). * * @param x value to wrap * @return wrapped value */ static inline int64_t wrapAtMinMaxTicks(int64_t x) { if (x < 0) { x += TICKS_PER_SECOND * 128L; } else if (x >= (int64_t)(TICKS_PER_SECOND * 128L)) { x -= TICKS_PER_SECOND * 128L; } #ifdef DEBUG if (x >= (int64_t)(TICKS_PER_SECOND * 128L)) { debugWarning("insufficient wrapping (max): %" PRIu64 "\n",x); } if (x < 0) { debugWarning("insufficient wrapping (min): %" PRId64 "\n",x); } #endif return x; } /** * @brief Computes the sum of two cycle values * * This function computes a sum between cycles * such that it respects wrapping (at 8000 cycles). * * The passed arguments are assumed to be valid cycle numbers, * i.e. they should be wrapped at 8000 cycles * * See addTicks * * @param x First cycle value * @param y Second cycle value * @return the sum x+y, wrapped */ static inline unsigned int addCycles(unsigned int x, unsigned int y) { unsigned int sum = x + y; #ifdef DEBUG if (x >= CYCLES_PER_SECOND || y >= CYCLES_PER_SECOND ) { debugWarning("At least one argument not wrapped correctly: x=%u, y=%u\n",x,y); } #endif // since both x and y are < CYCLES_PER_SECOND this should be enough to unwrap if (sum > CYCLES_PER_SECOND) sum -= CYCLES_PER_SECOND; return sum; } /** * @brief Computes a difference between cycles * * This function computes a difference between cycles * such that it respects wrapping (at 8000 cycles). * * See diffTicks * * @param x First cycle value * @param y Second cycle value * @return the difference x-y, unwrapped */ static inline int diffCycles(unsigned int x, unsigned int y) { int diff = (int)x - (int)y; // the maximal difference we allow (4000 cycles) const int max=CYCLES_PER_SECOND/2; if(diff > max) { diff -= CYCLES_PER_SECOND; } else if (diff < -max) { diff += CYCLES_PER_SECOND; } return diff; } /** * @brief Computes a difference between timestamps * * This function computes a difference between timestamps * such that it respects wrapping. * * If x wraps around, but y doesn't, the result of x-y is * negative and very large. However the real difference is * not large. It can be calculated by unwrapping x and then * calculating x-y. * * @param x First timestamp * @param y Second timestamp * @return the difference x-y, unwrapped */ static inline int64_t diffTicks(int64_t x, int64_t y) { int64_t diff=(int64_t)x - (int64_t)y; // the maximal difference we allow (64secs) const int64_t wrapvalue=((int64_t)TICKS_PER_SECOND)*128LL; const int64_t max=wrapvalue/2LL; if(diff > max) { // this means that y has wrapped, but // x has not. we should unwrap y // by adding TICKS_PER_SECOND*128L, meaning that we should substract // this value from diff diff -= wrapvalue; } else if (diff < -max) { // this means that x has wrapped, but // y has not. we should unwrap x // by adding TICKS_PER_SECOND*128L, meaning that we should add // this value to diff diff += wrapvalue; } #ifdef DEBUG if(diff > max || diff < -max) { debugWarning("difference does not make any sense\n"); debugWarning("diff=%" PRId64 " max=%" PRId64 "\n", diff, max); } #endif return (int64_t)diff; } /** * @brief Computes a sum of timestamps * * This function computes a sum of timestamps in ticks, * wrapping the result if necessary. * * @param x First timestamp * @param y Second timestamp * @return the sum x+y, wrapped */ static inline uint64_t addTicks(uint64_t x, uint64_t y) { uint64_t sum=x+y; return wrapAtMaxTicks(sum); } /** * @brief Computes a substraction of timestamps * * This function computes a substraction of timestamps in ticks, * wrapping the result if necessary. * * @param x First timestamp * @param y Second timestamp * @return the difference x-y, wrapped */ static inline uint64_t substractTicks(uint64_t x, uint64_t y) { int64_t subs=x-y; return wrapAtMinTicks(subs); } /** * @brief Converts a received SYT timestamp to a full timestamp in ticks. * * * @param syt_timestamp The SYT timestamp as present in the packet * @param rcv_cycle The cycle this timestamp was received on * @param ctr_now The current value of the cycle timer ('now') * @return */ static inline uint64_t sytRecvToFullTicks(uint64_t syt_timestamp, unsigned int rcv_cycle, uint64_t ctr_now) { uint64_t timestamp; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "SYT=%" PRIX64 " CY=%u CTR=%08" PRIX64 "\n", syt_timestamp, rcv_cycle, ctr_now); // reconstruct the full cycle uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now); uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now); // check for bogus ctr // the cycle timer should be ahead of the receive timer int diff_cycles = diffCycles(cc_cycles, rcv_cycle); if (diff_cycles<0) { debugWarning("current cycle timer not ahead of receive cycle: rcv: %u / cc: %" PRIu64 " (%d)\n", rcv_cycle, cc_cycles, diff_cycles); } // the cycletimer has wrapped since this packet was received // we want cc_seconds to reflect the 'seconds' at the point this // was received if (rcv_cycle>cc_cycles && (diff_cycles>=0)) { if (cc_seconds) { cc_seconds--; } else { // seconds has wrapped around, so we'd better not substract 1 // the good value is 127 cc_seconds=127; } } // reconstruct the top part of the timestamp using the current cycle number uint64_t rcv_cycle_masked=rcv_cycle & 0xF; uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp); // if this is true, wraparound has occurred, undo this wraparound if(syt_cycle= 8000) { debugWarning("insufficient unwrapping\n"); } #endif timestamp = new_cycles * TICKS_PER_CYCLE; // add one second due to wraparound timestamp += TICKS_PER_SECOND; } timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp); timestamp = addTicks(timestamp, cc_seconds * TICKS_PER_SECOND); #ifdef DEBUG if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) { debugWarning("back-converted timestamp not equal to SYT\n"); debugWarning("TS=%011" PRIu64 " TSC=%08" PRIX64 " SYT=%04" PRIX64 "\n", timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp); } #endif return timestamp; } /** * @brief Converts a received SYT timestamp to a full timestamp in ticks. * * * @param syt_timestamp The SYT timestamp as present in the packet * @param rcv_ctr The CTR value this timestamp was received on (offset can be 0) * @return */ static inline uint64_t sytRecvToFullTicks2(uint64_t syt_timestamp, uint32_t rcv_ctr) { uint64_t timestamp; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "SYT=%04" PRIX64 " RCV_CTR=%08X\n", syt_timestamp, rcv_ctr); // reconstruct the top part of the timestamp using the current cycle number unsigned int rcv_cycle = CYCLE_TIMER_GET_CYCLES(rcv_ctr); unsigned int rcv_cycle_masked = rcv_cycle & 0xF; unsigned int syt_cycle = CYCLE_TIMER_GET_CYCLES(syt_timestamp); // if this is true, wraparound has occurred, undo this wraparound if(syt_cycle= 8000) { debugWarning("insufficient unwrapping\n"); } #endif timestamp = rcv_cycle * TICKS_PER_CYCLE; // add one second due to wraparound timestamp += TICKS_PER_SECOND; } timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp); timestamp = addTicks(timestamp, CYCLE_TIMER_GET_SECS(rcv_ctr) * TICKS_PER_SECOND); #ifdef DEBUG if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) { debugWarning("back-converted timestamp not equal to SYT\n"); debugWarning("TS=%011" PRIu64 " TSC=%08" PRIX64 " SYT=%04" PRIX64 "\n", timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp); } #endif return timestamp; } /** * @brief Converts a transmit SYT timestamp to a full timestamp in ticks. * * The difference between sytRecvToFullTicks and sytXmitToFullTicks is * the way SYT cycle wraparound is detected: in the receive version, * wraparound is present if rcv_cycle > current_cycle. In the xmit * version this is when current_cycle > xmt_cycle. * * @param syt_timestamp The SYT timestamp as present in the packet * @param xmt_cycle The cycle this timestamp was received on * @param ctr_now The current value of the cycle timer ('now') * @return */ static inline uint64_t sytXmitToFullTicks(uint64_t syt_timestamp, unsigned int xmt_cycle, uint64_t ctr_now) { uint64_t timestamp; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "SYT=%08" PRIX64 " CY=%04X CTR=%08" PRIX64 "\n", syt_timestamp, xmt_cycle, ctr_now); // reconstruct the full cycle uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now); uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now); // check for bogus CTR int diff_cycles = diffCycles(xmt_cycle, cc_cycles); if (diff_cycles<0) { debugWarning("xmit cycle not ahead of current cycle: xmt: %u / cc: %" PRIu64 " (%d)\n", xmt_cycle, cc_cycles, diff_cycles); } // the cycletimer has wrapped since this packet was received // we want cc_seconds to reflect the 'seconds' at the point this // is to be transmitted if (cc_cycles>xmt_cycle && (diff_cycles>=0)) { if (cc_seconds) { cc_seconds--; } else { // seconds has wrapped around, so we'd better not substract 1 // the good value is 127 cc_seconds=127; } } // reconstruct the top part of the timestamp using the current cycle number uint64_t xmt_cycle_masked=xmt_cycle & 0xF; uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp); // if this is true, wraparound has occurred, undo this wraparound if(syt_cycle= 8000) { debugWarning("insufficient unwrapping\n"); } #endif timestamp = new_cycles * TICKS_PER_CYCLE; // add one second due to wraparound timestamp += TICKS_PER_SECOND; } timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp); timestamp = addTicks(timestamp, cc_seconds * TICKS_PER_SECOND); #ifdef DEBUG if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) { debugWarning("back-converted timestamp not equal to SYT\n"); debugWarning("TS=%011" PRIu64 " TSC=%08" PRIX64 " SYT=%04" PRIX64 "\n", timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp); } #endif return timestamp; } #endif // __CYCLETIMER_H__ libffado-2.4.5/src/libieee1394/ieee1394service.cpp0000644000175000001440000016334014206145246020716 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2012 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "ieee1394service.h" #include "cycletimer.h" #include "IsoHandlerManager.h" #include "CycleTimerHelper.h" #include #include #include #include "libutil/SystemTimeSource.h" #include "libutil/Watchdog.h" #include "libutil/PosixMutex.h" #include "libutil/PosixThread.h" #include "libutil/Configuration.h" #include #include "libutil/ByteSwap.h" #include #include #include // Permit linking against older libraw1394 which didn't include this // function. #ifdef __GNUC__ #ifdef __APPLE__ #define WEAK_ATTRIBUTE weak_import #else #define WEAK_ATTRIBUTE __weak__ #endif int raw1394_read_cycle_timer_and_clock(raw1394handle_t handle, u_int32_t *cycle_timer, u_int64_t *local_time, clockid_t clk_id) __attribute__((WEAK_ATTRIBUTE)); #endif using namespace std; IMPL_DEBUG_MODULE( Ieee1394Service, Ieee1394Service, DEBUG_LEVEL_NORMAL ); Ieee1394Service::Ieee1394Service() : m_configuration( NULL ) , m_resetHelper( NULL ) , m_armHelperNormal( NULL ) , m_armHelperRealtime( NULL ) , m_handle( 0 ) , m_handle_lock( new Util::PosixMutex("SRVCHND") ) , m_util_handle( 0 ) , m_port( -1 ) , m_realtime ( false ) , m_base_priority ( 0 ) , m_pIsoManager( new IsoHandlerManager( *this ) ) , m_pCTRHelper ( new CycleTimerHelper( *this, IEEE1394SERVICE_CYCLETIMER_DLL_UPDATE_INTERVAL_USEC ) ) , m_have_new_ctr_read ( false ) , m_filterFCPResponse ( false ) , m_pWatchdog ( new Util::Watchdog() ) { for (unsigned int i=0; i<64; i++) { m_channels[i].channel=-1; m_channels[i].bandwidth=-1; m_channels[i].alloctype=AllocFree; m_channels[i].xmit_node=0xFFFF; m_channels[i].xmit_plug=-1; m_channels[i].recv_node=0xFFFF; m_channels[i].recv_plug=-1; } } Ieee1394Service::Ieee1394Service(bool rt, int prio) : m_configuration( NULL ) , m_resetHelper( NULL ) , m_armHelperNormal( NULL ) , m_armHelperRealtime( NULL ) , m_handle( 0 ) , m_handle_lock( new Util::PosixMutex("SRVCHND") ) , m_util_handle( 0 ) , m_port( -1 ) , m_realtime ( rt ) , m_base_priority ( prio ) , m_pIsoManager( new IsoHandlerManager( *this, rt, prio ) ) , m_pCTRHelper ( new CycleTimerHelper( *this, IEEE1394SERVICE_CYCLETIMER_DLL_UPDATE_INTERVAL_USEC, rt && IEEE1394SERVICE_CYCLETIMER_HELPER_RUN_REALTIME, IEEE1394SERVICE_CYCLETIMER_HELPER_PRIO ) ) , m_have_new_ctr_read ( false ) , m_filterFCPResponse ( false ) , m_pWatchdog ( new Util::Watchdog() ) { for (unsigned int i=0; i<64; i++) { m_channels[i].channel=-1; m_channels[i].bandwidth=-1; m_channels[i].alloctype=AllocFree; m_channels[i].xmit_node=0xFFFF; m_channels[i].xmit_plug=-1; m_channels[i].recv_node=0xFFFF; m_channels[i].recv_plug=-1; } } Ieee1394Service::~Ieee1394Service() { delete m_pIsoManager; delete m_pCTRHelper; m_resetHelper->Stop(); m_armHelperNormal->Stop(); m_armHelperRealtime->Stop(); for ( arm_handler_vec_t::iterator it = m_armHandlers.begin(); it != m_armHandlers.end(); ++it ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Unregistering ARM handler for 0x%016" PRIX64 "\n", (*it)->getStart()); if(m_armHelperNormal) { int err = raw1394_arm_unregister(m_armHelperNormal->get1394Handle(), (*it)->getStart()); if (err) { debugError(" Failed to unregister ARM handler for 0x%016" PRIX64 "\n", (*it)->getStart()); debugError(" Error: %s\n", strerror(errno)); } } else { debugWarning("ARM handler registered without valid ARM helper thread\n"); } } delete m_pWatchdog; if ( m_handle ) { raw1394_destroy_handle( m_handle ); } delete m_handle_lock; if(m_resetHelper) delete m_resetHelper; if(m_armHelperNormal) delete m_armHelperNormal; if(m_armHelperRealtime) delete m_armHelperRealtime; if ( m_util_handle ) { raw1394_destroy_handle( m_util_handle ); } } bool Ieee1394Service::useConfiguration(Util::Configuration *c) { m_configuration = c; return configurationUpdated(); } bool Ieee1394Service::configurationUpdated() { if(m_configuration) { } return true; } #define DEVICEFAILTEXT "Could not get libraw1394 handle.\n\ This usually means:\n\ a) The device-node /dev/raw1394 doesn't exists because you don't have a\n\ (recognized) FireWire controller.\n \ b) The modules needed aren't loaded. This is not in the scope of ffado but of\n\ your distribution, so if you have a FireWire controller that should be\n\ supported and the modules aren't loaded, file a bug with your distributions\n\ bug tracker.\n \ c) You don't have permissions to access /dev/raw1394. 'ls -l /dev/raw1394'\n\ shows the device-node with its permissions, make sure you belong to the\n\ right group and the group is allowed to access the device.\n" int Ieee1394Service::detectNbPorts() { raw1394handle_t tmp_handle = raw1394_new_handle(); if ( tmp_handle == NULL ) { debugError(DEVICEFAILTEXT); return -1; } struct raw1394_portinfo pinf[IEEE1394SERVICE_MAX_FIREWIRE_PORTS]; int nb_detected_ports = raw1394_get_port_info(tmp_handle, pinf, IEEE1394SERVICE_MAX_FIREWIRE_PORTS); raw1394_destroy_handle(tmp_handle); if (nb_detected_ports < 0) { debugError("Failed to detect number of ports\n"); return -1; } return nb_detected_ports; } void Ieee1394Service::doBusReset() { debugOutput(DEBUG_LEVEL_VERBOSE, "Issue bus reset on service %p (port %d).\n", this, getPort()); raw1394_reset_bus(m_handle); } /** * This function waits until there are no bus resets generated in a sleep_time_ms interval * @param nb_tries number of tries to take * @param sleep_time_ms sleep between tries * @return true if the storm passed */ bool Ieee1394Service::waitForBusResetStormToEnd( int nb_tries, int sleep_time_ms ) { unsigned int gen_current; do { gen_current = getGeneration(); debugOutput(DEBUG_LEVEL_VERBOSE, "Waiting... (gen: %u)\n", gen_current); // wait for a while Util::SystemTimeSource::SleepUsecRelative( sleep_time_ms * 1000); } while (gen_current != getGeneration() && --nb_tries); debugOutput(DEBUG_LEVEL_VERBOSE, "Bus reset storm over at gen: %u\n", gen_current); if (!nb_tries) { debugError( "Bus reset storm did not stop on time...\n"); return false; } return true; } bool Ieee1394Service::initialize( int port ) { using namespace std; int nb_ports = detectNbPorts(); if (port + 1 > nb_ports) { debugFatal("Requested port (%d) out of range (# ports: %d)\n", port, nb_ports); } m_port = port; if(!m_pWatchdog) { debugError("No valid RT watchdog found.\n"); return false; } if(!m_pWatchdog->start()) { debugError("Could not start RT watchdog.\n"); return false; } m_handle = raw1394_new_handle_on_port( port ); if ( !m_handle ) { if ( !errno ) { debugFatal("libraw1394 not compatible\n"); } else { debugFatal("Ieee1394Service::initialize: Could not get 1394 handle: %s\n", strerror(errno) ); debugFatal("Is ieee1394 and raw1394 driver loaded?\n"); } return false; } // helper threads for all sorts of ASYNC events // note: m_port has to be set! m_resetHelper = new HelperThread(*this, "BUSRST"); if ( !m_resetHelper ) { debugFatal("Could not allocate busreset handler helper\n"); return false; } m_armHelperNormal = new HelperThread(*this, "ARMSTD"); if ( !m_armHelperNormal ) { debugFatal("Could not allocate standard ARM handler helper\n"); return false; } m_armHelperRealtime = new HelperThread(*this, "ARMRT", m_realtime, m_base_priority); if ( !m_armHelperRealtime ) { debugFatal("Could not allocate realtime ARM handler helper\n"); return false; } // start helper threads if(!m_resetHelper->Start()) { debugFatal("Could not start busreset helper thread\n"); return false; } if(!m_armHelperNormal->Start()) { debugFatal("Could not start standard ARM helper thread\n"); return false; } if(!m_armHelperRealtime->Start()) { debugFatal("Could not start realtime ARM helper thread\n"); return false; } // attach the reset and ARM handlers // NOTE: the handlers have to be started first, or there is no 1394handle raw1394_set_bus_reset_handler( m_resetHelper->get1394Handle(), this->resetHandlerLowLevel ); m_default_arm_handler = raw1394_set_arm_tag_handler( m_armHelperNormal->get1394Handle(), this->armHandlerLowLevel ); // utility handle (used to read the CTR register) 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; m_have_read_ctr_and_clock = false; err = raw1394_read_cycle_timer(m_util_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 { m_have_new_ctr_read = true; // Only if raw1394_read_cycle_timer() is present is it worth even // considering the option that raw1394_read_cycle_timer_and_clock() // might be available. if (raw1394_read_cycle_timer_and_clock != NULL) { err = raw1394_read_cycle_timer_and_clock(m_util_handle, &cycle_timer, &local_time, CLOCK_MONOTONIC); if (!err && Util::SystemTimeSource::setSource(CLOCK_MONOTONIC)==true) m_have_read_ctr_and_clock = true; } if (m_have_read_ctr_and_clock) { debugOutput(DEBUG_LEVEL_VERBOSE, "This system supports the raw1394_read_cycle_timer_and_clock call and the\n"); debugOutput(DEBUG_LEVEL_VERBOSE, "CLOCK_MONOTONIC clock source; using them.\n"); } else { debugOutput(DEBUG_LEVEL_VERBOSE, "This system supports the raw1394_read_cycle_timer call, using it.\n"); debugOutput(DEBUG_LEVEL_NORMAL, "The raw1394_read_cycle_timer_and_clock call and/or the CLOCK_MONOTONIC\n"); debugOutput(DEBUG_LEVEL_NORMAL, "clock source is not available.\n"); debugOutput(DEBUG_LEVEL_NORMAL, "Fallback to raw1394_read_cycle_timer.\n"); debugOutput(DEBUG_LEVEL_NORMAL, "FFADO may be susceptible to NTP-induced clock discontinuities.\n"); debugOutput(DEBUG_LEVEL_NORMAL, "If this is an issue, upgrade libraw1394 to version 2.1.0 or later and/or\n"); debugOutput(DEBUG_LEVEL_NORMAL, "kernel 2.6.36 or later.\n"); } } // obtain port name raw1394handle_t tmp_handle = raw1394_new_handle(); if ( tmp_handle == NULL ) { debugError("Could not get temporary 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"; } // set userdata raw1394_set_userdata( m_handle, this ); raw1394_set_userdata( m_util_handle, this ); // increase the split-transaction timeout if required (e.g. for bebob's) int split_timeout = IEEE1394SERVICE_MIN_SPLIT_TIMEOUT_USECS; if(m_configuration) { m_configuration->getValueForSetting("ieee1394.min_split_timeout_usecs", split_timeout); } // set SPLIT_TIMEOUT to one second to cope with DM1x00 devices that // send responses regardless of the timeout int timeout = getSplitTimeoutUsecs(getLocalNodeId()); debugOutput(DEBUG_LEVEL_VERBOSE, "Minimum SPLIT_TIMEOUT: %d. Current: %d\n", split_timeout, timeout); if (timeout < split_timeout) { if(!setSplitTimeoutUsecs(getLocalNodeId(), split_timeout+124)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Could not set SPLIT_TIMEOUT to min requested (%d)\n", split_timeout); } timeout = getSplitTimeoutUsecs(getLocalNodeId()); if (timeout < split_timeout) { debugOutput(DEBUG_LEVEL_VERBOSE, "Set SPLIT_TIMEOUT to min requested (%d) did not succeed\n", split_timeout); } } // init helpers 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; } // 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; if (priority < THREAD_MIN_RTPRIO) priority = THREAD_MIN_RTPRIO; m_base_priority = priority; m_realtime = rt; if (m_pIsoManager) { debugOutput(DEBUG_LEVEL_VERBOSE, "Switching IsoManager to (rt=%d, prio=%d)\n", rt, priority); result &= m_pIsoManager->setThreadParameters(rt, priority); } //else debugError("Bogus isomanager\n"); if (m_pCTRHelper) { debugOutput(DEBUG_LEVEL_VERBOSE, "Switching CycleTimerHelper to (rt=%d, prio=%d)\n", rt && IEEE1394SERVICE_CYCLETIMER_HELPER_RUN_REALTIME, IEEE1394SERVICE_CYCLETIMER_HELPER_PRIO); result &= m_pCTRHelper->setThreadParameters(rt && IEEE1394SERVICE_CYCLETIMER_HELPER_RUN_REALTIME, IEEE1394SERVICE_CYCLETIMER_HELPER_PRIO); } //else debugError("Bogus CTR helper\n"); if(m_armHelperRealtime) { m_armHelperRealtime->setThreadParameters(rt, priority); } //else debugError("Bogus RT ARM helper\n"); return result; } int Ieee1394Service::getNodeCount() { Util::MutexLockHelper lock(*m_handle_lock); return raw1394_get_nodecount( m_handle ); } nodeid_t Ieee1394Service::getLocalNodeId() { Util::MutexLockHelper lock(*m_handle_lock); return raw1394_get_local_id(m_handle) & 0x3F; } /** * Returns the current value of the cycle timer (in ticks) * * @return the current value of the cycle timer (in ticks) */ uint32_t Ieee1394Service::getCycleTimerTicks() { return m_pCTRHelper->getCycleTimerTicks(); } /** * Returns the current value of the cycle timer (as is) * * @return the current value of the cycle timer (as is) */ uint32_t Ieee1394Service::getCycleTimer() { return m_pCTRHelper->getCycleTimer(); } /** * Returns the current value of the cycle timer (in ticks) * for a specific time instant (usecs since epoch) * @return the current value of the cycle timer (in ticks) */ uint32_t Ieee1394Service::getCycleTimerTicks(uint64_t t) { return m_pCTRHelper->getCycleTimerTicks(t); } /** * Returns the current value of the cycle timer (as is) * for a specific time instant (usecs since epoch) * @return the current value of the cycle timer (as is) */ uint32_t Ieee1394Service::getCycleTimer(uint64_t t) { return m_pCTRHelper->getCycleTimer(t); } uint64_t Ieee1394Service::getSystemTimeForCycleTimerTicks(uint32_t ticks) { return m_pCTRHelper->getSystemTimeForCycleTimerTicks(ticks); } uint64_t Ieee1394Service::getSystemTimeForCycleTimer(uint32_t ctr) { return m_pCTRHelper->getSystemTimeForCycleTimer(ctr); } bool Ieee1394Service::readCycleTimerReg(uint32_t *cycle_timer, uint64_t *local_time) { if (m_have_read_ctr_and_clock) { int err; err = raw1394_read_cycle_timer_and_clock(m_util_handle, cycle_timer, local_time, Util::SystemTimeSource::getSource()); if(err) { debugWarning("raw1394_read_cycle_timer_and_clock error: %s\n", strerror(errno)); return false; } return true; } else 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 error: %s\n", strerror(errno)); return false; } return true; } else { // do a normal read of the CTR register // the disadvantage is that local_time and cycle time are not // read at the same time instant (scheduling issues) *local_time = getCurrentTimeAsUsecs(); if ( raw1394_read( m_util_handle, getLocalNodeId() | 0xFFC0, CSR_REGISTER_BASE | CSR_CYCLE_TIME, sizeof(uint32_t), cycle_timer ) == 0 ) { *cycle_timer = CondSwapFromBus32(*cycle_timer); return true; } else { return false; } } } uint64_t Ieee1394Service::getCurrentTimeAsUsecs() { return Util::SystemTimeSource::getCurrentTimeAsUsecs(); } bool Ieee1394Service::read( fb_nodeid_t nodeId, fb_nodeaddr_t addr, size_t length, fb_quadlet_t* buffer ) { Util::MutexLockHelper lock(*m_handle_lock); return readNoLock(nodeId, addr, length, buffer); } bool Ieee1394Service::readNoLock( fb_nodeid_t nodeId, fb_nodeaddr_t addr, size_t length, fb_quadlet_t* buffer ) { if (nodeId == INVALID_NODE_ID) { debugWarning("operation on invalid node\n"); return false; } if ( raw1394_read( m_handle, nodeId, addr, length*4, buffer ) == 0 ) { #ifdef DEBUG debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "read: node 0x%hX, addr = 0x%016" PRIX64 ", length = %zd\n", nodeId, addr, length); printBuffer( DEBUG_LEVEL_VERY_VERBOSE, length, buffer ); #endif return true; } else { #ifdef DEBUG debugOutput(DEBUG_LEVEL_VERBOSE, "raw1394_read failed: node 0x%hX, addr = 0x%016" PRIX64 ", length = %zd\n", nodeId, addr, length); #endif return false; } } bool Ieee1394Service::read_quadlet( fb_nodeid_t nodeId, fb_nodeaddr_t addr, fb_quadlet_t* buffer ) { return read( nodeId, addr, sizeof( *buffer )/4, buffer ); } bool Ieee1394Service::read_octlet( fb_nodeid_t nodeId, fb_nodeaddr_t addr, fb_octlet_t* buffer ) { return read( nodeId, addr, sizeof( *buffer )/4, reinterpret_cast( buffer ) ); } bool Ieee1394Service::write( fb_nodeid_t nodeId, fb_nodeaddr_t addr, size_t length, fb_quadlet_t* data ) { Util::MutexLockHelper lock(*m_handle_lock); return writeNoLock(nodeId, addr, length, data); } bool Ieee1394Service::writeNoLock( fb_nodeid_t nodeId, fb_nodeaddr_t addr, size_t length, fb_quadlet_t* data ) { if (nodeId == INVALID_NODE_ID) { debugWarning("operation on invalid node\n"); return false; } #ifdef DEBUG debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"write: node 0x%hX, addr = 0x%016" PRIX64 ", length = %zd\n", nodeId, addr, length); printBuffer( DEBUG_LEVEL_VERY_VERBOSE, length, data ); #endif return raw1394_write( m_handle, nodeId, addr, length*4, data ) == 0; } bool Ieee1394Service::write_quadlet( fb_nodeid_t nodeId, fb_nodeaddr_t addr, fb_quadlet_t data ) { return write( nodeId, addr, sizeof( data )/4, &data ); } bool Ieee1394Service::write_octlet( fb_nodeid_t nodeId, fb_nodeaddr_t addr, fb_octlet_t data ) { return write( nodeId, addr, sizeof( data )/4, reinterpret_cast( &data ) ); } bool Ieee1394Service::lockCompareSwap64( fb_nodeid_t nodeId, fb_nodeaddr_t addr, fb_octlet_t compare_value, fb_octlet_t swap_value, fb_octlet_t* result ) { if (nodeId == INVALID_NODE_ID) { debugWarning("operation on invalid node\n"); return false; } #ifdef DEBUG debugOutput(DEBUG_LEVEL_VERBOSE,"lockCompareSwap64: node 0x%X, addr = 0x%016" PRIX64 "\n", nodeId, addr); debugOutput(DEBUG_LEVEL_VERBOSE," if (*(addr)==0x%016" PRIX64 ") *(addr)=0x%016" PRIX64 "\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%016" PRIX64 "\n", buffer); } #endif // do endiannes swapping compare_value = CondSwapToBus64(compare_value); swap_value = CondSwapToBus64(swap_value); // do separate locking here (no MutexLockHelper) since // we use read_octlet in the DEBUG code in this function m_handle_lock->Lock(); int retval=raw1394_lock64(m_handle, nodeId, addr, RAW1394_EXTCODE_COMPARE_SWAP, swap_value, compare_value, result); m_handle_lock->Unlock(); if(retval) { debugError("raw1394_lock64 failed: %s\n", strerror(errno)); } #ifdef DEBUG if(!read_octlet( nodeId, addr,&buffer )) { debugWarning("Could not read register\n"); } else { debugOutput(DEBUG_LEVEL_VERBOSE,"after = 0x%016" PRIX64 "\n", buffer); } #endif *result = CondSwapFromBus64(*result); return (retval == 0); } fb_quadlet_t* Ieee1394Service::transactionBlock( fb_nodeid_t nodeId, fb_quadlet_t* buf, int len, unsigned int* resp_len ) { // FIXME: simplify semantics if (nodeId == INVALID_NODE_ID) { debugWarning("operation on invalid node\n"); return NULL; } // NOTE: this expects a call to transactionBlockClose to unlock m_handle_lock->Lock(); // clear the request & response memory memset(&m_fcp_block, 0, sizeof(m_fcp_block)); // make a local copy of the request if(len < MAX_FCP_BLOCK_SIZE_QUADS) { memcpy(m_fcp_block.request, buf, len*sizeof(quadlet_t)); m_fcp_block.request_length = len; } else { debugWarning("Truncating FCP request\n"); memcpy(m_fcp_block.request, buf, MAX_FCP_BLOCK_SIZE_BYTES); m_fcp_block.request_length = MAX_FCP_BLOCK_SIZE_QUADS; } m_fcp_block.target_nodeid = 0xffc0 | nodeId; bool success = doFcpTransaction(); if(success) { *resp_len = m_fcp_block.response_length; return m_fcp_block.response; } else { debugWarning("FCP transaction failed\n"); *resp_len = 0; return NULL; } } bool Ieee1394Service::transactionBlockClose() { m_handle_lock->Unlock(); return true; } // FCP code bool Ieee1394Service::doFcpTransaction() { for(int i=0; i < IEEE1394SERVICE_FCP_MAX_TRIES; i++) { if(doFcpTransactionTry()) { return true; } else { debugOutput(DEBUG_LEVEL_VERBOSE, "FCP transaction try %d failed\n", i); Util::SystemTimeSource::SleepUsecRelative( IEEE1394SERVICE_FCP_SLEEP_BETWEEN_FAILURES_USECS); } } debugError("FCP transaction didn't succeed in %d tries\n", IEEE1394SERVICE_FCP_MAX_TRIES); return false; } #define FCP_COMMAND_ADDR 0xFFFFF0000B00ULL #define FCP_RESPONSE_ADDR 0xFFFFF0000D00ULL /* AV/C FCP response codes */ #define FCP_RESPONSE_NOT_IMPLEMENTED 0x08000000 #define FCP_RESPONSE_ACCEPTED 0x09000000 #define FCP_RESPONSE_REJECTED 0x0A000000 #define FCP_RESPONSE_IN_TRANSITION 0x0B000000 #define FCP_RESPONSE_IMPLEMENTED 0x0C000000 #define FCP_RESPONSE_STABLE 0x0C000000 #define FCP_RESPONSE_CHANGED 0x0D000000 #define FCP_RESPONSE_INTERIM 0x0F000000 /* AV/C FCP mask macros */ #define FCP_MASK_START(x) ((x) & 0xF0000000) #define FCP_MASK_CTYPE(x) ((x) & 0x0F000000) #define FCP_MASK_RESPONSE(x) ((x) & 0x0F000000) #define FCP_MASK_SUBUNIT(x) ((x) & 0x00FF0000) #define FCP_MASK_SUBUNIT_TYPE(x) ((x) & 0x00F80000) #define FCP_MASK_SUBUNIT_ID(x) ((x) & 0x00070000) #define FCP_MASK_OPCODE(x) ((x) & 0x0000FF00) #define FCP_MASK_SUBUNIT_AND_OPCODE(x) ((x) & 0x00FFFF00) #define FCP_MASK_OPERAND0(x) ((x) & 0x000000FF) #define FCP_MASK_OPERAND(x, n) ((x) & (0xFF000000 >> ((((n)-1)%4)*8))) #define FCP_MASK_RESPONSE_OPERAND(x, n) ((x) & (0xFF000000 >> (((n)%4)*8))) bool Ieee1394Service::doFcpTransactionTry() { // NOTE that access to this is protected by the m_handle lock int err; bool retval = true; uint64_t timeout; // prepare an fcp response handler raw1394_set_fcp_handler(m_handle, _avc_fcp_handler); // start listening for FCP requests // this fails if some other program is listening for a FCP response err = raw1394_start_fcp_listen(m_handle); if(err) { debugOutput(DEBUG_LEVEL_VERBOSE, "could not start FCP listen (err=%d, errno=%d)\n", err, errno); retval = false; goto out; } m_fcp_block.status = eFS_Waiting; #ifdef DEBUG debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"fcp request: node 0x%hX, length = %d bytes\n", m_fcp_block.target_nodeid, m_fcp_block.request_length*4); printBuffer(DEBUG_LEVEL_VERY_VERBOSE, m_fcp_block.request_length, m_fcp_block.request ); #endif // write the FCP request if(!writeNoLock( m_fcp_block.target_nodeid, FCP_COMMAND_ADDR, m_fcp_block.request_length, m_fcp_block.request)) { debugOutput(DEBUG_LEVEL_VERBOSE, "write of FCP request failed\n"); retval = false; goto out; } // wait for the response to arrive struct pollfd raw1394_poll; raw1394_poll.fd = raw1394_get_fd(m_handle); raw1394_poll.events = POLLIN; timeout = Util::SystemTimeSource::getCurrentTimeAsUsecs() + IEEE1394SERVICE_FCP_RESPONSE_TIMEOUT_USEC; while(m_fcp_block.status == eFS_Waiting && Util::SystemTimeSource::getCurrentTimeAsUsecs() < timeout) { if(poll( &raw1394_poll, 1, IEEE1394SERVICE_FCP_POLL_TIMEOUT_MSEC) > 0) { if (raw1394_poll.revents & POLLIN) { raw1394_loop_iterate(m_handle); } } } // check the request and figure out what happened if(m_fcp_block.status == eFS_Waiting) { debugOutput(DEBUG_LEVEL_VERBOSE, "FCP response timed out\n"); retval = false; goto out; } if(m_fcp_block.status == eFS_Error) { debugError("FCP request/response error\n"); retval = false; goto out; } out: // stop listening for FCP responses err = raw1394_stop_fcp_listen(m_handle); if(err) { debugOutput(DEBUG_LEVEL_VERBOSE, "could not stop FCP listen (err=%d, errno=%d)\n", err, errno); retval = false; } m_fcp_block.status = eFS_Empty; return retval; } int Ieee1394Service::_avc_fcp_handler(raw1394handle_t handle, nodeid_t nodeid, int response, size_t length, unsigned char *data) { Ieee1394Service *service = static_cast(raw1394_get_userdata(handle)); if(service) { return service->handleFcpResponse(nodeid, response, length, data); } else return -1; } int Ieee1394Service::handleFcpResponse(nodeid_t nodeid, int response, size_t length, unsigned char *data) { static struct sFcpBlock fcp_block_last; fb_quadlet_t *data_quads = (fb_quadlet_t *)data; #ifdef DEBUG debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"fcp response: node 0x%hX, response = %d, length = %zd bytes\n", nodeid, response, length); printBuffer(DEBUG_LEVEL_VERY_VERBOSE, (length+3)/4, data_quads ); #endif if (response && length > 3) { if(length > MAX_FCP_BLOCK_SIZE_BYTES) { length = MAX_FCP_BLOCK_SIZE_BYTES; debugWarning("Truncated FCP response\n"); } // is it an actual response or is it INTERIM? quadlet_t first_quadlet = CondSwapFromBus32(data_quads[0]); if(FCP_MASK_RESPONSE(first_quadlet) == FCP_RESPONSE_INTERIM) { debugOutput(DEBUG_LEVEL_VERBOSE, "INTERIM\n"); } else { // it's an actual response, check if it matches our request if(nodeid != m_fcp_block.target_nodeid) { debugOutput(DEBUG_LEVEL_VERBOSE, "FCP response node id's don't match! (%x, %x)\n", m_fcp_block.target_nodeid, nodeid); } else if (first_quadlet == 0) { debugWarning("Bogus FCP response\n"); printBuffer(DEBUG_LEVEL_WARNING, (length+3)/4, data_quads ); #ifdef DEBUG } else if(FCP_MASK_RESPONSE(first_quadlet) < 0x08000000) { debugWarning("Bogus AV/C FCP response code\n"); printBuffer(DEBUG_LEVEL_WARNING, (length+3)/4, data_quads ); #endif } else if(FCP_MASK_SUBUNIT_AND_OPCODE(first_quadlet) != FCP_MASK_SUBUNIT_AND_OPCODE(CondSwapFromBus32(m_fcp_block.request[0]))) { debugOutput(DEBUG_LEVEL_VERBOSE, "FCP response not for this request: %08X != %08X\n", FCP_MASK_SUBUNIT_AND_OPCODE(first_quadlet), FCP_MASK_SUBUNIT_AND_OPCODE(CondSwapFromBus32(m_fcp_block.request[0]))); } else if(m_filterFCPResponse && (memcmp(fcp_block_last.response, data, length) == 0)) { // This is workaround for the Edirol FA-101. The device tends to send more than // one responde to one request. This seems to happen when discovering // function blocks and looks very likely there is a race condition in the // device. The workaround here compares the just arrived FCP responde // to the last one. If it is the same as the previously one then we // just ignore it. The downside of this approach is, we cannot issue // the same FCP twice. debugWarning("Received duplicate FCP response. Ignore it\n"); } else { m_fcp_block.response_length = (length + sizeof(quadlet_t) - 1) / sizeof(quadlet_t); memcpy(m_fcp_block.response, data, length); if (m_filterFCPResponse) { memcpy(fcp_block_last.response, data, length); } m_fcp_block.status = eFS_Responded; } } } return 0; } bool Ieee1394Service::setSplitTimeoutUsecs(fb_nodeid_t nodeId, unsigned int timeout) { Util::MutexLockHelper lock(*m_handle_lock); debugOutput(DEBUG_LEVEL_VERBOSE, "setting SPLIT_TIMEOUT on node 0x%X to %uusecs...\n", nodeId, timeout); unsigned int secs = timeout / 1000000; unsigned int usecs = timeout % 1000000; quadlet_t split_timeout_hi = CondSwapToBus32(secs & 7); quadlet_t split_timeout_low = CondSwapToBus32(((usecs / 125) & 0x1FFF) << 19); // write the CSR registers if(!writeNoLock( 0xffc0 | nodeId, CSR_REGISTER_BASE + CSR_SPLIT_TIMEOUT_HI, 1, &split_timeout_hi)) { debugOutput(DEBUG_LEVEL_VERBOSE, "write of CSR_SPLIT_TIMEOUT_HI failed\n"); return false; } if(!writeNoLock( 0xffc0 | nodeId, CSR_REGISTER_BASE + CSR_SPLIT_TIMEOUT_LO, 1, &split_timeout_low)) { debugOutput(DEBUG_LEVEL_VERBOSE, "write of CSR_SPLIT_TIMEOUT_LO failed\n"); return false; } return true; } int Ieee1394Service::getSplitTimeoutUsecs(fb_nodeid_t nodeId) { Util::MutexLockHelper lock(*m_handle_lock); // Keep Valgrind quiet by including explicit assignment quadlet_t split_timeout_hi = 0; quadlet_t split_timeout_low = 0; debugOutput(DEBUG_LEVEL_VERBOSE, "reading SPLIT_TIMEOUT on node 0x%X...\n", nodeId); if(!readNoLock( 0xffc0 | nodeId, CSR_REGISTER_BASE + CSR_SPLIT_TIMEOUT_HI, 1, &split_timeout_hi)) { debugOutput(DEBUG_LEVEL_VERBOSE, "read of CSR_SPLIT_TIMEOUT_HI failed\n"); return 0; } debugOutput(DEBUG_LEVEL_VERBOSE, " READ HI: 0x%08X\n", split_timeout_hi); if(!readNoLock( 0xffc0 | nodeId, CSR_REGISTER_BASE + CSR_SPLIT_TIMEOUT_LO, 1, &split_timeout_low)) { debugOutput(DEBUG_LEVEL_VERBOSE, "read of CSR_SPLIT_TIMEOUT_LO failed\n"); return 0; } debugOutput(DEBUG_LEVEL_VERBOSE, " READ LO: 0x%08X\n", split_timeout_low); split_timeout_hi = CondSwapFromBus32(split_timeout_hi); split_timeout_low = CondSwapFromBus32(split_timeout_low); return (split_timeout_hi & 7) * 1000000 + (split_timeout_low >> 19) * 125; } void Ieee1394Service::setFCPResponseFiltering(bool enable) { m_filterFCPResponse = enable; } 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::HelperThread *thread = reinterpret_cast(raw1394_get_userdata( handle )); if(thread == NULL) { debugFatal("Bogus 1394 handle private data\n"); return -1; } Ieee1394Service& service = thread->get1394Service(); service.resetHandler( generation ); return 0; } bool Ieee1394Service::resetHandler( unsigned int generation ) { quadlet_t buf=0; m_handle_lock->Lock(); raw1394_update_generation(m_handle, generation); m_handle_lock->Unlock(); // do a simple read on ourself in order to update the internal structures // this avoids failures after a bus reset read_quadlet( getLocalNodeId() | 0xFFC0, CSR_REGISTER_BASE | CSR_CYCLE_TIME, &buf ); for ( reset_handler_vec_t::iterator it = m_busResetHandlers.begin(); it != m_busResetHandlers.end(); ++it ) { Util::Functor* func = *it; ( *func )(); } return true; } bool Ieee1394Service::registerARMHandler(ARMHandler *h) { debugOutput(DEBUG_LEVEL_VERBOSE, "Registering ARM handler (%p) for 0x%016" PRIX64 ", length %zu\n", h, h->getStart(), h->getLength()); // FIXME: note that this will result in the ARM handlers not running in a realtime context int err = raw1394_arm_register(m_armHelperNormal->get1394Handle(), 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%016" PRIX64 "\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%016" PRIX64 "\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_armHelperNormal->get1394Handle(), 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 %zd bytes, from 0x%016" PRIX64 " in steps of %zd bytes\n", length, start, step); int cnt=0; const int maxcnt=10; int err=1; Util::MutexLockHelper lock(*m_handle_lock); while(err && cnt++ < maxcnt) { // try to register err = raw1394_arm_register(m_handle, start, length, 0, 0, 0, 0, 0); if (err) { debugOutput(DEBUG_LEVEL_VERBOSE, " -> cannot use 0x%016" PRIX64 "\n", start); debugError(" Error: %s\n", strerror(errno)); start += step; } else { debugOutput(DEBUG_LEVEL_VERBOSE, " -> use 0x%016" PRIX64 "\n", start); err = raw1394_arm_unregister(m_handle, 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::HelperThread *thread = reinterpret_cast(raw1394_get_userdata( handle )); if(thread == NULL) { debugFatal("Bogus 1394 handle private data\n"); return -1; } Ieee1394Service& service = thread->get1394Service(); if(service.armHandler( arm_tag, request_type, requested_length, data )) { return 0; } else { return -1; } } 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%016" PRIX64 " 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_armHelperNormal->get1394Handle(), arm_tag, request_type, requested_length, data ); return true; } bool Ieee1394Service::addBusResetHandler( Util::Functor* functor ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Adding busreset handler (%p)\n", functor); m_busResetHandlers.push_back( functor ); return true; } bool Ieee1394Service::remBusResetHandler( Util::Functor* functor ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Removing busreset handler (%p)\n", functor); for ( reset_handler_vec_t::iterator it = m_busResetHandlers.begin(); it != m_busResetHandlers.end(); ++it ) { if ( *it == functor ) { debugOutput(DEBUG_LEVEL_VERBOSE, " found\n"); m_busResetHandlers.erase( it ); return true; } } debugOutput(DEBUG_LEVEL_VERBOSE, " not found\n"); return false; } /** * Allocates an iso channel for use by the interface in a similar way to * libiec61883. Returns -1 on error (due to there being no free channels) * or an allocated channel number. * * Does not perform anything other than registering the channel and the * bandwidth at the IRM * * Also allocates the necessary bandwidth (in ISO allocation units). * * FIXME: As in libiec61883, channel 63 is not requested; this is either a * bug or it's omitted since that's the channel preferred by video devices. * * @param bandwidth the bandwidth to allocate for this channel * @return the channel number */ signed int Ieee1394Service::allocateIsoChannelGeneric(unsigned int bandwidth) { debugOutput(DEBUG_LEVEL_VERBOSE, "Allocating ISO channel using generic method...\n" ); Util::MutexLockHelper lock(*m_handle_lock); struct ChannelInfo cinfo; int c = -1; for (c = 0; c < 63; c++) { if (raw1394_channel_modify (m_handle, c, RAW1394_MODIFY_ALLOC) == 0) break; } if (c < 63) { debugOutput(DEBUG_LEVEL_VERBOSE, "found free iso channel %d\n", c); 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 a specific fixed iso channel for use by the interface. Returns * -1 on error (due to the requested channel not being free) or the fixed iso * 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. * * @chan the channel number being requested * @param bandwidth the bandwidth to allocate for this channel * @return the channel number */ signed int Ieee1394Service::allocateFixedIsoChannelGeneric( unsigned int chan, unsigned int bandwidth ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Allocating ISO channel %d using generic method...\n", chan ); Util::MutexLockHelper lock(*m_handle_lock); struct ChannelInfo cinfo; if (raw1394_channel_modify (m_handle, chan, RAW1394_MODIFY_ALLOC) == 0) { 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, chan, RAW1394_MODIFY_FREE); return -1; } else { cinfo.channel=chan; cinfo.bandwidth=bandwidth; cinfo.alloctype=AllocGeneric; cinfo.xmit_node=-1; cinfo.xmit_plug=-1; cinfo.recv_node=-1; cinfo.recv_plug=-1; if (registerIsoChannel(chan, cinfo)) { return chan; } else { raw1394_bandwidth_modify(m_handle, bandwidth, RAW1394_MODIFY_FREE); raw1394_channel_modify (m_handle, chan, 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 ) { if (xmit_node == INVALID_NODE_ID) { debugWarning("operation on invalid node (XMIT)\n"); return -1; } if (recv_node == INVALID_NODE_ID) { debugWarning("operation on invalid node (RECV)\n"); return -1; } debugOutput(DEBUG_LEVEL_VERBOSE, "Allocating ISO channel using IEC61883 CMP...\n" ); Util::MutexLockHelper lock(*m_handle_lock); struct ChannelInfo cinfo; int c = -1; int bandwidth=1; #if IEEE1394SERVICE_SKIP_IEC61883_BANDWIDTH_ALLOCATION bandwidth=0; #endif // do connection management: make connection c = iec61883_cmp_connect( m_handle, xmit_node | 0xffc0, &xmit_plug, recv_node | 0xffc0, &recv_plug, &bandwidth); if((c<0) || (c>63)) { debugError("Could not do CMP from %04X:%02d to %04X:%02d\n", xmit_node, xmit_plug, recv_node, recv_plug ); return -1; } cinfo.channel=c; cinfo.bandwidth=bandwidth; cinfo.alloctype=AllocCMP; cinfo.xmit_node=xmit_node; cinfo.xmit_plug=xmit_plug; cinfo.recv_node=recv_node; cinfo.recv_plug=recv_plug; if (registerIsoChannel(c, cinfo)) { return c; } return -1; } /** * Deallocates an iso channel. Silently ignores a request to deallocate * a negative channel number. * * Figures out the method that was used to allocate the channel (generic, cmp, ...) * and uses the appropriate method to deallocate. Also frees the bandwidth * that was reserved along with this channel. * * @param c channel number * @return true if successful */ bool Ieee1394Service::freeIsoChannel(signed int c) { debugOutput(DEBUG_LEVEL_VERBOSE, "Freeing ISO channel %d...\n", c ); Util::MutexLockHelper lock(*m_handle_lock); if (c < 0 || c > 63) { debugWarning("Invalid channel number: %d\n", c); return false; } switch (m_channels[c].alloctype) { default: debugError(" BUG: invalid allocation type!\n"); return false; case AllocFree: debugWarning(" Channel %d not registered\n", c); return false; case AllocGeneric: debugOutput(DEBUG_LEVEL_VERBOSE, " allocated using generic routine...\n" ); debugOutput(DEBUG_LEVEL_VERBOSE, " freeing %d bandwidth units...\n", m_channels[c].bandwidth ); if (raw1394_bandwidth_modify(m_handle, m_channels[c].bandwidth, RAW1394_MODIFY_FREE) !=0) { debugWarning("Failed to deallocate bandwidth\n"); } debugOutput(DEBUG_LEVEL_VERBOSE, " freeing channel %d...\n", m_channels[c].channel ); if (raw1394_channel_modify (m_handle, m_channels[c].channel, RAW1394_MODIFY_FREE) != 0) { debugWarning("Failed to free channel\n"); } if (!unregisterIsoChannel(c)) return false; return true; case AllocCMP: debugOutput(DEBUG_LEVEL_VERBOSE, " allocated using IEC61883 CMP...\n" ); debugOutput(DEBUG_LEVEL_VERBOSE, " performing IEC61883 CMP disconnect...\n" ); if(iec61883_cmp_disconnect( m_handle, m_channels[c].xmit_node | 0xffc0, m_channels[c].xmit_plug, m_channels[c].recv_node | 0xffc0, m_channels[c].recv_plug, m_channels[c].channel, m_channels[c].bandwidth) != 0) { debugWarning("Could not do CMP disconnect for channel %d!\n",c); } if (!unregisterIsoChannel(c)) return false; return true; } // unreachable debugError("BUG: unreachable code reached!\n"); return false; } /** * Registers a channel as managed by this ieee1394service * @param c channel number * @param cinfo channel info struct * @return true if successful */ bool Ieee1394Service::registerIsoChannel(unsigned int c, struct ChannelInfo cinfo) { if (c < 63) { if (m_channels[c].alloctype != AllocFree) { debugWarning("Channel %d already registered with bandwidth %d\n", m_channels[c].channel, m_channels[c].bandwidth); } memcpy(&m_channels[c], &cinfo, sizeof(struct ChannelInfo)); } else return false; return true; } /** * unegisters a channel from this ieee1394service * @param c channel number * @return true if successful */ bool Ieee1394Service::unregisterIsoChannel(unsigned int c) { if (c < 63) { if (m_channels[c].alloctype == AllocFree) { debugWarning("Channel %d not registered\n", c); return false; } m_channels[c].channel=-1; m_channels[c].bandwidth=-1; m_channels[c].alloctype=AllocFree; m_channels[c].xmit_node=0xFFFF; m_channels[c].xmit_plug=-1; m_channels[c].recv_node=0xFFFF; m_channels[c].recv_plug=-1; } else return false; return true; } /** * Returns the current value of the `bandwidth available' register on * the IRM, or -1 on error. * @return */ signed int Ieee1394Service::getAvailableBandwidth() { quadlet_t buffer; Util::MutexLockHelper lock(*m_handle_lock); signed int result = raw1394_read (m_handle, raw1394_get_irm_id (m_handle), CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE, sizeof (quadlet_t), &buffer); if (result < 0) return -1; return CondSwapFromBus32(buffer); } void Ieee1394Service::setVerboseLevel(int l) { if (m_pIsoManager) m_pIsoManager->setVerboseLevel(l); if (m_pCTRHelper) m_pCTRHelper->setVerboseLevel(l); if (m_pWatchdog) m_pWatchdog->setVerboseLevel(l); setDebugLevel(l); debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); } void Ieee1394Service::show() { #ifdef DEBUG uint32_t cycle_timer; uint64_t local_time; if(!readCycleTimerReg(&cycle_timer, &local_time)) { debugWarning("Could not read cycle timer register\n"); } uint64_t ctr = CYCLE_TIMER_TO_TICKS( cycle_timer ); debugOutput( DEBUG_LEVEL_VERBOSE, "Port: %d\n", getPort() ); debugOutput( DEBUG_LEVEL_VERBOSE, " Name: %s\n", getPortName().c_str() ); debugOutput( DEBUG_LEVEL_VERBOSE, " CycleTimerHelper: %p, IsoManager: %p, WatchDog: %p\n", m_pCTRHelper, m_pIsoManager, m_pWatchdog ); debugOutput( DEBUG_LEVEL_VERBOSE, " Time: %011" PRIu64 " (%03us %04ucy %04uticks)\n", ctr, (unsigned int)TICKS_TO_SECS( ctr ), (unsigned int)TICKS_TO_CYCLES( ctr ), (unsigned int)TICKS_TO_OFFSET( ctr ) ); debugOutputShort( DEBUG_LEVEL_NORMAL, "Iso handler info:\n"); #endif if (m_pIsoManager) m_pIsoManager->dumpInfo(); } // the helper thread class Ieee1394Service::HelperThread::HelperThread(Ieee1394Service &parent, std::string name) : m_parent( parent ) , m_name( name ) , m_handle( NULL ) , m_thread( *(new Util::PosixThread(this, name, false, 0, PTHREAD_CANCEL_DEFERRED)) ) , m_iterate( false ) , m_debugModule(parent.m_debugModule) { m_handle = raw1394_new_handle_on_port( parent.m_port ); if(!m_handle) { debugError("Could not allocate handle\n"); // FIXME: better error handling required } raw1394_set_userdata( m_handle, this ); } Ieee1394Service::HelperThread::HelperThread(Ieee1394Service &parent, std::string name, bool rt, int prio) : m_parent( parent ) , m_name( name ) , m_handle( NULL ) , m_thread( *(new Util::PosixThread(this, name, rt, prio, PTHREAD_CANCEL_DEFERRED)) ) , m_iterate( false ) , m_debugModule(parent.m_debugModule) { m_handle = raw1394_new_handle_on_port( parent.m_port ); if(!m_handle) { debugError("Could not allocate handle\n"); // FIXME: better error handling required } raw1394_set_userdata( m_handle, this ); } Ieee1394Service::HelperThread::~HelperThread() { m_thread.Stop(); delete &m_thread; if(m_handle) { raw1394_destroy_handle(m_handle); } } bool Ieee1394Service::HelperThread::Init() { m_iterate = true; return true; } bool Ieee1394Service::HelperThread::Execute() { if(m_iterate) { int err; err = raw1394_loop_iterate (m_handle); if(err < 0) { debugError("Failed to iterate handler\n"); return false; } else { return true; } } else { Util::SystemTimeSource::SleepUsecRelative(1000); return true; } } void Ieee1394Service::HelperThread::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 if (rt) { m_thread.AcquireRealTime(priority); } else { m_thread.DropRealTime(); } } bool Ieee1394Service::HelperThread::Start() { return m_thread.Start() == 0; } bool Ieee1394Service::HelperThread::Stop() { // request to stop iterating m_iterate = false; // poke the handler such that the iterate() returns raw1394_wake_up(m_handle); // stop the thread return m_thread.Stop() == 0; } libffado-2.4.5/src/libieee1394/ieee1394service.h0000644000175000001440000004214214206145246020357 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FFADO_IEEE1394SERVICE_H #define FFADO_IEEE1394SERVICE_H #include "fbtypes.h" #include "libutil/Functors.h" #include "libutil/Mutex.h" #include "libutil/Thread.h" #include "debugmodule/debugmodule.h" #include "IEC61883.h" #include #include #include #include #include #define MAX_FCP_BLOCK_SIZE_BYTES (512) #define MAX_FCP_BLOCK_SIZE_QUADS (MAX_FCP_BLOCK_SIZE_BYTES / 4) class IsoHandlerManager; class CycleTimerHelper; namespace Util { class Watchdog; class Configuration; } class Ieee1394Service : public IEC61883 { public: class ARMHandler; public: Ieee1394Service(); Ieee1394Service(bool rt, int prio); ~Ieee1394Service(); bool initialize( int port ); bool setThreadParameters(bool rt, int priority); Util::Watchdog *getWatchdog() {return m_pWatchdog;}; /** * @brief get number of ports (FireWire adapters) in this machine * * @return the number of ports */ static int detectNbPorts(); /** * @brief get port (adapter) id * * @return get port (adapter) id */ int getPort() { return m_port; } /** * @brief get port (adapter) name * * @return get port (adapter) name */ std::string getPortName() { return m_portName; }; /** * @brief get number of nodes on the bus * * Since the root node always has * the highest node ID, this number can be used to determine that ID (it's * LOCAL_BUS|(count-1)). * * @return the number of nodes on the bus to which the port is connected. * This value can change with every bus reset. */ int getNodeCount(); /** * @brief get the node id of the local node * * @note does not include the bus part (0xFFC0) * * @return the node id of the local node * This value can change with every bus reset. */ nodeid_t getLocalNodeId(); /** * @brief get the most recent cycle timer value (in ticks) * * @note Uses the most appropriate method for getting the cycle timer * which is not necessarily a direct read (could be DLL) */ uint32_t getCycleTimerTicks(); /** * @brief get the most recent cycle timer value (in CTR format) * * @note Uses the most appropriate method for getting the cycle timer * which is not necessarily a direct read (could be DLL) */ uint32_t getCycleTimer(); /** * @brief get the cycle timer value for a specific time instant (in ticks) * * @note Uses the most appropriate method for getting the cycle timer * which is not necessarily a direct read (could be DLL) */ uint32_t getCycleTimerTicks(uint64_t t); /** * @brief get the cycle timer value for a specific time instant (in CTR format) * * @note Uses the most appropriate method for getting the cycle timer * which is not necessarily a direct read (could be DLL) */ uint32_t getCycleTimer(uint64_t t); /** * @brief get the system time for a specific cycle timer value (in ticks) * @note thread safe */ uint64_t getSystemTimeForCycleTimerTicks(uint32_t ticks); /** * @brief get the system time for a specific cycle timer value (in CTR format) * @note thread safe */ uint64_t getSystemTimeForCycleTimer(uint32_t ctr); /** * @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 ); /** * initiate AV/C transaction * @param nodeId * @param buf * @param len * @param resp_len * @return */ fb_quadlet_t* transactionBlock( fb_nodeid_t nodeId, fb_quadlet_t* buf, int len, unsigned int* resp_len ); /** * close AV/C transaction. * @param nodeId * @param buf * @param len * @param resp_len * @return */ bool transactionBlockClose(); int getVerboseLevel(); bool addBusResetHandler( Util::Functor* functor ); bool remBusResetHandler( Util::Functor* functor ); void doBusReset(); bool waitForBusResetStormToEnd( int nb_tries, int sleep_time_ms ); /** * @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 ); /** * @brief get the current generation * * @return the current generation **/ unsigned int getGeneration() { Util::MutexLockHelper lock(*m_handle_lock); return raw1394_get_generation( m_handle ); } /** * @brief update the current generation * * @return the current generation **/ void updateGeneration() { Util::MutexLockHelper lock(*m_handle_lock); raw1394_update_generation( m_handle, getGeneration()); } /** * @brief sets the SPLIT_TIMEOUT_HI and SPLIT_TIMEOUT_LO CSR registers * * sets the SPLIT_TIMEOUT_HI and SPLIT_TIMEOUT_LO CSR registers on node * nodeId such that the timeout is equal to timeout * * @param nodeId node to set CSR registers on * @param timeout timeout in usecs * @return true if successful */ bool setSplitTimeoutUsecs(fb_nodeid_t nodeId, unsigned int timeout); /** * @brief gets the SPLIT_TIMEOUT_X timeout value * * gets the SPLIT_TIMEOUT_HI and SPLIT_TIMEOUT_LO CSR registers on node * nodeId and recombine them into one usec value * * @param nodeId node to get CSR registers from * @return timeout in usecs if successful, 0 else */ int getSplitTimeoutUsecs(fb_nodeid_t nodeId); /** * @brief use the provided configuration for this service * * only update the config once, before init. not thread safe, * and no effect when things are already running. * * @param c configuration to use * @return bool if this config is ok. */ bool useConfiguration(Util::Configuration *c); Util::Configuration *getConfiguration() {return m_configuration;}; /** * @brief enable or disable FCP response doublicate filtering * * this is use only for devices (e.g. edirol fa101) which have a * buggy FCP implementation and send more then one FCP response * for one request. */ void setFCPResponseFiltering(bool enable); // ISO channel stuff public: signed int getAvailableBandwidth(); signed int allocateIsoChannelGeneric(unsigned int bandwidth); signed int allocateFixedIsoChannelGeneric( unsigned int chan, 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); public: // FIXME: should be private, but is used to do the PCR control in GenericAVC::AvDevice raw1394handle_t getHandle() {return m_handle;}; protected: Util::Configuration *m_configuration; private: // this class will create a new 1394 handle // and a thread that will iterate it class HelperThread : public Util::RunnableInterface { public: HelperThread(Ieee1394Service &, std::string); HelperThread(Ieee1394Service &, std::string, bool rt, int prio); virtual ~HelperThread(); raw1394handle_t get1394Handle() {return m_handle;}; Ieee1394Service &get1394Service() {return m_parent;}; virtual bool Init(); virtual bool Execute(); void setThreadParameters(bool rt, int priority); bool Start(); bool Stop(); private: Ieee1394Service &m_parent; std::string m_name; raw1394handle_t m_handle; Util::Thread & m_thread; bool m_iterate; DECLARE_DEBUG_MODULE_REFERENCE; }; HelperThread *m_resetHelper; HelperThread *m_armHelperNormal; HelperThread *m_armHelperRealtime; private: // unsorted bool configurationUpdated(); void printBuffer( unsigned int level, size_t length, fb_quadlet_t* buffer ) const; void printBufferBytes( unsigned int level, size_t length, byte_t* buffer ) const; static int resetHandlerLowLevel( raw1394handle_t handle, unsigned int generation ); bool resetHandler( unsigned int generation ); static int armHandlerLowLevel(raw1394handle_t handle, unsigned long arm_tag, byte_t request_type, unsigned int requested_length, void *data); bool armHandler( unsigned long arm_tag, byte_t request_type, unsigned int requested_length, void *data); raw1394handle_t m_handle; Util::Mutex* m_handle_lock; raw1394handle_t m_util_handle; int m_port; std::string m_portName; bool m_realtime; int m_base_priority; IsoHandlerManager* m_pIsoManager; CycleTimerHelper* m_pCTRHelper; bool m_have_new_ctr_read; bool m_have_read_ctr_and_clock; bool m_filterFCPResponse; // the RT watchdog Util::Watchdog* m_pWatchdog; typedef std::vector< Util::Functor* > reset_handler_vec_t; reset_handler_vec_t m_busResetHandlers; // ARM stuff arm_tag_handler_t m_default_arm_handler; typedef std::vector< ARMHandler * > arm_handler_vec_t; arm_handler_vec_t m_armHandlers; // unprotected variants bool writeNoLock( fb_nodeid_t nodeId, fb_nodeaddr_t addr, size_t length, fb_quadlet_t* data ); bool readNoLock( fb_nodeid_t nodeId, fb_nodeaddr_t addr, size_t length, fb_quadlet_t* buffer ); // FCP transaction support static int _avc_fcp_handler(raw1394handle_t handle, nodeid_t nodeid, int response, size_t length, unsigned char *data); int handleFcpResponse(nodeid_t nodeid, int response, size_t length, unsigned char *data); enum eFcpStatus { eFS_Empty, eFS_Waiting, eFS_Responded, eFS_Error, }; struct sFcpBlock { enum eFcpStatus status; nodeid_t target_nodeid; unsigned int request_length; quadlet_t request[MAX_FCP_BLOCK_SIZE_QUADS]; unsigned int response_length; quadlet_t response[MAX_FCP_BLOCK_SIZE_QUADS]; }; struct sFcpBlock m_fcp_block; bool doFcpTransaction(); bool doFcpTransactionTry(); public: void setVerboseLevel(int l); void show(); private: DECLARE_DEBUG_MODULE; public: /** * @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 { public: ARMHandler(Ieee1394Service &parent, nodeaddr_t start, size_t length, unsigned int access_rights, unsigned int notification_options, unsigned int client_transactions ); virtual ~ARMHandler(); virtual bool handleRead(struct raw1394_arm_request *); virtual bool handleWrite(struct raw1394_arm_request *); virtual bool handleLock(struct raw1394_arm_request *); struct raw1394_arm_response *getResponse() {return &m_response;}; nodeaddr_t getStart() {return m_start;}; size_t getLength() {return m_length;}; unsigned int getAccessRights() {return m_access_rights;}; unsigned int getNotificationOptions() {return m_notification_options;}; unsigned int getClientTransactions() {return m_client_transactions;}; byte_t *getBuffer() {return m_buffer;}; private: Ieee1394Service &m_parent; nodeaddr_t m_start; size_t m_length; unsigned int m_access_rights; unsigned int m_notification_options; unsigned int m_client_transactions; protected: 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); DECLARE_DEBUG_MODULE_REFERENCE; }; }; #endif // FFADO_IEEE1394SERVICE_H libffado-2.4.5/src/libieee1394/test-cyclecalc.cpp0000644000175000001440000000762314206145246021005 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "cycletimer.h" #include "debugmodule/debugmodule.h" #include DECLARE_GLOBAL_DEBUG_MODULE; int main() { setDebugLevel(DEBUG_LEVEL_VERY_VERBOSE); printf("Cycle timer operation tests (incomplete)\n"); /* TEST 1 * check reconstruction of SYT RECEIVE timestamp * * The now_ctr has wrapped, while the cycle and syt have not * */ #ifdef DEBUG_MESSAGES 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 : %011" PRIu64 " (%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 : %011" PRIu64 " (%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)); #else printf("DEBUG_MESSAGES not enabled for build. This program won't show anything without it.\n"); #endif // 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 // } libffado-2.4.5/src/libieee1394/vendor_model_ids.h0000644000175000001440000000340114206145246021055 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef VENDOR_MODEL_IDS #define VENDOR_MODEL_IDS #define FW_VENDORID_TERRATEC 0x000aac #define FW_VENDORID_MACKIE 0x00000f #define FW_VENDORID_APOGEE 0x0003db #define FW_VENDORID_BRIDGECO 0x0007f5 #define FW_VENDORID_PRESONUS 0x000a92 #define FW_VENDORID_ESI 0x000f1b #define FW_VENDORID_FOCUSRITE 0x00130e #define FW_VENDORID_EDIROL 0x0040ab #define FW_VENDORID_MAUDIO 0x000d6c #define FW_VENDORID_ECHO 0x001486 #define FW_VENDORID_RME 0x000a35 #define FW_VENDORID_MOTU 0x0001f2 #define FW_VENDORID_TCAT 0x000166 #define FW_VENDORID_ALESIS 0x000595 #define FW_VENDORID_WEISS 0x001C6A #define FW_VENDORID_STANTON 0x001260 #define FW_VENDORID_DNR 0x000F64 #define FW_VENDORID_YAMAHA 0x00a0de // this is the one we assign ourselves // maybe once we can get a real one :) #define FW_VENDORID_FFADO 0x0B0001 #endif /* VENDOR_MODEL_IDS */ libffado-2.4.5/src/libstreaming/0000755000175000001440000000000014206145612016130 5ustar jwoitheuserslibffado-2.4.5/src/libstreaming/StreamProcessorManager.cpp0000644000175000001440000021523214206145246023272 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "StreamProcessorManager.h" #include "generic/StreamProcessor.h" #include "generic/Port.h" #include "libieee1394/cycletimer.h" #include "devicemanager.h" #include "libutil/Time.h" #include #include #include namespace Streaming { IMPL_DEBUG_MODULE( StreamProcessorManager, StreamProcessorManager, DEBUG_LEVEL_VERBOSE ); StreamProcessorManager::StreamProcessorManager(DeviceManager &p) : m_time_of_transfer ( 0 ) #ifdef DEBUG , m_time_of_transfer2 ( 0 ) #endif , m_is_slave( false ) , m_SyncSource(NULL) , m_parent( p ) , m_xrun_happened( false ) , m_activity_wait_timeout_nsec( 0 ) // dynamically set , m_nb_buffers( 0 ) , m_period( 0 ) , m_sync_delay( 0 ) , m_audio_datatype( eADT_Float ) , m_nominal_framerate ( 0 ) , m_xruns(0) , m_shutdown_needed(false) , m_nbperiods(0) , m_WaitLock( new Util::PosixMutex("SPMWAIT") ) , m_max_diff_ticks( 50 ) { addOption(Util::OptionContainer::Option("slaveMode",false)); sem_init(&m_activity_semaphore, 0, 0); } StreamProcessorManager::StreamProcessorManager(DeviceManager &p, unsigned int period, unsigned int framerate, unsigned int nb_buffers) : m_time_of_transfer ( 0 ) #ifdef DEBUG , m_time_of_transfer2 ( 0 ) #endif , m_is_slave( false ) , m_SyncSource(NULL) , m_parent( p ) , m_xrun_happened( false ) , m_activity_wait_timeout_nsec( 0 ) // dynamically set , m_nb_buffers(nb_buffers) , m_period(period) , m_sync_delay( 0 ) , m_audio_datatype( eADT_Float ) , m_nominal_framerate ( framerate ) , m_xruns(0) , m_shutdown_needed(false) , m_nbperiods(0) , m_WaitLock( new Util::PosixMutex("SPMWAIT") ) , m_max_diff_ticks( 50 ) { addOption(Util::OptionContainer::Option("slaveMode",false)); sem_init(&m_activity_semaphore, 0, 0); } StreamProcessorManager::~StreamProcessorManager() { sem_post(&m_activity_semaphore); sem_destroy(&m_activity_semaphore); delete m_WaitLock; } // void // StreamProcessorManager::handleBusReset(Ieee1394Service &s) // { // // debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) Handle bus reset on service %p...\n", this, &s); // // // // bool handled_at_least_one = false; // // // note that all receive streams are gone once a device is unplugged // // // // // synchronize with the wait lock // // Util::MutexLockHelper lock(*m_WaitLock); // // // // debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) got wait lock...\n", this); // // // cause all SP's to bail out // // for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); // // it != m_ReceiveProcessors.end(); // // ++it ) // // { // // if(&s == &((*it)->getParent().get1394Service())) { // // debugOutput(DEBUG_LEVEL_NORMAL, // // "issue busreset on receive SPM on channel %d\n", // // (*it)->getChannel()); // // (*it)->handleBusReset(); // // handled_at_least_one = true; // // } else { // // debugOutput(DEBUG_LEVEL_NORMAL, // // "skipping receive SPM on channel %d since not on service %p\n", // // (*it)->getChannel(), &s); // // } // // } // // for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); // // it != m_TransmitProcessors.end(); // // ++it ) // // { // // if(&s == &((*it)->getParent().get1394Service())) { // // debugOutput(DEBUG_LEVEL_NORMAL, // // "issue busreset on transmit SPM on channel %d\n", // // (*it)->getChannel()); // // (*it)->handleBusReset(); // // handled_at_least_one = true; // // } else { // // debugOutput(DEBUG_LEVEL_NORMAL, // // "skipping transmit SPM on channel %d since not on service %p\n", // // (*it)->getChannel(), &s); // // } // // } // // // // // FIXME: we request shutdown for now. // // m_shutdown_needed = handled_at_least_one; // } void StreamProcessorManager::signalActivity() { sem_post(&m_activity_semaphore); debugOutputExtreme(DEBUG_LEVEL_VERBOSE,"%p activity\n", this); } enum StreamProcessorManager::eActivityResult StreamProcessorManager::waitForActivity() { debugOutputExtreme(DEBUG_LEVEL_VERBOSE,"%p waiting for activity\n", this); struct timespec ts; int result; if (m_activity_wait_timeout_nsec >= 0) { // CLOCK_REALTIME must be used because that's what sem_timedwait() // uses. This is safe - regardless of the clock used by // Util::SystemTimeSource - so long as the resulting time is only // used to implement a timeout in sem_timedwait(). if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { debugError("clock_gettime failed\n"); return eAR_Error; } ts.tv_nsec += m_activity_wait_timeout_nsec; while(ts.tv_nsec >= 1000000000LL) { ts.tv_sec += 1; ts.tv_nsec -= 1000000000LL; } } if (m_activity_wait_timeout_nsec >= 0) { result = sem_timedwait(&m_activity_semaphore, &ts); } else { result = sem_wait(&m_activity_semaphore); } if(result != 0) { if (errno == ETIMEDOUT) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) sem_timedwait() timed out (result=%d)\n", this, result); return eAR_Timeout; } else if (errno == EINTR) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) sem_[timed]wait() interrupted by signal (result=%d)\n", this, result); return eAR_Interrupted; } else if (errno == EINVAL) { debugError("(%p) sem_[timed]wait error (result=%d errno=EINVAL)\n", this, result); debugError("(%p) timeout_nsec=%" PRId64 " ts.sec=%" PRId64 " ts.nsec=%" PRId64 "\n", this, m_activity_wait_timeout_nsec, (int64_t)ts.tv_sec, (int64_t)ts.tv_nsec); return eAR_Error; } else { debugError("(%p) sem_[timed]wait error (result=%d errno=%d)\n", this, result, errno); debugError("(%p) timeout_nsec=%" PRId64 " ts.sec=%" PRId64 " ts.nsec=%" PRId64 "\n", this, m_activity_wait_timeout_nsec, (int64_t)ts.tv_sec, (int64_t)ts.tv_nsec); return eAR_Error; } } debugOutputExtreme(DEBUG_LEVEL_VERBOSE,"%p got activity\n", this); return eAR_Activity; } /** * Registers \ref processor with this manager. * * also registers it with the isohandlermanager * * be sure to call isohandlermanager->init() first! * and be sure that the processors are also ->init()'ed * * @param processor * @return true if successfull */ bool StreamProcessorManager::registerProcessor(StreamProcessor *processor) { debugOutput( DEBUG_LEVEL_VERBOSE, "Registering processor (%p)\n",processor); assert(processor); if (processor->getType() == StreamProcessor::ePT_Receive) { processor->setVerboseLevel(getDebugLevel()); // inherit debug level m_ReceiveProcessors.push_back(processor); Util::Functor* f = new Util::MemberFunctor0< StreamProcessorManager*, void (StreamProcessorManager::*)() > ( this, &StreamProcessorManager::updateShadowLists, false ); processor->addPortManagerUpdateHandler(f); updateShadowLists(); return true; } if (processor->getType() == StreamProcessor::ePT_Transmit) { processor->setVerboseLevel(getDebugLevel()); // inherit debug level m_TransmitProcessors.push_back(processor); Util::Functor* f = new Util::MemberFunctor0< StreamProcessorManager*, void (StreamProcessorManager::*)() > ( this, &StreamProcessorManager::updateShadowLists, false ); processor->addPortManagerUpdateHandler(f); updateShadowLists(); 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); // remove the functor Util::Functor * f = processor->getUpdateHandlerForPtr(this); if(f) { processor->remPortManagerUpdateHandler(f); delete f; } updateShadowLists(); 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); // remove the functor Util::Functor * f = processor->getUpdateHandlerForPtr(this); if(f) { processor->remPortManagerUpdateHandler(f); delete f; } updateShadowLists(); return true; } } } debugFatal("Processor (%p) not found!\n",processor); return false; //not found } bool StreamProcessorManager::streamingParamsOk(signed int period, signed int rate, signed int n_buffers) { // Return true if the given parameter combination is valid. If any // parameter is set to -1 the currently set value is used in the test. signed int min_period; if (period < 0) period = m_period; if (rate < 0) rate = m_nominal_framerate; if (n_buffers < 0) n_buffers = m_nb_buffers; // For most interfaces data is transmitted with 8/16/32 samples per // packet (at 1x, 2x and 4x rates respectively). This more or less // places a lower limit on the size of the period. Furthermore, the // current FFADO architecture dictates that m_nb_buffers can be no lower // than 2. if (n_buffers < 2) { printMessage("FFADO requires at least 2 buffers\n"); return false; } // The boundary between 1x, 2x and 4x speed is taken from the RME driver // since this seems to be the device with the widest available sampling // rate range. if (rate < 56000) { // 1x speed min_period = 8; } else if (rate < 112000) { // 2x speed min_period = 16; } else { // 4x speed min_period = 32; } if (period < min_period) { printMessage("At a rate of %d Hz, FFADO requires a buffer size of at least %d samples\n", rate, min_period); return false; } return true; } void StreamProcessorManager::setPeriodSize(unsigned int period) { // This method is called early in the initialisation sequence to set the // initial period size. However, at that point in time the stream // processors haven't been registered so they won't have their buffers // configured from here. The initial allocation of the stream processor // (SP) buffers happens from within the SP prepare() method. // // SP period size changes will normally only be acted on from here // if the change comes about due to a runtime change in the buffer size, // as happens via jack's setbufsize facility for example. if (period == m_period) return; debugOutput( DEBUG_LEVEL_VERBOSE, "Setting period size to %d (was %d)\n", period, m_period); m_period = period; for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { if ((*it)->periodSizeChanged(period) == false) debugWarning("receive stream processor %p couldn't set period size\n", *it); } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { if ((*it)->periodSizeChanged(period) == false) debugWarning("transmit stream processor %p couldn't set period size\n", *it); } // Keep the activity timeout in sync with the new period size. See // also comments about this in prepare(). if (m_nominal_framerate > 0) { int timeout_usec = 2*1000LL * 1000LL * m_period / m_nominal_framerate; debugOutput(DEBUG_LEVEL_VERBOSE, "setting activity timeout to %d\n", timeout_usec); setActivityWaitTimeoutUsec(timeout_usec); } } bool StreamProcessorManager::setSyncSource(StreamProcessor *s) { debugOutput( DEBUG_LEVEL_VERBOSE, "Setting sync source to (%p)\n", s); m_SyncSource = s; return true; } bool StreamProcessorManager::prepare() { debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing...\n"); m_is_slave=false; if(!getOption("slaveMode", m_is_slave)) { debugWarning("Could not retrieve slaveMode parameter, defaulting to false\n"); } m_shutdown_needed=false; // if no sync source is set, select one here if(m_SyncSource == NULL) { debugWarning("Sync Source is not set. Defaulting to first StreamProcessor.\n"); } // FIXME: put into separate method for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { if(m_SyncSource == NULL) { debugWarning(" => Sync Source is %p.\n", *it); m_SyncSource = *it; } } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { if(m_SyncSource == NULL) { debugWarning(" => Sync Source is %p.\n", *it); m_SyncSource = *it; } } // now do the actual preparation of the SP's debugOutput( DEBUG_LEVEL_VERBOSE, "Prepare Receive processors...\n"); for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { if(!(*it)->setOption("slaveMode", m_is_slave)) { debugOutput(DEBUG_LEVEL_VERBOSE, " note: could not set slaveMode option for (%p)...\n",(*it)); } if(!(*it)->prepare()) { debugFatal( " could not prepare (%p)...\n",(*it)); return false; } } debugOutput( DEBUG_LEVEL_VERBOSE, "Prepare Transmit processors...\n"); for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { if(!(*it)->setOption("slaveMode", m_is_slave)) { debugOutput(DEBUG_LEVEL_VERBOSE, " note: could not set slaveMode option for (%p)...\n",(*it)); } if(!(*it)->prepare()) { debugFatal( " could not prepare (%p)...\n",(*it)); return false; } } // if there are no stream processors registered, // fail if (m_ReceiveProcessors.size() + m_TransmitProcessors.size() == 0) { debugFatal("No stream processors registered, can't do anything useful\n"); return false; } // set the activity timeout value to two periods worth of usecs. // since we can expect activity once every period, but we might have some // offset, the safe value is two periods. int timeout_usec = 2*1000LL * 1000LL * m_period / m_nominal_framerate; debugOutput(DEBUG_LEVEL_VERBOSE, "setting activity timeout to %d\n", timeout_usec); setActivityWaitTimeoutUsec(timeout_usec); updateShadowLists(); return true; } bool StreamProcessorManager::startDryRunning() { debugOutput( DEBUG_LEVEL_VERBOSE, "Putting StreamProcessor streams into dry-running state...\n"); for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { if ((*it)->inError()) { debugOutput(DEBUG_LEVEL_VERBOSE, "SP %p in error state\n", *it); return false; } if (!(*it)->isDryRunning()) { if(!(*it)->scheduleStartDryRunning(-1)) { debugError("Could not put '%s' SP %p into the dry-running state\n", (*it)->getTypeString(), *it); return false; } } else { debugOutput( DEBUG_LEVEL_VERBOSE, " SP %p already dry-running...\n", *it); } } for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { if ((*it)->inError()) { debugOutput(DEBUG_LEVEL_VERBOSE, "SP %p in error state\n", *it); return false; } if (!(*it)->isDryRunning()) { if(!(*it)->scheduleStartDryRunning(-1)) { debugError("Could not put '%s' SP %p into the dry-running state\n", (*it)->getTypeString(), *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; // get the options int signal_delay_ticks = STREAMPROCESSORMANAGER_SIGNAL_DELAY_TICKS; int xmit_prebuffer_frames = STREAMPROCESSORMANAGER_XMIT_PREBUFFER_FRAMES; int sync_wait_time_msec = STREAMPROCESSORMANAGER_SYNC_WAIT_TIME_MSEC; int cycles_for_startup = STREAMPROCESSORMANAGER_CYCLES_FOR_STARTUP; int prestart_cycles_for_xmit = STREAMPROCESSORMANAGER_PRESTART_CYCLES_FOR_XMIT; int prestart_cycles_for_recv = STREAMPROCESSORMANAGER_PRESTART_CYCLES_FOR_RECV; Util::Configuration &config = m_parent.getConfiguration(); config.getValueForSetting("streaming.spm.signal_delay_ticks", signal_delay_ticks); config.getValueForSetting("streaming.spm.xmit_prebuffer_frames", xmit_prebuffer_frames); config.getValueForSetting("streaming.spm.sync_wait_time_msec", sync_wait_time_msec); config.getValueForSetting("streaming.spm.cycles_for_startup", cycles_for_startup); config.getValueForSetting("streaming.spm.prestart_cycles_for_xmit", prestart_cycles_for_xmit); config.getValueForSetting("streaming.spm.prestart_cycles_for_recv", prestart_cycles_for_recv); // 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; int packet_size_frames = 0; int max_packet_size_frames = 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; packet_size_frames = (*it)->getNominalFramesPerPacket(); if(packet_size_frames > max_packet_size_frames) max_packet_size_frames = packet_size_frames; } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { packet_size_frames = (*it)->getNominalFramesPerPacket(); if(packet_size_frames > max_packet_size_frames) max_packet_size_frames = packet_size_frames; } debugOutput( DEBUG_LEVEL_VERBOSE, " max_of_min_delay = %d, max_packet_size_frames = %d...\n", max_of_min_delay, max_packet_size_frames); // add some processing margin. This only shifts the time // at which the buffer is transfer()'ed. This makes things somewhat // more robust. m_sync_delay = max_of_min_delay + signal_delay_ticks; //STEP X: when we implement such a function, we can wait for a signal from the devices that they // have acquired lock //debugOutput( DEBUG_LEVEL_VERBOSE, "Waiting for device(s) to indicate clock sync lock...\n"); //sleep(2); // FIXME: be smarter here // make sure that we are dry-running long enough for the // DLL to have a decent sync (FIXME: does the DLL get updated when dry-running)? debugOutput( DEBUG_LEVEL_VERBOSE, "Waiting for sync...\n"); unsigned int nb_sync_runs = (sync_wait_time_msec * getNominalRate()); nb_sync_runs /= 1000; nb_sync_runs /= getPeriodSize(); while(nb_sync_runs--) { // or while not sync-ed? // check if we were woken up too soon uint64_t ticks_at_period = m_SyncSource->getTimeAtPeriod(); uint64_t ticks_at_period_margin = ticks_at_period + m_sync_delay; uint64_t pred_system_time_at_xfer = m_SyncSource->getParent().get1394Service().getSystemTimeForCycleTimerTicks(ticks_at_period_margin); #if DEBUG_EXTREME_ENABLE int64_t now = Util::SystemTimeSource::getCurrentTime(); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "CTR pred: %" PRId64 ", syncdelay: %" PRId64 ", diff: %" PRId64 "\n", ticks_at_period, ticks_at_period_margin, ticks_at_period_margin-ticks_at_period ); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "PREWAIT pred: %" PRId64 ", now: %" PRId64 ", wait: %" PRId64 "\n", pred_system_time_at_xfer, now, pred_system_time_at_xfer-now ); #endif // wait until it's time to transfer Util::SystemTimeSource::SleepUsecAbsolute(pred_system_time_at_xfer); #if DEBUG_EXTREME_ENABLE now = Util::SystemTimeSource::getCurrentTime(); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "POSTWAIT pred: %" PRId64 ", now: %" PRId64 ", excess: %" PRId64 "\n", pred_system_time_at_xfer, now, now-pred_system_time_at_xfer ); #endif } debugOutput( DEBUG_LEVEL_VERBOSE, "Propagate sync info...\n"); // FIXME: in the SPM it would be nice to have system time instead of // 1394 time float syncrate = 0.0; float tpf = m_SyncSource->getTicksPerFrame(); if (tpf > 0.0) { syncrate = 24576000.0/tpf; } else { debugWarning("tpf <= 0? %f\n", tpf); } debugOutput( DEBUG_LEVEL_VERBOSE, " sync source frame rate: %f fps (%f tpf)\n", syncrate, tpf); // 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=%011" PRIu64 " (%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. // we have to round this time to an integer number of audio packets double time_for_startup_abs = (double)(cycles_for_startup * TICKS_PER_CYCLE); int time_for_startup_frames = (int)(time_for_startup_abs / tpf); time_for_startup_frames = ((time_for_startup_frames / max_packet_size_frames) + 1) * max_packet_size_frames; uint64_t time_for_startup_ticks = (uint64_t)((float)time_for_startup_frames * tpf); time_of_first_sample = addTicks(time_of_first_sample, time_for_startup_ticks); debugOutput( DEBUG_LEVEL_VERBOSE, " add %d frames (%011" PRIu64 " ticks)...\n", time_for_startup_frames, time_for_startup_ticks); debugOutput( DEBUG_LEVEL_VERBOSE, " => first sample at TS=%011" PRIu64 " (%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, prestart_cycles_for_xmit * TICKS_PER_CYCLE); uint64_t time_to_start_recv = substractTicks(time_of_first_sample, prestart_cycles_for_recv * TICKS_PER_CYCLE); debugOutput( DEBUG_LEVEL_VERBOSE, " => xmit starts at TS=%011" PRIu64 " (%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=%011" PRIu64 " (%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)); // print the sync delay int sync_delay_frames = (int)((float)m_sync_delay / m_SyncSource->getTicksPerFrame()); debugOutput( DEBUG_LEVEL_VERBOSE, " sync delay: %d = %d + %d ticks (%03us %04uc %04ut) [%d frames]...\n", m_sync_delay, max_of_min_delay, signal_delay_ticks, (unsigned int)TICKS_TO_SECS(m_sync_delay), (unsigned int)TICKS_TO_CYCLES(m_sync_delay), (unsigned int)TICKS_TO_OFFSET(m_sync_delay), sync_delay_frames); // the amount of prebuffer frames should be a multiple of the common block size // as otherwise the position of MIDI is messed up if(xmit_prebuffer_frames % max_packet_size_frames) { int tmp = 0; while(tmp < xmit_prebuffer_frames) { tmp += max_packet_size_frames; } debugOutput(DEBUG_LEVEL_VERBOSE, "The number of prebuffer frames (%d) is not a multiple of the common block size (%d), increased to %d...\n", xmit_prebuffer_frames, max_packet_size_frames, tmp); xmit_prebuffer_frames = tmp; } // check if this can even work. // the worst case point where we can receive a period is at 1 period + sync delay // this means that the number of frames in the xmit buffer has to be at least // 1 period + sync delay if(xmit_prebuffer_frames + m_period * m_nb_buffers < m_period + sync_delay_frames) { debugWarning("The amount of transmit buffer frames (%d) is too small (< %d). " "This will most likely cause xruns.\n", xmit_prebuffer_frames + m_period * m_nb_buffers, m_period + sync_delay_frames); } // 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 ) { // set the number of prebuffer frames (*it)->setExtraBufferFrames(xmit_prebuffer_frames); // set the TSP of the first sample in the buffer (*it)->setBufferHeadTimestamp(time_of_first_sample); ffado_timestamp_t ts; signed int fc; (*it)->getBufferHeadTimestamp ( &ts, &fc ); debugOutput( DEBUG_LEVEL_VERBOSE, " transmit buffer tail %010" PRId64 " => head TS %010" PRIu64 ", fc=%d...\n", time_of_first_sample, (uint64_t)ts, fc); } // the receive processors can be delayed by sync_delay ticks // this means that in the worst case we have to be able to accomodate // an extra sync_delay ticks worth of frames in the receive SP buffer // the sync delay should be rounded to an integer amount of max_packet_size int tmp = sync_delay_frames / max_packet_size_frames; tmp = tmp + 1; sync_delay_frames = tmp * max_packet_size_frames; if (sync_delay_frames < 1024) sync_delay_frames = 1024; //HACK for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { // set the number of extra buffer frames (*it)->setExtraBufferFrames(sync_delay_frames); } // switch syncsource to running state uint64_t time_to_start_sync; // FIXME: this is most likely not going to work for transmit sync sources // but those are unsupported in this version if(m_SyncSource->getType() == StreamProcessor::ePT_Receive ) { time_to_start_sync = time_to_start_recv; } else { time_to_start_sync = time_to_start_xmit; } if(!m_SyncSource->scheduleStartRunning(time_to_start_sync)) { debugError("m_SyncSource->scheduleStartRunning(%11" PRIu64 ") failed\n", time_to_start_sync); return false; } // STEP X: switch all non-syncsource SP's over to the running state for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { if(*it != m_SyncSource) { if(!(*it)->scheduleStartRunning(time_to_start_recv)) { debugError("%p->scheduleStartRunning(%11" PRIu64 ") failed\n", *it, time_to_start_recv); return false; } } } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { if(*it != m_SyncSource) { if(!(*it)->scheduleStartRunning(time_to_start_xmit)) { debugError("%p->scheduleStartRunning(%11" PRIu64 ") failed\n", *it, time_to_start_xmit); return false; } } } // wait for the syncsource to start running. // that will block the waitForPeriod call until everyone has started (theoretically) // note: the SP's are scheduled to start in STREAMPROCESSORMANAGER_CYCLES_FOR_STARTUP cycles, // so a 20 times this value should be a good timeout //int cnt = cycles_for_startup * 20; // by then it should have started // or maybe we just have to use 1 second, as this wraps the cycle counter int cnt = 8000; while (!m_SyncSource->isRunning() && cnt) { SleepRelativeUsec(125); cnt--; } if(cnt==0) { debugOutput(DEBUG_LEVEL_VERBOSE, " Timeout waiting for the SyncSource to get started\n"); return false; } // the sync source is running, we can now read a decent received timestamp from it m_time_of_transfer = m_SyncSource->getTimeAtPeriod(); // and a (rough) approximation of the rate float rate = m_SyncSource->getTicksPerFrame(); #ifdef DEBUG // the time at which the previous period would have passed m_time_of_transfer2 = m_time_of_transfer; m_time_of_transfer2 = substractTicks(m_time_of_transfer2, (uint64_t)(m_period * rate)); #endif debugOutput( DEBUG_LEVEL_VERBOSE, " initial time of transfer %010" PRId64 ", rate %f...\n", m_time_of_transfer, rate); // FIXME: ideally we'd want the SP itself to account for the xmit_prebuffer_frames // but that would also require to use a different approach to setting the initial TSP's int64_t delay_in_ticks = (int64_t)(((float)((m_nb_buffers-1) * m_period + xmit_prebuffer_frames)) * rate); // then use this information to initialize the xmit handlers // we now set the buffer tail timestamp of the transmit buffer // to the period transfer time instant plus what's nb_buffers - 1 // in ticks. This due to the fact that we (should) have received one period // worth of ticks at t = m_time_of_transfer // hence one period of frames should also have been transmitted, which means // that there should be (nb_buffers - 1) * periodsize of frames in the xmit buffer // there are also xmit_prebuffer_frames frames extra present in the buffer // that allows us to calculate the tail timestamp for the buffer. int64_t transmit_tail_timestamp = addTicks(m_time_of_transfer, delay_in_ticks); debugOutput( DEBUG_LEVEL_VERBOSE, " preset transmit tail TS %010" PRId64 ", rate %f...\n", transmit_tail_timestamp, rate); for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { (*it)->setTicksPerFrame(rate); (*it)->setBufferTailTimestamp(transmit_tail_timestamp); ffado_timestamp_t ts; signed int fc; (*it)->getBufferHeadTimestamp ( &ts, &fc ); debugOutput( DEBUG_LEVEL_VERBOSE, " => transmit head TS %010" PRId64 ", fc=%d...\n", (uint64_t)ts, fc); } // align the received streams to be phase aligned if(!alignReceivedStreams()) { debugError("Could not align streams...\n"); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, " StreamProcessor streams running...\n"); return true; } bool StreamProcessorManager::alignReceivedStreams() { debugOutput( DEBUG_LEVEL_VERBOSE, "Aligning received streams...\n"); unsigned int nb_sync_runs; unsigned int nb_rcv_sp = m_ReceiveProcessors.size(); int64_t diff_between_streams[nb_rcv_sp]; int64_t diff; unsigned int i; int cnt = STREAMPROCESSORMANAGER_NB_ALIGN_TRIES; int align_average_time_msec = STREAMPROCESSORMANAGER_ALIGN_AVERAGE_TIME_MSEC; Util::Configuration &config = m_parent.getConfiguration(); config.getValueForSetting("streaming.spm.align_tries", cnt); config.getValueForSetting("streaming.spm.align_average_time_msec", align_average_time_msec); unsigned int periods_per_align_try = (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; while (!aligned && cnt--) { nb_sync_runs = periods_per_align_try; while(nb_sync_runs) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " check (%d)...\n", nb_sync_runs); if(!waitForPeriod()) { debugWarning("xrun while aligning streams...\n"); return false; } // before we do anything else, transfer if(!transferSilence()) { debugError("Could not transfer silence\n"); return false; } // now calculate the stream offset i = 0; for ( i = 0; i < nb_rcv_sp; i++) { StreamProcessor *s = m_ReceiveProcessors.at(i); diff = diffTicks(m_SyncSource->getTimeAtPeriod(), s->getTimeAtPeriod()); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " offset between SyncSP %p and SP %p is %" PRId64 " ticks...\n", m_SyncSource, s, diff); if ( nb_sync_runs == periods_per_align_try ) { diff_between_streams[i] = diff; } else { diff_between_streams[i] += diff; } } nb_sync_runs--; } // calculate the average offsets debugOutput( DEBUG_LEVEL_VERBOSE, " Average offsets:\n"); int diff_between_streams_frames[nb_rcv_sp]; aligned = true; // first find whether the streams are aligned and what their offset is 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 %" PRId64 " ticks, %d frames...\n", m_SyncSource, s, diff_between_streams[i], diff_between_streams_frames[i]); aligned &= (diff_between_streams_frames[i] == 0); } // if required, align the streams int frames_to_shift_stream[nb_rcv_sp]; int min_shift = 9999; if (!aligned) { // find the minimum value (= earliest stream) for ( i = 0; i < nb_rcv_sp; i++) { if (diff_between_streams_frames[i] < min_shift) { min_shift = diff_between_streams_frames[i]; } } debugOutput( DEBUG_LEVEL_VERBOSE, " correcting shift with %d frames\n", min_shift); // ensure that the streams are shifted only in the 'positive' direction // i.e. that frames are only dropped, not added since that results // in multiple writers for the data ringbuffer // this also results in 'minimal shift' (not that it's required since the // sync SP is part of the SP set) for ( i = 0; i < nb_rcv_sp; i++) { frames_to_shift_stream[i] = diff_between_streams_frames[i] - min_shift; debugOutput(DEBUG_LEVEL_VERBOSE, " going to drop %03d frames from stream %d\n", frames_to_shift_stream[i], i); } // perform the actual shift for ( i = 0; i < nb_rcv_sp; i++) { StreamProcessor *s = m_ReceiveProcessors.at(i); // reposition the stream if(!s->shiftStream(frames_to_shift_stream[i])) { debugError("Could not shift SP %p %d frames\n", s, frames_to_shift_stream[i]); return false; } } } if (!aligned) { debugOutput(DEBUG_LEVEL_VERBOSE, "Streams not aligned, doing new round...\n"); } } if (cnt == 0) { debugError("Align failed\n"); return false; } return true; } bool StreamProcessorManager::start() { debugOutput( DEBUG_LEVEL_VERBOSE, "Starting Processors...\n"); // start all SP's synchonized bool start_result = false; for (int ntries=0; ntries < STREAMPROCESSORMANAGER_SYNCSTART_TRIES; ntries++) { // put all SP's into dry-running state if (!startDryRunning()) { debugOutput(DEBUG_LEVEL_VERBOSE, "Could not put SP's in dry-running state (try %d)\n", ntries); start_result = false; continue; } start_result = syncStartAll(); if(start_result) { break; } else { debugOutput(DEBUG_LEVEL_VERBOSE, "Sync start try %d failed...\n", ntries); if(m_shutdown_needed) { debugOutput(DEBUG_LEVEL_VERBOSE, "Some fatal error occurred, stop trying.\n"); return false; } } } if (!start_result) { debugFatal("Could not syncStartAll...\n"); // If unable to start, ensure stream processors and their handlers // are in the stopped state, which is what the caller would reasonably // expect if start() fails. stop(); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, " Started...\n"); return true; } bool StreamProcessorManager::stop() { debugOutput( DEBUG_LEVEL_VERBOSE, "Stopping...\n"); debugOutput( DEBUG_LEVEL_VERBOSE, " scheduling stop for all SP's...\n"); // switch SP's over to the dry-running state for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { if((*it)->isRunning()) { if(!(*it)->scheduleStopRunning(-1)) { debugError("%p->scheduleStopRunning(-1) failed\n", *it); return false; } } } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { if((*it)->isRunning()) { if(!(*it)->scheduleStopRunning(-1)) { debugError("%p->scheduleStopRunning(-1) failed\n", *it); return false; } } } // wait for the SP's to get into the dry-running/stopped state int cnt = 8000; bool ready = false; while (!ready && cnt) { ready = true; for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { ready &= ((*it)->isDryRunning() || (*it)->isStopped() || (*it)->isWaitingForStream() || (*it)->inError()); } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { ready &= ((*it)->isDryRunning() || (*it)->isStopped() || (*it)->isWaitingForStream() || (*it)->inError()); } SleepRelativeUsec(125); cnt--; } if(cnt==0) { debugWarning(" Timeout waiting for the SP's to start dry-running\n"); for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { (*it)->dumpInfo(); } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { (*it)->dumpInfo(); } return false; } // switch SP's over to the stopped state for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { if ((*it)->inError()) { debugOutput(DEBUG_LEVEL_VERBOSE, "SP %p in error state\n", *it); } else 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)->inError()) { debugOutput(DEBUG_LEVEL_VERBOSE, "SP %p in error state\n", *it); } else if(!(*it)->scheduleStopDryRunning(-1)) { debugError("%p->scheduleStopDryRunning(-1) failed\n", *it); return false; } } // wait for the SP's to get into the stopped state cnt = 8000; ready = false; while (!ready && cnt) { ready = true; for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { ready &= ((*it)->isStopped() || (*it)->inError()); } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { ready &= ((*it)->isStopped() || (*it)->inError()); } SleepRelativeUsec(125); cnt--; } if(cnt==0) { debugWarning(" Timeout waiting for the SP's to stop\n"); for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { (*it)->dumpInfo(); } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { (*it)->dumpInfo(); } return false; } debugOutput( DEBUG_LEVEL_VERBOSE, " Stopped...\n"); return true; } /** * Called upon Xrun events. This brings all StreamProcessors back * into their starting state, and then carries on streaming. This should * have the same effect as restarting the whole thing. * * @return true if successful, false otherwise */ bool StreamProcessorManager::handleXrun() { debugOutput( DEBUG_LEVEL_VERBOSE, "Handling Xrun ...\n"); dumpInfo(); /* * Reset means: * 1) Disabling the SP's, so that they don't process any packets * note: the isomanager does keep on delivering/requesting them * 2) Bringing all buffers & streamprocessors into a know state * - Clear all capture buffers * - Put nb_periods*period_size of null frames into the playback buffers * 3) Re-enable the SP's */ debugOutput( DEBUG_LEVEL_VERBOSE, "Restarting StreamProcessors...\n"); // start all SP's synchonized bool start_result = false; for (int ntries=0; ntries < STREAMPROCESSORMANAGER_SYNCSTART_TRIES; ntries++) { if(m_shutdown_needed) { debugOutput(DEBUG_LEVEL_VERBOSE, "Shutdown requested...\n"); return true; } // put all SP's into dry-running state if (!startDryRunning()) { debugShowBackLog(); debugOutput(DEBUG_LEVEL_VERBOSE, "Could not put SP's in dry-running state (try %d)\n", ntries); start_result = false; continue; } start_result = syncStartAll(); if(start_result) { break; } else { debugOutput(DEBUG_LEVEL_VERBOSE, "Sync start try %d failed...\n", ntries); } } if (!start_result) { debugFatal("Could not syncStartAll...\n"); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "Xrun handled...\n"); return true; } /** * @brief Waits until the next period of samples is ready * * This function does not return until a full period of samples is (or should be) * ready to be transferred. * * @return true if the period is ready, false if not */ bool StreamProcessorManager::waitForPeriod() { if(m_SyncSource == NULL) return false; if(m_shutdown_needed) return false; bool xrun_occurred = false; bool in_error = false; // grab the wait lock // this ensures that bus reset handling doesn't interfere Util::MutexLockHelper lock(*m_WaitLock); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "waiting for period (%d frames in buffer)...\n", m_SyncSource->getBufferFill()); uint64_t ticks_at_period = m_SyncSource->getTimeAtPeriod(); uint64_t ticks_at_period_margin = ticks_at_period + m_sync_delay; uint64_t pred_system_time_at_xfer = m_SyncSource->getParent().get1394Service().getSystemTimeForCycleTimerTicks(ticks_at_period_margin); #if DEBUG_EXTREME_ENABLE int64_t now = Util::SystemTimeSource::getCurrentTime(); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "CTR pred: %" PRId64 ", syncdelay: %" PRId64 ", diff: %" PRId64 "\n", ticks_at_period, ticks_at_period_margin, ticks_at_period_margin-ticks_at_period ); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "PREWAIT pred: %" PRId64 ", now: %" PRId64 ", wait: %" PRId64 "\n", pred_system_time_at_xfer, now, pred_system_time_at_xfer-now ); #endif // wait until it's time to transfer Util::SystemTimeSource::SleepUsecAbsolute(pred_system_time_at_xfer); #if DEBUG_EXTREME_ENABLE now = Util::SystemTimeSource::getCurrentTime(); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "POSTWAIT pred: %" PRId64 ", now: %" PRId64 ", excess: %" PRId64 "\n", pred_system_time_at_xfer, now, now-pred_system_time_at_xfer ); #endif // the period should be ready now #if DEBUG_EXTREME_ENABLE int rcv_fills[10]; int xmt_fills[10]; int i; i=0; for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { rcv_fills[i] = (*it)->getBufferFill(); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "RECV SP %p bufferfill: %05d\n", *it, rcv_fills[i]); i++; } i=0; for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { xmt_fills[i] = (*it)->getBufferFill(); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "XMIT SP %p bufferfill: %05d\n", *it, xmt_fills[i]); i++; } for(i=0;i<1;i++) { debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "SP %02d RECV: %05d [%05d] XMIT: %05d [%05d] DIFF: %05d\n", i, rcv_fills[i], rcv_fills[i] - m_period, xmt_fills[i], xmt_fills[i] - m_period, rcv_fills[i] - xmt_fills[i]); } #endif #if STREAMPROCESSORMANAGER_ALLOW_DELAYED_PERIOD_SIGNAL // HACK: we force wait until every SP is ready. this is needed // since the raw1394 interface provides no control over interrupts // resulting in very bad predictability on when the data is present. bool period_not_ready = true; while(period_not_ready) { period_not_ready = false; for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { bool this_sp_period_ready = (*it)->canConsumePeriod(); if (!this_sp_period_ready) { period_not_ready = true; } } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { bool this_sp_period_ready = (*it)->canProducePeriod(); if (!this_sp_period_ready) { period_not_ready = true; } } if (period_not_ready) { debugOutput(DEBUG_LEVEL_VERBOSE, " wait extended since period not ready...\n"); Util::SystemTimeSource::SleepUsecRelative(125); // one cycle } // check for underruns/errors 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(); in_error |= (*it)->inError(); } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { // a xrun has occurred on the Iso side xrun_occurred |= (*it)->xrunOccurred(); in_error |= (*it)->inError(); } if(xrun_occurred | in_error | m_shutdown_needed) break; } #else // check for underruns/errors 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 ) { // xrun on data buffer side if (!(*it)->canConsumePeriod()) { xrun_occurred = true; } // a xrun has occurred on the Iso side xrun_occurred |= (*it)->xrunOccurred(); in_error |= (*it)->inError(); } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { // xrun on data buffer side if (!(*it)->canProducePeriod()) { xrun_occurred = true; } // a xrun has occurred on the Iso side xrun_occurred |= (*it)->xrunOccurred(); in_error |= (*it)->inError(); } #endif if(xrun_occurred) { debugOutput( DEBUG_LEVEL_VERBOSE, "exit due to xrun...\n"); } if(in_error) { debugOutput( DEBUG_LEVEL_VERBOSE, "exit due to error...\n"); m_shutdown_needed = true; } // we save the 'ideal' time of the transfer at this point, // because we can have interleaved read - process - write // cycles making that we modify a receiving stream's buffer // before we get to writing. // NOTE: before waitForPeriod() is called again, both the transmit // and the receive processors should have done their transfer. m_time_of_transfer = m_SyncSource->getTimeAtPeriod(); #ifdef DEBUG int ticks_per_period = (int)(m_SyncSource->getTicksPerFrame() * m_period); int diff = diffTicks(m_time_of_transfer, m_time_of_transfer2); // display message if the difference between two successive tick // values is more than 50 ticks. 1 sample at 48k is 512 ticks // so 50 ticks = 10%, which is a rather large jitter value. if(diff-ticks_per_period > m_max_diff_ticks || diff-ticks_per_period < -m_max_diff_ticks) { debugOutput(DEBUG_LEVEL_VERBOSE, "rather large TSP difference TS=%011" PRIu64 " => TS=%011" PRIu64 " (%d, nom %d)\n", m_time_of_transfer2, m_time_of_transfer, diff, ticks_per_period); } m_time_of_transfer2 = m_time_of_transfer; #endif debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "transfer period %d at %" PRIu64 " ticks...\n", m_nbperiods, m_time_of_transfer); #if DEBUG_EXTREME_ENABLE int rcv_bf=0, xmt_bf=0; for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { rcv_bf = (*it)->getBufferFill(); } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { xmt_bf = (*it)->getBufferFill(); } debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "XF at %011" PRIu64 " ticks, RBF=%d, XBF=%d, SUM=%d...\n", m_time_of_transfer, rcv_bf, xmt_bf, rcv_bf+xmt_bf); #endif #ifdef DEBUG // check if xruns occurred on the Iso side. // also check if xruns will occur should we transfer() now for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { if ((*it)->xrunOccurred()) { debugOutput(DEBUG_LEVEL_NORMAL, "Xrun on RECV SP %p due to ISO side xrun\n", *it); (*it)->dumpInfo(); } if (!((*it)->canClientTransferFrames(m_period))) { debugOutput(DEBUG_LEVEL_NORMAL, "Xrun on RECV SP %p due to buffer side xrun\n", *it); (*it)->dumpInfo(); } } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { if ((*it)->xrunOccurred()) { debugOutput(DEBUG_LEVEL_NORMAL, "Xrun on XMIT SP %p due to ISO side xrun\n", *it); } if (!((*it)->canClientTransferFrames(m_period))) { debugOutput(DEBUG_LEVEL_NORMAL, "Xrun on XMIT SP %p due to buffer side xrun\n", *it); } } #endif m_nbperiods++; // this is to notify the client of the delay that we introduced by waiting pred_system_time_at_xfer = m_SyncSource->getParent().get1394Service().getSystemTimeForCycleTimerTicks(m_time_of_transfer); m_delayed_usecs = Util::SystemTimeSource::getCurrentTime() - pred_system_time_at_xfer; debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "delayed for %d usecs...\n", m_delayed_usecs); // now we can signal the client that we are (should be) ready return !xrun_occurred; } /** * @brief Transfer one period of frames for both receive and transmit StreamProcessors * * Transfers one period of frames from the client side to the Iso side and vice versa. * * @return true if successful, false otherwise (indicates xrun). */ bool StreamProcessorManager::transfer() { debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "Transferring period...\n"); bool retval=true; retval &= transfer(StreamProcessor::ePT_Receive); retval &= transfer(StreamProcessor::ePT_Transmit); return retval; } /** * @brief Transfer one period of frames for either the receive or transmit StreamProcessors * * Transfers one period of frames from the client side to the Iso side or vice versa. * * @param t The processor type to tranfer for (receive or transmit) * @return true if successful, false otherwise (indicates xrun). */ bool StreamProcessorManager::transfer(enum StreamProcessor::eProcessorType t) { if(m_SyncSource == NULL) return false; debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "transfer(%d) at TS=%011" PRIu64 " (%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, %11" PRIu64 ") 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(); for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { // this is the delay in frames between the point where a frame is received and // when it is transmitted again unsigned int one_ringbuffer_in_frames = m_nb_buffers * m_period + (*it)->getExtraBufferFrames(); int64_t one_ringbuffer_in_ticks = (int64_t)(((float)one_ringbuffer_in_frames) * 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); if(!(*it)->putFrames(m_period, transmit_timestamp)) { debugWarning("could not putFrames(%u,%" PRIu64 ") to stream processor (%p)\n", m_period, transmit_timestamp, *it); retval &= false; // buffer underrun } } } return retval; } /** * @brief Transfer one period of silence for both receive and transmit StreamProcessors * * Transfers one period of silence to the Iso side for transmit SP's * or dump one period of frames for receive SP's * * @return true if successful, false otherwise (indicates xrun). */ bool StreamProcessorManager::transferSilence() { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Transferring silent period...\n"); bool retval=true; // NOTE: the order here is opposite from the order in // normal operation (transmit is before receive), because // we can do that here (data=silence=available) and // it increases reliability (esp. on startup) retval &= transferSilence(StreamProcessor::ePT_Transmit); retval &= transferSilence(StreamProcessor::ePT_Receive); return retval; } /** * @brief Transfer one period of silence for either the receive or transmit StreamProcessors * * Transfers one period of silence to the Iso side for transmit SP's * or dump one period of frames for receive SP's * * @param t The processor type to tranfer for (receive or transmit) * @return true if successful, false otherwise (indicates xrun). */ bool StreamProcessorManager::transferSilence(enum StreamProcessor::eProcessorType t) { if(m_SyncSource == NULL) return false; debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "transferSilence(%d) at TS=%011" PRIu64 " (%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, %11" PRIu64 ") 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(); for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { // this is the delay in frames between the point where a frame is received and // when it is transmitted again unsigned int one_ringbuffer_in_frames = m_nb_buffers * m_period + (*it)->getExtraBufferFrames(); int64_t one_ringbuffer_in_ticks = (int64_t)(((float)one_ringbuffer_in_frames) * 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); if(!(*it)->putSilenceFrames(m_period, transmit_timestamp)) { debugWarning("could not putSilenceFrames(%u,%" PRIu64 ") to stream processor (%p)\n", m_period, transmit_timestamp, *it); retval &= false; // buffer underrun } } } return retval; } void StreamProcessorManager::dumpInfo() { debugOutputShort( DEBUG_LEVEL_NORMAL, "----------------------------------------------------\n"); debugOutputShort( DEBUG_LEVEL_NORMAL, "Dumping StreamProcessorManager information...\n"); debugOutputShort( DEBUG_LEVEL_NORMAL, "Period count: %6d\n", m_nbperiods); debugOutputShort( DEBUG_LEVEL_NORMAL, "Data type: %s\n", (m_audio_datatype==eADT_Float?"float":"int24")); debugOutputShort( DEBUG_LEVEL_NORMAL, " Receive processors...\n"); for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { (*it)->dumpInfo(); } debugOutputShort( DEBUG_LEVEL_NORMAL, " Transmit processors...\n"); for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { (*it)->dumpInfo(); } debugOutputShort( DEBUG_LEVEL_NORMAL, "----------------------------------------------------\n"); // list port info in verbose mode debugOutputShort( DEBUG_LEVEL_VERBOSE, "Port Information\n"); int nb_ports; debugOutputShort( DEBUG_LEVEL_VERBOSE, " Playback\n"); nb_ports = getPortCount(Port::E_Playback); for(int i=0; i < nb_ports; i++) { Port *p = getPortByIndex(i, Port::E_Playback); debugOutputShort( DEBUG_LEVEL_VERBOSE, " %3d (%p): ", i, p); if (p) { bool disabled = p->isDisabled(); debugOutputShort( DEBUG_LEVEL_VERBOSE, "[%p] [%3s] ", &p->getManager(), (disabled?"off":"on")); debugOutputShort( DEBUG_LEVEL_VERBOSE, "[%7s] ", p->getPortTypeName().c_str()); debugOutputShort( DEBUG_LEVEL_VERBOSE, "%3s ", p->getName().c_str()); } else { debugOutputShort( DEBUG_LEVEL_VERBOSE, "invalid "); } debugOutputShort( DEBUG_LEVEL_VERBOSE, "\n"); } debugOutputShort( DEBUG_LEVEL_VERBOSE, " Capture\n"); nb_ports = getPortCount(Port::E_Capture); for(int i=0; i < nb_ports; i++) { Port *p = getPortByIndex(i, Port::E_Capture); debugOutputShort( DEBUG_LEVEL_VERBOSE, " %3d (%p): ", i, p); if (p) { bool disabled = p->isDisabled(); debugOutputShort( DEBUG_LEVEL_VERBOSE, "[%p] [%3s] ", &p->getManager(), (disabled?"off":"on")); debugOutputShort( DEBUG_LEVEL_VERBOSE, "[%7s] ", p->getPortTypeName().c_str()); debugOutputShort( DEBUG_LEVEL_VERBOSE, " %3s ", p->getName().c_str()); } else { debugOutputShort( DEBUG_LEVEL_VERBOSE, " invalid "); } debugOutputShort( DEBUG_LEVEL_VERBOSE, "\n"); } debugOutputShort( DEBUG_LEVEL_VERBOSE, "----------------------------------------------------\n"); } void StreamProcessorManager::setVerboseLevel(int l) { if(m_WaitLock) m_WaitLock->setVerboseLevel(l); for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { (*it)->setVerboseLevel(l); } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { (*it)->setVerboseLevel(l); } setDebugLevel(l); debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", 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; } void StreamProcessorManager::updateShadowLists() { debugOutput( DEBUG_LEVEL_VERBOSE, "Updating port shadow lists...\n"); m_CapturePorts_shadow.clear(); m_PlaybackPorts_shadow.clear(); for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); it != m_ReceiveProcessors.end(); ++it ) { PortManager *pm = *it; for (int i=0; i < pm->getPortCount(); i++) { Port *p = pm->getPortAtIdx(i); if (!p) { debugError("getPortAtIdx(%d) returned NULL\n", i); continue; } if(p->getDirection() != Port::E_Capture) { debugError("port at idx %d for receive SP is not a capture port!\n", i); continue; } m_CapturePorts_shadow.push_back(p); } } for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); it != m_TransmitProcessors.end(); ++it ) { PortManager *pm = *it; for (int i=0; i < pm->getPortCount(); i++) { Port *p = pm->getPortAtIdx(i); if (!p) { debugError("getPortAtIdx(%d) returned NULL\n", i); continue; } if(p->getDirection() != Port::E_Playback) { debugError("port at idx %d for transmit SP is not a playback port!\n", i); continue; } m_PlaybackPorts_shadow.push_back(p); } } } Port* StreamProcessorManager::getPortByIndex(int idx, enum Port::E_Direction direction) { debugOutputExtreme( DEBUG_LEVEL_ULTRA_VERBOSE, "getPortByIndex(%d, %d)...\n", idx, direction); if (direction == Port::E_Capture) { #ifdef DEBUG if(idx >= (int)m_CapturePorts_shadow.size()) { debugError("Capture port %d out of range (%zd)\n", idx, m_CapturePorts_shadow.size()); return NULL; } #endif return m_CapturePorts_shadow.at(idx); } else { #ifdef DEBUG if(idx >= (int)m_PlaybackPorts_shadow.size()) { debugError("Playback port %d out of range (%zd)\n", idx, m_PlaybackPorts_shadow.size()); return NULL; } #endif return m_PlaybackPorts_shadow.at(idx); } return NULL; } bool StreamProcessorManager::setThreadParameters(bool rt, int priority) { m_thread_realtime=rt; m_thread_priority=priority; return true; } } // end of namespace libffado-2.4.5/src/libstreaming/StreamProcessorManager.h0000644000175000001440000001365314206145246022742 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_STREAMPROCESSORMANAGER__ #define __FFADO_STREAMPROCESSORMANAGER__ #include "generic/PortManager.h" #include "generic/Port.h" #include "generic/StreamProcessor.h" #include "debugmodule/debugmodule.h" #include "libutil/Thread.h" #include "libutil/Mutex.h" #include "libutil/OptionContainer.h" #include #include class DeviceManager; namespace Streaming { class StreamProcessor; typedef std::vector StreamProcessorVector; typedef std::vector::iterator StreamProcessorVectorIterator; /*! \brief Manages a collection of StreamProcessors and provides a synchronisation interface */ class StreamProcessorManager : public Util::OptionContainer { friend class StreamProcessor; public: enum eADT_AudioDataType { eADT_Int24, eADT_Float, }; StreamProcessorManager(DeviceManager &parent); StreamProcessorManager(DeviceManager &parent, 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(); // activity signaling enum eActivityResult { eAR_Activity, eAR_Timeout, eAR_Interrupted, eAR_Error }; void signalActivity(); enum eActivityResult waitForActivity(); // this is the setup API bool registerProcessor(StreamProcessor *processor); ///< start managing a streamprocessor bool unregisterProcessor(StreamProcessor *processor); ///< stop managing a streamprocessor bool streamingParamsOk(signed int period, signed int rate, signed int n_buffers); void setPeriodSize(unsigned int period); unsigned int getPeriodSize() {return m_period;}; bool setAudioDataType(enum eADT_AudioDataType t) {m_audio_datatype = t; return true;}; enum eADT_AudioDataType getAudioDataType() {return m_audio_datatype;} void setNbBuffers(unsigned int nb_buffers) {m_nb_buffers = nb_buffers;}; unsigned int getNbBuffers() {return m_nb_buffers;}; void setMaxDiffTicks(signed int n) { m_max_diff_ticks = n; }; // this is the amount of usecs we wait before an activity // timeout occurs. void setActivityWaitTimeoutUsec(int usec) {m_activity_wait_timeout_nsec = usec*1000LL;}; int getActivityWaitTimeoutUsec() {return m_activity_wait_timeout_nsec/1000;}; 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); // for bus reset handling void lockWaitLoop() {m_WaitLock->Lock();}; void unlockWaitLoop() {m_WaitLock->Unlock();}; private: bool transferSilence(); bool transferSilence(enum StreamProcessor::eProcessorType); bool alignReceivedStreams(); public: int getDelayedUsecs() {return m_delayed_usecs;}; bool xrunOccurred(); bool shutdownNeeded() {return m_shutdown_needed;}; int getXrunCount() {return m_xruns;}; void setNominalRate(unsigned int r) {m_nominal_framerate = r;}; unsigned int getNominalRate() {return m_nominal_framerate;}; uint64_t getTimeOfLastTransfer() { return m_time_of_transfer;}; private: int m_delayed_usecs; // this stores the time at which the next transfer should occur // usually this is in the past, but it is needed as a timestamp // for the transmit SP's uint64_t m_time_of_transfer; #ifdef DEBUG uint64_t m_time_of_transfer2; #endif public: bool handleXrun(); ///< reset the streams & buffers after xrun bool setThreadParameters(bool rt, int priority); virtual void setVerboseLevel(int l); void dumpInfo(); private: // slaving support bool m_is_slave; // the sync source stuff private: StreamProcessor *m_SyncSource; public: bool setSyncSource(StreamProcessor *s); StreamProcessor& getSyncSource() {return *m_SyncSource;}; protected: // FIXME: private? // parent device manager DeviceManager &m_parent; // thread related vars bool m_xrun_happened; int64_t m_activity_wait_timeout_nsec; bool m_thread_realtime; int m_thread_priority; // activity signaling sem_t m_activity_semaphore; // processor list StreamProcessorVector m_ReceiveProcessors; StreamProcessorVector m_TransmitProcessors; // port shadow lists PortVector m_CapturePorts_shadow; PortVector m_PlaybackPorts_shadow; void updateShadowLists(); unsigned int m_nb_buffers; unsigned int m_period; unsigned int m_sync_delay; enum eADT_AudioDataType m_audio_datatype; unsigned int m_nominal_framerate; unsigned int m_xruns; bool m_shutdown_needed; unsigned int m_nbperiods; Util::Mutex *m_WaitLock; signed int m_max_diff_ticks; DECLARE_DEBUG_MODULE; }; } #endif /* __FFADO_STREAMPROCESSORMANAGER__ */ libffado-2.4.5/src/libstreaming/amdtp/0000755000175000001440000000000014206145612017235 5ustar jwoitheuserslibffado-2.4.5/src/libstreaming/amdtp/AmdtpBufferOps.h0000644000175000001440000001126214206145246022274 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_AMDTPBUFFEROPS__ #define __FFADO_AMDTPBUFFEROPS__ #include // to check for SSE etc... #include #define AMDTP_FLOAT_MULTIPLIER 2147483392.0 #ifdef __SSE2__ //#if 0 #include // There's no need to warn about this anymore - jwoithe. // #warning SSE2 build //static inline void void convertFromFloatAndLabelAsMBLA(quadlet_t *data, unsigned int nb_elements) { // Work input until data reaches 16 byte alignment while ((((unsigned long)data) & 0xF) && nb_elements > 0) { float *in = (float *)data; float v = (*in) * AMDTP_FLOAT_MULTIPLIER; unsigned int tmp = ((int) v); tmp = ( tmp >> 8 ) | 0x40000000; *data = (quadlet_t)tmp; data++; nb_elements--; } assert((((unsigned long)data) & 0xF) == 0); // now do the SSE based conversion/labeling __m128i v_int; __m128i label = _mm_set_epi32 (0x40000000, 0x40000000, 0x40000000, 0x40000000); __m128 mult = _mm_set_ps(AMDTP_FLOAT_MULTIPLIER, AMDTP_FLOAT_MULTIPLIER, AMDTP_FLOAT_MULTIPLIER, AMDTP_FLOAT_MULTIPLIER); __m128 v_float; while(nb_elements >= 4) { float *in = (float *)data; // load the data into the vector unit v_float = _mm_load_ps(in); // multiply v_float = _mm_mul_ps(v_float, mult); // convert to signed integer v_int = _mm_cvttps_epi32( v_float ); // shift right 8 bits v_int = _mm_srli_epi32( v_int, 8 ); // label it v_int = _mm_or_si128( v_int, label ); // store result _mm_store_si128 ((__m128i*)data, v_int); data += 4; nb_elements -= 4; } // and do the remaining ones while (nb_elements > 0) { float *in = (float *)data; float v = (*in) * AMDTP_FLOAT_MULTIPLIER; unsigned int tmp = ((int) v); tmp = ( tmp >> 8 ) | 0x40000000; *data = (quadlet_t)tmp; data++; nb_elements--; } } //static inline void void convertFromInt24AndLabelAsMBLA(quadlet_t *data, unsigned int nb_elements) { // Work input until data reaches 16 byte alignment while ((((unsigned long)data) & 0xF) && nb_elements > 0) { uint32_t in = (uint32_t)(*data); *data = (quadlet_t)((in & 0x00FFFFFF) | 0x40000000); data++; nb_elements--; } assert((((unsigned long)data) & 0xF) == 0); // now do the SSE based labeling __m128i v; const __m128i mask = _mm_set_epi32 (0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF); const __m128i label = _mm_set_epi32 (0x40000000, 0x40000000, 0x40000000, 0x40000000); while(nb_elements >= 4) { // load the data into the vector unit v = _mm_load_si128((__m128i*)data); // mask v = _mm_and_si128( v, mask ); // label v = _mm_or_si128( v, label ); // store result _mm_store_si128 ((__m128i*)data, v); data += 4; nb_elements -= 4; } // and do the remaining ones while (nb_elements > 0) { uint32_t in = (uint32_t)(*data); *data = (quadlet_t)((in & 0x00FFFFFF) | 0x40000000); data++; nb_elements--; } } #else //static inline void void convertFromFloatAndLabelAsMBLA(quadlet_t *data, unsigned int nb_elements) { unsigned int i=0; for(; i> 8 ) | 0x40000000; *data = (quadlet_t)tmp; data++; } } //static inline void void convertFromInt24AndLabelAsMBLA(quadlet_t *data, unsigned int nb_elements) { unsigned int i=0; for(; i. * */ #include "AmdtpPort.h" #include namespace Streaming { } // end of namespace Streaming libffado-2.4.5/src/libstreaming/amdtp/AmdtpPort.h0000644000175000001440000000427714206145246021335 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_AMDTPPORT__ #define __FFADO_AMDTPPORT__ /** * This file implements the AMDTP ports as used in the BeBoB's */ #include "debugmodule/debugmodule.h" #include "../generic/Port.h" #include "AmdtpPortInfo.h" namespace Streaming { /*! \brief The Base Class for an AMDTP Audio Port The AMDTP/AM824/IEC61883-6 port that represents audio. */ class AmdtpAudioPort : public AudioPort, public AmdtpPortInfo { public: AmdtpAudioPort(PortManager &m, std::string name, enum E_Direction direction, int position, int location, enum E_Formats format) : AudioPort(m, name, direction), AmdtpPortInfo(position, location, format) {}; virtual ~AmdtpAudioPort() {}; }; /*! \brief The Base Class for an AMDTP Midi Port The AMDTP/AM824/IEC61883-6 port that represents midi. */ class AmdtpMidiPort : public MidiPort, public AmdtpPortInfo { public: AmdtpMidiPort(PortManager &m, std::string name, enum E_Direction direction, int position, int location, enum E_Formats format) : MidiPort(m, name, direction), AmdtpPortInfo(position, location, format) {}; virtual ~AmdtpMidiPort() {}; }; } // end of namespace Streaming #endif /* __FFADO_AMDTPPORT__ */ libffado-2.4.5/src/libstreaming/amdtp/AmdtpPortInfo.cpp0000644000175000001440000000165414206145246022500 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "AmdtpPortInfo.h" #include namespace Streaming { } // end of namespace Streaming libffado-2.4.5/src/libstreaming/amdtp/AmdtpPortInfo.h0000644000175000001440000000440714206145246022144 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_AMDTPPORTINFO__ #define __FFADO_AMDTPPORTINFO__ #include "debugmodule/debugmodule.h" #include namespace Streaming { /*! \brief Class containing the stream information for an AMDTP channel Contains the information that maps the port to an AMDTP stream position (i.e. channel) this allows the AMDTP stream demultiplexer to find the channel associated to this port. */ class AmdtpPortInfo { public: /** * Sometimes a channel can have multiple formats, depending on the * device configuration (e.g. an SPDIF port could be plain audio in 24bit integer * or AC3 passthrough in IEC compliant frames.) * * This kind of enum allows to discriminate these formats when decoding * If all channels always have the same format, you won't be needing this */ enum E_Formats { E_MBLA, ///< multibit linear audio E_Midi, ///< midi E_SPDIF,///< IEC.... format }; AmdtpPortInfo( int position, int location, enum E_Formats format) : m_position(position), m_location(location), m_format(format) {}; virtual ~AmdtpPortInfo() {}; unsigned int getLocation() {return m_location;}; unsigned int getPosition() {return m_position;}; enum E_Formats getFormat() {return m_format;}; protected: unsigned int m_position; unsigned int m_location; enum E_Formats m_format; }; } // end of namespace Streaming #endif /* __FFADO_AMDTPPORTINFO__ */ libffado-2.4.5/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.cpp0000644000175000001440000005024614206145246025377 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "AmdtpReceiveStreamProcessor.h" #include "AmdtpPort.h" #include "../StreamProcessorManager.h" #include "devicemanager.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/IsoHandlerManager.h" #include "libieee1394/cycletimer.h" #include "libutil/ByteSwap.h" #include #include "libutil/SystemTimeSource.h" #include #define unlikely(x) __builtin_expect((x),0) namespace Streaming { /* --------------------- RECEIVE ----------------------- */ AmdtpReceiveStreamProcessor::AmdtpReceiveStreamProcessor(FFADODevice &parent, int dimension) : StreamProcessor(parent, ePT_Receive) , m_dimension( dimension ) , m_nb_audio_ports( 0 ) , m_nb_midi_ports( 0 ) , mb_head( 0 ) , mb_tail( 0 ) {} unsigned int AmdtpReceiveStreamProcessor::getSytInterval() { switch (m_StreamProcessorManager.getNominalRate()) { case 32000: case 44100: case 48000: return 8; case 88200: case 96000: return 16; case 176400: case 192000: return 32; default: debugError("Unsupported rate: %d\n", m_StreamProcessorManager.getNominalRate()); return 0; } } bool AmdtpReceiveStreamProcessor::prepareChild() { debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this); m_syt_interval = getSytInterval(); if (!initPortCache()) { debugError("Could not init port cache\n"); return false; } return true; } /** * Processes packet header to extract timestamps and so on * @param data * @param length * @param channel * @param tag * @param sy * @param pkt_ctr CTR value when packet was received * @return */ enum StreamProcessor::eChildReturnValue AmdtpReceiveStreamProcessor::processPacketHeader(unsigned char *data, unsigned int length, unsigned char tag, unsigned char sy, uint32_t pkt_ctr) { struct iec61883_packet *packet = (struct iec61883_packet *) data; assert(packet); bool ok = (packet->syt != 0xFFFF) && (packet->fdf != 0xFF) && (packet->fmt == 0x10) && (packet->dbs > 0) && (length >= 2*sizeof(quadlet_t)); if(ok) { m_last_timestamp = sytRecvToFullTicks2((uint32_t)CondSwapFromBus16(packet->syt), pkt_ctr); //#ifdef DEBUG #if 0 uint32_t now = m_1394service.getCycleTimer(); //=> convert the SYT to a full timestamp in ticks uint64_t old_last_timestamp = sytRecvToFullTicks((uint32_t)CondSwapFromBus16(packet->syt), CYCLE_TIMER_GET_CYCLES(pkt_ctr), now); if(m_last_timestamp != old_last_timestamp) { debugWarning("discepancy between timestamp calculations\n"); } #endif } return (ok ? eCRV_OK : eCRV_Invalid ); } /** * extract the data from the packet * @pre the IEC61883 packet is valid according to isValidPacket * @param data * @param length * @param channel * @param tag * @param sy * @param pkt_ctr * @return */ enum StreamProcessor::eChildReturnValue AmdtpReceiveStreamProcessor::processPacketData(unsigned char *data, unsigned int length) { struct iec61883_packet *packet = (struct iec61883_packet *) data; assert(packet); /* Euan de Kock 8th February 2011 See Ticket #310 This is a fix to force the Echo Devices to work at sample rates above 48000 where the ADAT digital channels get multiplexed to handle higher frequencies. At 88200 and 96000 the ADAT channels are reduced to 4, and at 192000 they should be reduced to 2. (I don't have a device capable of 192K to verify this) With the Echo devices (5.5 firmware at least), the device does not reduce its channel count when it multiplexes the ADAT data, but the actual packet does contain a reduced set of channels. The channels are referred to as Dimensions in the FireWire parlance. With an Audiofire Pre8, we typically receive 17 channels of data at 44100 and 48000, being 8 Analog, 8 Digital and 1 MIDI. When we switch to 88200 or 96000 the ADAT (Digital) channel count is reduced to four, giving a total channel count (dimension) of 13. However Echo still reports the DBS parameter as being 17. To get around this, I don't bother using the DBS value at all. We know that the FDF is a reliable value for AM824 packets (type 0 to 7), so we can just use the FDF field to directly infer the size of each channel or dimension - 8, 16 or 32 Quadwords respectively for 41/48K, 88/96K and 192K. We set nevents directly to this for all AM824 packets directly, and do the standard calculation for any non-standard types. This is actually slightly less work than doing the calculation directly and should be portable across all devices that send AM824 data. */ unsigned int nevents; switch(packet->fdf) { case 0x00: case 0x01: case 0x02: // 8Q = 32B nevents=8; break; case 0x03: case 0x04: // 16Q = 64B nevents=16; break; case 0x05: case 0x06: // 32Q = 128B nevents=32; break; default: nevents=((length / sizeof (quadlet_t)) - 2)/packet->dbs; break; } debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "packet->dbs %d calculated dbs %d packet->fdf %02X nevents %d\n", packet->dbs, (length - 8)/nevents, packet->fdf, nevents); // 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) #if DEBUG_EXTREME_ENABLE static int64_t last_t = Util::SystemTimeSource::getCurrentTime(); int64_t now_t = Util::SystemTimeSource::getCurrentTime(); if(isRunning()) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "STMP: %"PRIu64"ticks | syt_interval=%d, tpf=%f\n", m_last_timestamp, m_syt_interval, getTicksPerFrame()); /* debugOutput(DEBUG_LEVEL_NORMAL, "STMP: %12"PRIu64" ticks | delta_t: %5"PRId64" | bufferfill: %5d\n", m_last_timestamp, now_t-last_t, m_data_buffer->getBufferFill());*/ } last_t = now_t; #endif #ifdef DEBUG // check whether nevents is a multiple of 8. if (nevents & 0x7) { debugError("Invalid nevents value for AMDTP (%u)\n", nevents); } #endif if(m_data_buffer->writeFrames(nevents, (char *)(data+8), m_last_timestamp)) { return eCRV_OK; } else { return eCRV_XRun; } } /*********************************************** * Encoding/Decoding API * ***********************************************/ /** * @brief write received events to the stream ringbuffers. */ bool AmdtpReceiveStreamProcessor::processReadBlock(char *data, unsigned int nevents, unsigned int offset) { debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "(%p)->processReadBlock(%u, %u)\n", this, nevents, offset); // update the variable parts of the cache updatePortCache(); // decode audio data switch(m_StreamProcessorManager.getAudioDataType()) { case StreamProcessorManager::eADT_Int24: decodeAudioPortsInt24((quadlet_t *)data, offset, nevents); break; case StreamProcessorManager::eADT_Float: decodeAudioPortsFloat((quadlet_t *)data, offset, nevents); break; } // do midi ports decodeMidiPorts((quadlet_t *)data, offset, nevents); return true; } //#ifdef __SSE2__ #if 0 // SSE code is not ready yet #include #warning SSE2 build /** * @brief demux events to all audio ports (int24) * @param data * @param offset * @param nevents */ void AmdtpReceiveStreamProcessor::decodeAudioPortsInt24(quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j; quadlet_t *target_event; unsigned int i; for (i = 0; i < m_nb_audio_ports; i++) { struct _MBLA_port_cache &p = m_audio_ports.at(i); target_event = (quadlet_t *)(data + i); #ifdef DEBUG assert(nevents + offset <= p.buffer_size ); #endif if(p.buffer && p.enabled) { quadlet_t *buffer = (quadlet_t *)(p.buffer); buffer += offset; for(j = 0; j < nevents; j += 1) { *(buffer)=(CondSwapFromBus32((*target_event) ) & 0x00FFFFFF); buffer++; target_event+=m_dimension; } } } } /** * @brief demux events to all audio ports (float) * @param data * @param offset * @param nevents */ void AmdtpReceiveStreamProcessor::decodeAudioPortsFloat(quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j; quadlet_t *target_event; unsigned int i; const float multiplier = 1.0f / (float)(0x7FFFFF); for (i = 0; i < m_nb_audio_ports; i++) { struct _MBLA_port_cache &p = m_audio_ports.at(i); target_event = (quadlet_t *)(data + i); #ifdef DEBUG assert(nevents + offset <= p.buffer_size ); #endif if(p.buffer && p.enabled) { float *buffer = (float *)(p.buffer); buffer += offset; for(j = 0; j < nevents; j += 1) { unsigned int v = CondSwapFromBus32(*target_event) & 0x00FFFFFF; // sign-extend highest bit of 24-bit int int tmp = (int)(v << 8) / 256; *buffer = tmp * multiplier; buffer++; target_event+=m_dimension; } } } } #else /** * @brief demux events to all audio ports (int24) * @param data * @param offset * @param nevents */ void AmdtpReceiveStreamProcessor::decodeAudioPortsInt24(quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j; quadlet_t *target_event; unsigned int i; for (i = 0; i < m_nb_audio_ports; i++) { struct _MBLA_port_cache &p = m_audio_ports.at(i); target_event = (quadlet_t *)(data + i); #ifdef DEBUG assert(nevents + offset <= p.buffer_size ); #endif if(p.buffer && p.enabled) { quadlet_t *buffer = (quadlet_t *)(p.buffer); buffer += offset; for(j = 0; j < nevents; j += 1) { *(buffer)=(CondSwapFromBus32((*target_event) ) & 0x00FFFFFF); buffer++; target_event+=m_dimension; } } } } /** * @brief demux events to all audio ports (float) * @param data * @param offset * @param nevents */ void AmdtpReceiveStreamProcessor::decodeAudioPortsFloat(quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j; quadlet_t *target_event; unsigned int i; const float multiplier = 1.0f / (float)(0x7FFFFF); for (i = 0; i < m_nb_audio_ports; i++) { struct _MBLA_port_cache &p = m_audio_ports.at(i); target_event = (quadlet_t *)(data + i); #ifdef DEBUG assert(nevents + offset <= p.buffer_size ); #endif if(p.buffer && p.enabled) { float *buffer = (float *)(p.buffer); buffer += offset; for(j = 0; j < nevents; j += 1) { unsigned int v = CondSwapFromBus32(*target_event) & 0x00FFFFFF; // sign-extend highest bit of 24-bit int int tmp = (int)(v << 8) / 256; *buffer = tmp * multiplier; buffer++; target_event+=m_dimension; } } } } #endif /** * @brief decode all midi ports in the cache from events * @param data * @param offset * @param nevents */ void AmdtpReceiveStreamProcessor::decodeMidiPorts(quadlet_t *data, unsigned int offset, unsigned int nevents) { quadlet_t *target_event; quadlet_t sample_int; unsigned int i,j; for (i = 0; i < m_nb_midi_ports; i++) { struct _MIDI_port_cache &p = m_midi_ports.at(i); if (p.buffer && p.enabled) { uint32_t *buffer = (quadlet_t *)(p.buffer); buffer += offset; /* clear output (to jackd) buffer for MIDI data */ memset (buffer, 0, nevents*sizeof(*buffer)); for (j = 0; j < nevents; j += 1) { target_event = (quadlet_t *) (data + ((j * m_dimension) + p.position)); sample_int = CondSwapFromBus32(*target_event); // FIXME: this assumes that 2X and 3X speed isn't used, // because only the 1X slot is put into the ringbuffer if(unlikely(IEC61883_AM824_HAS_LABEL(sample_int, IEC61883_AM824_LABEL_MIDI_1X))) { sample_int=(sample_int >> 16) & 0x000000FF; sample_int |= 0x01000000; // flag that there is a midi event present midibuffer[mb_head++] = sample_int; mb_head &= RX_MIDIBUFFER_SIZE-1; if (unlikely(mb_head == mb_tail)) { debugWarning("AMDTP rx MIDI buffer overflow\n"); /* Dump oldest byte. This overflow can only happen if the * rate coming in from the hardware MIDI port grossly * exceeds the official MIDI baud rate of 31250 bps, so it * should never occur in practice. */ mb_tail = (mb_tail + 1) & (RX_MIDIBUFFER_SIZE-1); } debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "(%p) MIDI [%d]: %08X\n", this, i, sample_int); } else if(unlikely((IEC61883_AM824_HAS_LABEL(sample_int, IEC61883_AM824_LABEL_MIDI_2X) || IEC61883_AM824_HAS_LABEL(sample_int, IEC61883_AM824_LABEL_MIDI_3X) ))) { debugOutput(DEBUG_LEVEL_VERBOSE, "Midi mode %X not supported.\n", IEC61883_AM824_GET_LABEL(sample_int)); } /* Write to the buffer if we're at an 8-sample boundary */ if (unlikely(0 == j % 8)) { if (mb_head != mb_tail) { *buffer = midibuffer[mb_tail++]; mb_tail &= RX_MIDIBUFFER_SIZE-1; } buffer += 8; } } } } } bool AmdtpReceiveStreamProcessor::initPortCache() { // make use of the fact that audio ports are the first ports in // the cluster as per AMDTP. so we can sort the ports by position // and have very efficient lookups: // m_float_ports.at(i).buffer -> audio stream i buffer // for midi ports we simply cache all port info since they are (usually) not // that numerous m_nb_audio_ports = 0; m_audio_ports.clear(); m_nb_midi_ports = 0; m_midi_ports.clear(); for(PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { AmdtpPortInfo *pinfo=dynamic_cast(*it); assert(pinfo); // this should not fail!! switch( pinfo->getFormat() ) { case AmdtpPortInfo::E_MBLA: m_nb_audio_ports++; break; case AmdtpPortInfo::E_SPDIF: // still unimplemented break; case AmdtpPortInfo::E_Midi: m_nb_midi_ports++; break; default: // ignore break; } } unsigned int idx; for (idx = 0; idx < m_nb_audio_ports; idx++) { for(PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { AmdtpPortInfo *pinfo=dynamic_cast(*it); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "idx %u: looking at port %s at position %u\n", idx, (*it)->getName().c_str(), pinfo->getPosition()); if(pinfo->getPosition() == idx) { struct _MBLA_port_cache p; p.port = dynamic_cast(*it); if(p.port == NULL) { debugError("Port is not an AmdtpAudioPort!\n"); return false; } p.buffer = NULL; // to be filled by updatePortCache #ifdef DEBUG p.buffer_size = (*it)->getBufferSize(); #endif m_audio_ports.push_back(p); debugOutput(DEBUG_LEVEL_VERBOSE, "Cached port %s at position %u\n", p.port->getName().c_str(), idx); goto next_index; } } debugError("No MBLA port found for position %d\n", idx); return false; next_index: continue; } for(PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { AmdtpPortInfo *pinfo=dynamic_cast(*it); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "idx %u: looking at port %s at position %u, location %u\n", idx, (*it)->getName().c_str(), pinfo->getPosition(), pinfo->getLocation()); if ((*it)->getPortType() == Port::E_Midi) { struct _MIDI_port_cache p; p.port = dynamic_cast(*it); if(p.port == NULL) { debugError("Port is not an AmdtpMidiPort!\n"); return false; } p.position = pinfo->getPosition(); p.location = pinfo->getLocation(); p.buffer = NULL; // to be filled by updatePortCache #ifdef DEBUG p.buffer_size = (*it)->getBufferSize(); #endif m_midi_ports.push_back(p); debugOutput(DEBUG_LEVEL_VERBOSE, "Cached port %s at position %u, location %u\n", p.port->getName().c_str(), p.position, p.location); } } return true; } //FIXME: DRY. Needs to be refactored with AmdtpTransmitStreamProcessor void AmdtpReceiveStreamProcessor::updatePortCache() { unsigned int idx; for (idx = 0; idx < m_nb_audio_ports; idx++) { struct _MBLA_port_cache& p = m_audio_ports.at(idx); AmdtpAudioPort *port = p.port; p.buffer = port->getBufferAddress(); p.enabled = !port->isDisabled(); #ifdef DEBUG p.buffer_size = port->getBufferSize(); #endif } for (idx = 0; idx < m_nb_midi_ports; idx++) { struct _MIDI_port_cache& p = m_midi_ports.at(idx); AmdtpMidiPort *port = p.port; p.buffer = port->getBufferAddress(); p.enabled = !port->isDisabled(); #ifdef DEBUG p.buffer_size = port->getBufferSize(); #endif } } } // end of namespace Streaming libffado-2.4.5/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.h0000644000175000001440000001070714206145246025042 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_AMDTPRECEIVESTREAMPROCESSOR__ #define __FFADO_AMDTPRECEIVESTREAMPROCESSOR__ /** * This class implements IEC61883-6 / AM824 / AMDTP based streaming */ #include "AmdtpStreamProcessor-common.h" 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() {}; virtual enum eChildReturnValue processPacketHeader(unsigned char *data, unsigned int length, unsigned char tag, unsigned char sy, uint32_t pkt_ctr); virtual enum eChildReturnValue processPacketData(unsigned char *data, unsigned int length); virtual bool prepareChild(); public: virtual unsigned int getEventSize() {return 4;}; virtual unsigned int getMaxPacketSize() {return 4 * (2 + getSytInterval() * m_dimension);}; virtual unsigned int getEventsPerFrame() { return m_dimension; }; virtual unsigned int getNominalFramesPerPacket() {return getSytInterval();}; protected: bool processReadBlock(char *data, unsigned int nevents, unsigned int offset); protected: void decodeAudioPortsFloat(quadlet_t *data, unsigned int offset, unsigned int nevents); void decodeAudioPortsInt24(quadlet_t *data, unsigned int offset, unsigned int nevents); void decodeMidiPorts(quadlet_t *data, unsigned int offset, unsigned int nevents); unsigned int getSytInterval(); int m_dimension; unsigned int m_syt_interval; private: // local port caching for performance struct _MBLA_port_cache { AmdtpAudioPort* port; void* buffer; bool enabled; #ifdef DEBUG unsigned int buffer_size; #endif }; std::vector m_audio_ports; unsigned int m_nb_audio_ports; struct _MIDI_port_cache { AmdtpMidiPort* port; void* buffer; bool enabled; unsigned int position; unsigned int location; #ifdef DEBUG unsigned int buffer_size; #endif }; std::vector m_midi_ports; unsigned int m_nb_midi_ports; /* A small MIDI buffer to cover for the case where we need to span a * period - that is, if more than one MIDI byte is sent per packet. * Since the long-term average data rate must be close to the MIDI spec * (as it's coming from a physical MIDI port_ this buffer doesn't have * to be particularly large. The size is a power of 2 for optimisation * reasons. * * FIXME: it is yet to be determined whether this is required for RME * devices. * * FIXME: copied from RmeReceiveStreamProcessor.h. Needs refactoring */ #define RX_MIDIBUFFER_SIZE_EXP 6 #define RX_MIDIBUFFER_SIZE (1<. * */ #ifndef __FFADO_AMDTPTRANSMITSTREAMPROCESSOR_COMMON__ #define __FFADO_AMDTPTRANSMITSTREAMPROCESSOR_COMMON__ #include "config.h" #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 #define IEC61883_AM824_HAS_LABEL(x, lbl) (((x) & 0xFF000000) == (((quadlet_t)(lbl))<<24)) #endif /* __FFADO_AMDTPTRANSMITSTREAMPROCESSOR_COMMON__ */ libffado-2.4.5/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.cpp0000644000175000001440000012521114206145246025611 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "AmdtpTransmitStreamProcessor.h" #include "AmdtpPort.h" #include "../StreamProcessorManager.h" #include "devicemanager.h" #include "libutil/Time.h" #include "libutil/float_cast.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/IsoHandlerManager.h" #include "libieee1394/cycletimer.h" #include "libutil/ByteSwap.h" #include #include #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) #define AMDTP_FLOAT_MULTIPLIER (1.0f * ((1<<23) - 1)) namespace Streaming { /* transmit */ AmdtpTransmitStreamProcessor::AmdtpTransmitStreamProcessor(FFADODevice &parent, int dimension) : StreamProcessor(parent, ePT_Transmit) , m_dimension( dimension ) , m_dbc( 0 ) #if AMDTP_ALLOW_PAYLOAD_IN_NODATA_XMIT , m_send_nodata_payload ( AMDTP_SEND_PAYLOAD_IN_NODATA_XMIT_BY_DEFAULT ) #endif , m_max_cycles_to_transmit_early ( AMDTP_MAX_CYCLES_TO_TRANSMIT_EARLY ) , m_transmit_transfer_delay ( AMDTP_TRANSMIT_TRANSFER_DELAY ) , m_min_cycles_before_presentation ( AMDTP_MIN_CYCLES_BEFORE_PRESENTATION ) , m_nb_audio_ports( 0 ) , m_nb_midi_ports( 0 ) {} enum StreamProcessor::eChildReturnValue AmdtpTransmitStreamProcessor::generatePacketHeader ( unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr ) { __builtin_prefetch(data, 1, 0); // prefetch events for write, no temporal locality struct iec61883_packet *packet = (struct iec61883_packet *)data; /* Our node ID can change after a bus reset, so it is best to fetch * our node ID for each packet. */ packet->sid = m_local_node_id; packet->eoh0 = 0; packet->dbs = m_dimension; packet->fn = 0; packet->qpc = 0; packet->sph = 0; packet->reserved = 0; packet->dbc = m_dbc; packet->eoh1 = 2; packet->fmt = IEC61883_FMT_AMDTP; *tag = IEC61883_TAG_WITH_CIP; *sy = 0; signed int fc; uint64_t presentation_time; unsigned int presentation_cycle; int cycles_until_presentation; uint64_t transmit_at_time; unsigned int transmit_at_cycle; int cycles_until_transmit; debugOutputExtreme( DEBUG_LEVEL_ULTRA_VERBOSE, "Try for cycle %d\n", (int) CYCLE_TIMER_GET_CYCLES(pkt_ctr) ); // check whether the packet buffer has packets for us to send. // the base timestamp is the one of the next sample in the buffer ffado_timestamp_t ts_head_tmp; m_data_buffer->getBufferHeadTimestamp( &ts_head_tmp, &fc ); // thread safe // the timestamp gives us the time at which we want the sample block // to be output by the device presentation_time = ( uint64_t ) ts_head_tmp; // now we calculate the time when we have to transmit the sample block transmit_at_time = substractTicks( presentation_time, m_transmit_transfer_delay ); // calculate the cycle this block should be presented in // (this is just a virtual calculation since at that time it should // already be in the device's buffer) presentation_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( presentation_time ) ); // calculate the cycle this block should be transmitted in transmit_at_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( transmit_at_time ) ); // we can check whether this cycle is within the 'window' we have // to send this packet. // first calculate the number of cycles left before presentation time cycles_until_presentation = diffCycles ( presentation_cycle, CYCLE_TIMER_GET_CYCLES(pkt_ctr) ); // we can check whether this cycle is within the 'window' we have // to send this packet. // first calculate the number of cycles left before presentation time cycles_until_transmit = diffCycles ( transmit_at_cycle, CYCLE_TIMER_GET_CYCLES(pkt_ctr) ); // two different options: // 1) there are not enough frames for one packet // => determine wether this is a problem, since we might still // have some time to send it // 2) there are enough packets // => determine whether we have to send them in this packet if ( fc < ( signed int ) m_syt_interval ) { // not enough frames in the buffer, // we can still postpone the queueing of the packets // if we are far enough ahead of the presentation time if ( cycles_until_presentation <= m_min_cycles_before_presentation ) { debugOutput( DEBUG_LEVEL_NORMAL, "Insufficient frames (P): N=%02d, CY=%04d, TC=%04u, CUT=%04d\n", fc, (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), transmit_at_cycle, cycles_until_transmit ); // we are too late return eCRV_XRun; } else { #if DEBUG_EXTREME unsigned int now_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( m_1394service.getCycleTimerTicks() ) ); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "Insufficient frames (NP): N=%02d, CY=%04d, TC=%04u, CUT=%04d, NOW=%04d\n", fc, (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), transmit_at_cycle, cycles_until_transmit, now_cycle ); #endif // there is still time left to send the packet // we want the system to give this packet another go at a later time instant return eCRV_Again; // note that the raw1394 again system doesn't work as expected // we could wait here for a certain time before trying again. However, this // is not going to work since we then block the iterator thread, hence also // the receiving code, meaning that we are not processing received packets, // and hence there is no progression in the number of frames available. // for example: // SleepRelativeUsec(125); // one cycle // goto try_block_of_frames; // or more advanced, calculate how many cycles we are ahead of 'now' and // base the sleep on that. // note that this requires that there is one thread for each IsoHandler, // otherwise we're in the deadlock described above. } } else { // there are enough frames, so check the time they are intended for // all frames have a certain 'time window' in which they can be sent // this corresponds to the range of the timestamp mechanism: // we can send a packet 15 cycles in advance of the 'presentation time' // in theory we can send the packet up till one cycle before the presentation time, // however this is not very smart. // There are 3 options: // 1) the frame block is too early // => send an empty packet // 2) the frame block is within the window // => send it // 3) the frame block is too late // => discard (and raise xrun?) // get next block of frames and repeat if(cycles_until_transmit < 0) { // we are too late debugOutput(DEBUG_LEVEL_VERBOSE, "Too late: CY=%04d, TC=%04u, CUT=%04d, TSP=%011" PRIu64 " (%04u)\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), transmit_at_cycle, cycles_until_transmit, presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time) ); //debugShowBackLogLines(200); // however, if we can send this sufficiently before the presentation // time, it could be harmless. // NOTE: dangerous since the device has no way of reporting that it didn't get // this packet on time. if(cycles_until_presentation >= m_min_cycles_before_presentation) { // we are not that late and can still try to transmit the packet m_dbc += fillDataPacketHeader(packet, length, presentation_time); m_last_timestamp = presentation_time; return (fc < (signed)(2*m_syt_interval) ? eCRV_Defer : eCRV_Packet); } else // definitely too late { return eCRV_XRun; } } else if(cycles_until_transmit <= m_max_cycles_to_transmit_early) { // it's time send the packet m_dbc += fillDataPacketHeader(packet, length, presentation_time); m_last_timestamp = presentation_time; // for timestamp tracing debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "XMIT PKT: TSP= %011" PRIu64 " (%04u) (%04u) (%04u)\n", presentation_time, (unsigned int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), presentation_cycle, transmit_at_cycle); return (fc < (signed)(m_syt_interval) ? eCRV_Defer : eCRV_Packet); } else { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "Too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011" PRIu64 " (%04u), TSP=%011" PRId64 " (%04u)\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), transmit_at_cycle, cycles_until_transmit, transmit_at_time, (unsigned int)TICKS_TO_CYCLES(transmit_at_time), presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time)); #ifdef DEBUG if ( cycles_until_transmit > m_max_cycles_to_transmit_early + 1 ) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "Way too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011" PRIu64 " (%04u), TSP=%011" PRId64 "(%04u)\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), transmit_at_cycle, cycles_until_transmit, transmit_at_time, (unsigned int)TICKS_TO_CYCLES(transmit_at_time), presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time)); } #endif // we are too early, send only an empty packet return eCRV_EmptyPacket; } } return eCRV_Invalid; } enum StreamProcessor::eChildReturnValue AmdtpTransmitStreamProcessor::generatePacketData ( unsigned char *data, unsigned int *length ) { if (m_data_buffer->readFrames(m_syt_interval, (char *)(data + 8))) { debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "XMIT DATA: TSP= %011" PRIu64 " (%04u)\n", m_last_timestamp, (unsigned int)TICKS_TO_CYCLES(m_last_timestamp)); #if 0 // debug code to output the packet content char tmpbuff[8192]; int cnt=0; quadlet_t *tmp = (quadlet_t *)((char *)(data + 8)); for(int i=0; isid = m_local_node_id; packet->eoh0 = 0; packet->dbs = m_dimension; packet->fn = 0; packet->qpc = 0; packet->sph = 0; packet->reserved = 0; packet->dbc = m_dbc; packet->eoh1 = 2; packet->fmt = IEC61883_FMT_AMDTP; *tag = IEC61883_TAG_WITH_CIP; *sy = 0; m_dbc += fillNoDataPacketHeader(packet, length); return eCRV_Packet; } enum StreamProcessor::eChildReturnValue AmdtpTransmitStreamProcessor::generateSilentPacketData ( unsigned char *data, unsigned int *length ) { return eCRV_OK; // no need to do anything } enum StreamProcessor::eChildReturnValue AmdtpTransmitStreamProcessor::generateEmptyPacketHeader ( unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr ) { struct iec61883_packet *packet = ( struct iec61883_packet * ) data; debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, "XMIT EMPTY (cy %04d): TSP=%011" PRIu64 " (%04u)\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp, (unsigned int)TICKS_TO_CYCLES(m_last_timestamp) ); packet->sid = m_local_node_id; packet->eoh0 = 0; packet->dbs = m_dimension; packet->fn = 0; packet->qpc = 0; packet->sph = 0; packet->reserved = 0; packet->dbc = m_dbc; packet->eoh1 = 2; packet->fmt = IEC61883_FMT_AMDTP; *tag = IEC61883_TAG_WITH_CIP; *sy = 0; m_dbc += fillNoDataPacketHeader(packet, length); return eCRV_OK; } enum StreamProcessor::eChildReturnValue AmdtpTransmitStreamProcessor::generateEmptyPacketData ( unsigned char *data, unsigned int *length ) { return eCRV_OK; // no need to do anything } unsigned int AmdtpTransmitStreamProcessor::fillDataPacketHeader ( struct iec61883_packet *packet, unsigned int* length, uint32_t ts ) { packet->fdf = m_fdf; // convert the timestamp to SYT format uint16_t timestamp_SYT = TICKS_TO_SYT ( ts ); packet->syt = CondSwapToBus16 ( timestamp_SYT ); // FIXME: use a precomputed value here *length = m_syt_interval*sizeof ( quadlet_t ) *m_dimension + 8; return m_syt_interval; } unsigned int AmdtpTransmitStreamProcessor::fillNoDataPacketHeader ( struct iec61883_packet *packet, unsigned int* length ) { // no-data packets have syt=0xFFFF // and (can) have the usual amount of events as dummy data // DBC is not increased packet->fdf = IEC61883_FDF_NODATA; packet->syt = 0xffff; #if AMDTP_ALLOW_PAYLOAD_IN_NODATA_XMIT if ( m_send_nodata_payload ) { // no-data packets with payload (NOTE: DICE-II doesn't like that) *length = 2*sizeof ( quadlet_t ) + m_syt_interval * m_dimension * sizeof ( quadlet_t ); return m_syt_interval; } else { // no-data packets without payload *length = 2*sizeof ( quadlet_t ); return 0; } #else // no-data packets without payload *length = 2*sizeof ( quadlet_t ); return 0; #endif } unsigned int AmdtpTransmitStreamProcessor::getSytInterval() { switch (m_StreamProcessorManager.getNominalRate()) { case 32000: case 44100: case 48000: return 8; case 88200: case 96000: return 16; case 176400: case 192000: return 32; default: debugError("Unsupported rate: %d\n", m_StreamProcessorManager.getNominalRate()); return 0; } } unsigned int AmdtpTransmitStreamProcessor::getFDF() { switch (m_StreamProcessorManager.getNominalRate()) { case 32000: return IEC61883_FDF_SFC_32KHZ; case 44100: return IEC61883_FDF_SFC_44K1HZ; case 48000: return IEC61883_FDF_SFC_48KHZ; case 88200: return IEC61883_FDF_SFC_88K2HZ; case 96000: return IEC61883_FDF_SFC_96KHZ; case 176400: return IEC61883_FDF_SFC_176K4HZ; case 192000: return IEC61883_FDF_SFC_192KHZ; default: debugError("Unsupported rate: %d\n", m_StreamProcessorManager.getNominalRate()); return 0; } } bool AmdtpTransmitStreamProcessor::prepareChild() { debugOutput ( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this ); m_syt_interval = getSytInterval(); m_fdf = getFDF(); debugOutput ( DEBUG_LEVEL_VERBOSE, " SYT interval / FDF : %d / %d\n", m_syt_interval, m_fdf ); #if AMDTP_ALLOW_PAYLOAD_IN_NODATA_XMIT debugOutput ( DEBUG_LEVEL_VERBOSE, " Send payload in No-Data packets: %s \n", m_send_nodata_payload?"Yes":"No" ); #endif debugOutput ( DEBUG_LEVEL_VERBOSE, " Max early transmit cycles : %d\n", m_max_cycles_to_transmit_early ); debugOutput ( DEBUG_LEVEL_VERBOSE, " Transfer delay : %d\n", m_transmit_transfer_delay ); debugOutput ( DEBUG_LEVEL_VERBOSE, " Min cycles before presentation : %d\n", m_min_cycles_before_presentation ); iec61883_cip_init ( &m_cip_status, IEC61883_FMT_AMDTP, m_fdf, m_StreamProcessorManager.getNominalRate(), m_dimension, m_syt_interval ); if (!initPortCache()) { debugError("Could not init port cache\n"); return false; } return true; } /* * compose the event streams for the packets from the port buffers */ bool AmdtpTransmitStreamProcessor::processWriteBlock ( char *data, unsigned int nevents, unsigned int offset ) { // update the variable parts of the cache updatePortCache(); // encode audio data switch(m_StreamProcessorManager.getAudioDataType()) { case StreamProcessorManager::eADT_Int24: encodeAudioPortsInt24((quadlet_t *)data, offset, nevents); break; case StreamProcessorManager::eADT_Float: encodeAudioPortsFloat((quadlet_t *)data, offset, nevents); break; } // do midi ports encodeMidiPorts((quadlet_t *)data, offset, nevents); return true; } bool AmdtpTransmitStreamProcessor::transmitSilenceBlock( char *data, unsigned int nevents, unsigned int offset) { // no need to update the port cache when transmitting silence since // no dynamic values are used to do so. encodeAudioPortsSilence((quadlet_t *)data, offset, nevents); encodeMidiPortsSilence((quadlet_t *)data, offset, nevents); return true; } /** * @brief encodes all audio ports in the cache to events (silent data) * @param data * @param offset * @param nevents */ void AmdtpTransmitStreamProcessor::encodeAudioPortsSilence(quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j; quadlet_t *target_event; int i; for (i = 0; i < m_nb_audio_ports; i++) { target_event = (quadlet_t *)(data + i); for (j = 0;j < nevents; j += 1) { *target_event = CONDSWAPTOBUS32_CONST(0x40000000); target_event += m_dimension; } } } #ifdef __SSE2__ #include // There's no obvious reason to warn about this anymore - jwoithe. // #warning SSE2 build /** * @brief mux all audio ports to events * @param data * @param offset * @param nevents */ void AmdtpTransmitStreamProcessor::encodeAudioPortsFloat(quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j; quadlet_t *target_event; int i; float * client_buffers[4]; float tmp_values[4] __attribute__ ((aligned (16))); uint32_t tmp_values_int[4] __attribute__ ((aligned (16))); // prepare the scratch buffer assert(m_scratch_buffer_size_bytes > nevents * 4); memset(m_scratch_buffer, 0, nevents * 4); const __m128i label = _mm_set_epi32 (0x40000000, 0x40000000, 0x40000000, 0x40000000); const __m128i mask = _mm_set_epi32 (0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF); const __m128 mult = _mm_set_ps(AMDTP_FLOAT_MULTIPLIER, AMDTP_FLOAT_MULTIPLIER, AMDTP_FLOAT_MULTIPLIER, AMDTP_FLOAT_MULTIPLIER); #if AMDTP_CLIP_FLOATS const __m128 v_max = _mm_set_ps(1.0, 1.0, 1.0, 1.0); const __m128 v_min = _mm_set_ps(-1.0, -1.0, -1.0, -1.0); #endif // this assumes that audio ports are sorted by position, // and that there are no gaps for (i = 0; i < ((int)m_nb_audio_ports)-4; i += 4) { struct _MBLA_port_cache *p; // get the port buffers for (j=0; j<4; j++) { p = &(m_audio_ports.at(i+j)); if(likely(p->buffer && p->enabled)) { client_buffers[j] = (float *) p->buffer; client_buffers[j] += offset; } else { // if a port is disabled or has no valid // buffer, use the scratch buffer (all zero's) client_buffers[j] = (float *) m_scratch_buffer; } } // the base event for this position target_event = (quadlet_t *)(data + i); // process the events for (j=0;j < nevents; j += 1) { // read the values tmp_values[0] = *(client_buffers[0]); tmp_values[1] = *(client_buffers[1]); tmp_values[2] = *(client_buffers[2]); tmp_values[3] = *(client_buffers[3]); // now do the SSE based conversion/labeling __m128 v_float = *((__m128*)tmp_values); __m128i *target = (__m128i*)target_event; __m128i v_int; // clip #if AMDTP_CLIP_FLOATS // do SSE clipping v_float = _mm_max_ps(v_float, v_min); v_float = _mm_min_ps(v_float, v_max); #endif // multiply v_float = _mm_mul_ps(v_float, mult); // convert to signed integer v_int = _mm_cvttps_epi32( v_float ); // mask v_int = _mm_and_si128( v_int, mask ); // label it v_int = _mm_or_si128( v_int, label ); // do endian conversion (SSE is always little endian) // do first swap v_int = _mm_or_si128( _mm_slli_epi16( v_int, 8 ), _mm_srli_epi16( v_int, 8 ) ); // do second swap v_int = _mm_or_si128( _mm_slli_epi32( v_int, 16 ), _mm_srli_epi32( v_int, 16 ) ); // store the packed int // (target misalignment is assumed since we don't know the m_dimension) _mm_storeu_si128 (target, v_int); // increment the buffer pointers client_buffers[0]++; client_buffers[1]++; client_buffers[2]++; client_buffers[3]++; // go to next target event position target_event += m_dimension; } } // do remaining ports // NOTE: these can be time-SSE'd for (; i < (int)m_nb_audio_ports; i++) { struct _MBLA_port_cache &p = m_audio_ports.at(i); target_event = (quadlet_t *)(data + i); #ifdef DEBUG assert(nevents + offset <= p.buffer_size ); #endif if(likely(p.buffer && p.enabled)) { float *buffer = (float *)(p.buffer); buffer += offset; for (j = 0;j < nevents; j += 4) { // read the values tmp_values[0] = *buffer; buffer++; tmp_values[1] = *buffer; buffer++; tmp_values[2] = *buffer; buffer++; tmp_values[3] = *buffer; buffer++; // now do the SSE based conversion/labeling __m128 v_float = *((__m128*)tmp_values); __m128i v_int; #if AMDTP_CLIP_FLOATS // do SSE clipping v_float = _mm_max_ps(v_float, v_min); v_float = _mm_min_ps(v_float, v_max); #endif // multiply v_float = _mm_mul_ps(v_float, mult); // convert to signed integer v_int = _mm_cvttps_epi32( v_float ); // mask v_int = _mm_and_si128( v_int, mask ); // label it v_int = _mm_or_si128( v_int, label ); // do endian conversion (SSE is always little endian) // do first swap v_int = _mm_or_si128( _mm_slli_epi16( v_int, 8 ), _mm_srli_epi16( v_int, 8 ) ); // do second swap v_int = _mm_or_si128( _mm_slli_epi32( v_int, 16 ), _mm_srli_epi32( v_int, 16 ) ); // store the packed int _mm_store_si128 ((__m128i *)(&tmp_values_int), v_int); // increment the buffer pointers *target_event = tmp_values_int[0]; target_event += m_dimension; *target_event = tmp_values_int[1]; target_event += m_dimension; *target_event = tmp_values_int[2]; target_event += m_dimension; *target_event = tmp_values_int[3]; target_event += m_dimension; } // do the remainder of the events for(;j < nevents; j += 1) { float *in = (float *)buffer; #if AMDTP_CLIP_FLOATS // clip directly to the value of a maxed event if(unlikely(*in > 1.0)) { *target_event = CONDSWAPTOBUS32_CONST(0x407FFFFF); } else if(unlikely(*in < -1.0)) { *target_event = CONDSWAPTOBUS32_CONST(0x40800001); } else { float v = (*in) * AMDTP_FLOAT_MULTIPLIER; unsigned int tmp = ((int) v); tmp = ( tmp & 0x00FFFFFF ) | 0x40000000; *target_event = CondSwapToBus32((quadlet_t)tmp); } #else float v = (*in) * AMDTP_FLOAT_MULTIPLIER; unsigned int tmp = ((int) v); tmp = ( tmp & 0x00FFFFFF ) | 0x40000000; *target_event = CondSwapToBus32((quadlet_t)tmp); #endif buffer++; target_event += m_dimension; } } else { for (j = 0;j < nevents; j += 1) { // hardcoded byte swapped *target_event = 0x00000040; target_event += m_dimension; } } } } /** * @brief mux all audio ports to events * @param data * @param offset * @param nevents */ void AmdtpTransmitStreamProcessor::encodeAudioPortsInt24(quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j; quadlet_t *target_event; int i; uint32_t *client_buffers[4]; uint32_t tmp_values[4] __attribute__ ((aligned (16))); // prepare the scratch buffer assert(m_scratch_buffer_size_bytes > nevents * 4); memset(m_scratch_buffer, 0, nevents * 4); const __m128i label = _mm_set_epi32 (0x40000000, 0x40000000, 0x40000000, 0x40000000); const __m128i mask = _mm_set_epi32 (0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF); // this assumes that audio ports are sorted by position, // and that there are no gaps for (i = 0; i < ((int)m_nb_audio_ports)-4; i += 4) { struct _MBLA_port_cache *p; // get the port buffers for (j=0; j<4; j++) { p = &(m_audio_ports.at(i+j)); if(likely(p->buffer && p->enabled)) { client_buffers[j] = (uint32_t *) p->buffer; client_buffers[j] += offset; } else { // if a port is disabled or has no valid // buffer, use the scratch buffer (all zero's) client_buffers[j] = (uint32_t *) m_scratch_buffer; } } // the base event for this position target_event = (quadlet_t *)(data + i); // process the events for (j=0;j < nevents; j += 1) { // read the values tmp_values[0] = *(client_buffers[0]); tmp_values[1] = *(client_buffers[1]); tmp_values[2] = *(client_buffers[2]); tmp_values[3] = *(client_buffers[3]); // now do the SSE based conversion/labeling __m128i *target = (__m128i*)target_event; __m128i v_int = *((__m128i*)tmp_values);; // mask v_int = _mm_and_si128( v_int, mask ); // label it v_int = _mm_or_si128( v_int, label ); // do endian conversion (SSE is always little endian) // do first swap v_int = _mm_or_si128( _mm_slli_epi16( v_int, 8 ), _mm_srli_epi16( v_int, 8 ) ); // do second swap v_int = _mm_or_si128( _mm_slli_epi32( v_int, 16 ), _mm_srli_epi32( v_int, 16 ) ); // store the packed int // (target misalignment is assumed since we don't know the m_dimension) _mm_storeu_si128 (target, v_int); // increment the buffer pointers client_buffers[0]++; client_buffers[1]++; client_buffers[2]++; client_buffers[3]++; // go to next target event position target_event += m_dimension; } } // do remaining ports // NOTE: these can be time-SSE'd for (; i < ((int)m_nb_audio_ports); i++) { struct _MBLA_port_cache &p = m_audio_ports.at(i); target_event = (quadlet_t *)(data + i); #ifdef DEBUG assert(nevents + offset <= p.buffer_size ); #endif if(likely(p.buffer && p.enabled)) { uint32_t *buffer = (uint32_t *)(p.buffer); buffer += offset; for (j = 0;j < nevents; j += 4) { // read the values tmp_values[0] = *buffer; buffer++; tmp_values[1] = *buffer; buffer++; tmp_values[2] = *buffer; buffer++; tmp_values[3] = *buffer; buffer++; // now do the SSE based conversion/labeling __m128i v_int = *((__m128i*)tmp_values);; // mask v_int = _mm_and_si128( v_int, mask ); // label it v_int = _mm_or_si128( v_int, label ); // do endian conversion (SSE is always little endian) // do first swap v_int = _mm_or_si128( _mm_slli_epi16( v_int, 8 ), _mm_srli_epi16( v_int, 8 ) ); // do second swap v_int = _mm_or_si128( _mm_slli_epi32( v_int, 16 ), _mm_srli_epi32( v_int, 16 ) ); // store the packed int _mm_store_si128 ((__m128i *)(&tmp_values), v_int); // increment the buffer pointers *target_event = tmp_values[0]; target_event += m_dimension; *target_event = tmp_values[1]; target_event += m_dimension; *target_event = tmp_values[2]; target_event += m_dimension; *target_event = tmp_values[3]; target_event += m_dimension; } // do the remainder of the events for(;j < nevents; j += 1) { uint32_t in = (uint32_t)(*buffer); *target_event = CondSwapToBus32((quadlet_t)((in & 0x00FFFFFF) | 0x40000000)); buffer++; target_event += m_dimension; } } else { for (j = 0;j < nevents; j += 1) { // hardcoded byte swapped *target_event = 0x00000040; target_event += m_dimension; } } } } #else /** * @brief mux all audio ports to events * @param data * @param offset * @param nevents */ void AmdtpTransmitStreamProcessor::encodeAudioPortsInt24(quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j; quadlet_t *target_event; int i; for (i = 0; i < m_nb_audio_ports; i++) { struct _MBLA_port_cache &p = m_audio_ports.at(i); target_event = (quadlet_t *)(data + i); #ifdef DEBUG assert(nevents + offset <= p.buffer_size ); #endif if(likely(p.buffer && p.enabled)) { quadlet_t *buffer = (quadlet_t *)(p.buffer); buffer += offset; for (j = 0;j < nevents; j += 1) { uint32_t in = (uint32_t)(*buffer); *target_event = CondSwapToBus32((quadlet_t)((in & 0x00FFFFFF) | 0x40000000)); buffer++; target_event += m_dimension; } } else { for (j = 0;j < nevents; j += 1) { *target_event = CONDSWAPTOBUS32_CONST(0x40000000); target_event += m_dimension; } } } } /** * @brief mux all audio ports to events * @param data * @param offset * @param nevents */ void AmdtpTransmitStreamProcessor::encodeAudioPortsFloat(quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j; quadlet_t *target_event; int i; for (i = 0; i < m_nb_audio_ports; i++) { struct _MBLA_port_cache &p = m_audio_ports.at(i); target_event = (quadlet_t *)(data + i); #ifdef DEBUG assert(nevents + offset <= p.buffer_size ); #endif if(likely(p.buffer && p.enabled)) { quadlet_t *buffer = (quadlet_t *)(p.buffer); buffer += offset; for (j = 0;j < nevents; j += 1) { float *in = (float *)buffer; #if AMDTP_CLIP_FLOATS // clip directly to the value of a maxed event if(unlikely(*in > 1.0)) { *target_event = CONDSWAPTOBUS32_CONST(0x407FFFFF); } else if(unlikely(*in < -1.0)) { *target_event = CONDSWAPTOBUS32_CONST(0x40800001); } else { float v = (*in) * AMDTP_FLOAT_MULTIPLIER; unsigned int tmp = ((int) v); tmp = ( tmp & 0x00FFFFFF ) | 0x40000000; *target_event = CondSwapToBus32((quadlet_t)tmp); } #else float v = (*in) * AMDTP_FLOAT_MULTIPLIER; unsigned int tmp = ((int) v); tmp = ( tmp & 0x00FFFFFF ) | 0x40000000; *target_event = CondSwapToBus32((quadlet_t)tmp); #endif buffer++; target_event += m_dimension; } } else { for (j = 0;j < nevents; j += 1) { *target_event = CONDSWAPTOBUS32_CONST(0x40000000); target_event += m_dimension; } } } } #endif /** * @brief encodes all midi ports in the cache to events (silence) * @param data * @param offset * @param nevents */ void AmdtpTransmitStreamProcessor::encodeMidiPortsSilence(quadlet_t *data, unsigned int offset, unsigned int nevents) { quadlet_t *target_event; int i; unsigned int j; for (i = 0; i < m_nb_midi_ports; i++) { struct _MIDI_port_cache &p = m_midi_ports.at(i); for (j = p.location;j < nevents; j += 8) { target_event = (quadlet_t *) (data + ((j * m_dimension) + p.position)); *target_event = CondSwapToBus32(IEC61883_AM824_SET_LABEL(0, IEC61883_AM824_LABEL_MIDI_NO_DATA)); } } } /** * @brief encodes all midi ports in the cache to events * @param data * @param offset * @param nevents */ void AmdtpTransmitStreamProcessor::encodeMidiPorts(quadlet_t *data, unsigned int offset, unsigned int nevents) { quadlet_t *target_event; int i; unsigned int j; for (i = 0; i < m_nb_midi_ports; i++) { struct _MIDI_port_cache &p = m_midi_ports.at(i); if (p.buffer && p.enabled) { uint32_t *buffer = (quadlet_t *)(p.buffer); buffer += offset; for (j = p.location;j < nevents; j += 8) { target_event = (quadlet_t *) (data + ((j * m_dimension) + p.position)); if ( *buffer & 0xFF000000 ) // we can send a byte { quadlet_t tmpval; tmpval = ((*buffer)<<16) & 0x00FF0000; tmpval = IEC61883_AM824_SET_LABEL(tmpval, IEC61883_AM824_LABEL_MIDI_1X); *target_event = CondSwapToBus32(tmpval); debugOutputExtreme( DEBUG_LEVEL_VERBOSE, "MIDI port %s, pos=%u, loc=%u, nevents=%u, dim=%d\n", p.port->getName().c_str(), p.position, p.location, nevents, m_dimension ); debugOutputExtreme( DEBUG_LEVEL_VERBOSE, "base=%p, target=%p, value=%08X\n", data, target_event, tmpval ); } else { // can't send a byte, either because there is no byte, // or because this would exceed the maximum rate // FIXME: this can be ifdef optimized since it's a constant *target_event = CondSwapToBus32(IEC61883_AM824_SET_LABEL(0, IEC61883_AM824_LABEL_MIDI_NO_DATA)); } buffer+=8; } } else { for (j = p.location;j < nevents; j += 8) { target_event = (quadlet_t *)(data + ((j * m_dimension) + p.position)); __builtin_prefetch(target_event, 1, 0); // prefetch events for write, no temporal locality *target_event = CondSwapToBus32(IEC61883_AM824_SET_LABEL(0, IEC61883_AM824_LABEL_MIDI_NO_DATA)); } } } } bool AmdtpTransmitStreamProcessor::initPortCache() { // make use of the fact that audio ports are the first ports in // the cluster as per AMDTP. so we can sort the ports by position // and have very efficient lookups: // m_float_ports.at(i).buffer -> audio stream i buffer // for midi ports we simply cache all port info since they are (usually) not // that numerous m_nb_audio_ports = 0; m_audio_ports.clear(); m_nb_midi_ports = 0; m_midi_ports.clear(); for(PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { AmdtpPortInfo *pinfo=dynamic_cast(*it); assert(pinfo); // this should not fail!! switch( pinfo->getFormat() ) { case AmdtpPortInfo::E_MBLA: m_nb_audio_ports++; break; case AmdtpPortInfo::E_SPDIF: // still unimplemented break; case AmdtpPortInfo::E_Midi: m_nb_midi_ports++; break; default: // ignore break; } } int idx; for (idx = 0; idx < m_nb_audio_ports; idx++) { for(PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { AmdtpPortInfo *pinfo=dynamic_cast(*it); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "idx %u: looking at port %s at position %u\n", idx, (*it)->getName().c_str(), pinfo->getPosition()); if(pinfo->getPosition() == (unsigned int)idx) { struct _MBLA_port_cache p; p.port = dynamic_cast(*it); if(p.port == NULL) { debugError("Port is not an AmdtpAudioPort!\n"); return false; } p.buffer = NULL; // to be filled by updatePortCache #ifdef DEBUG p.buffer_size = (*it)->getBufferSize(); #endif m_audio_ports.push_back(p); debugOutput(DEBUG_LEVEL_VERBOSE, "Cached port %s at position %u\n", p.port->getName().c_str(), idx); goto next_index; } } debugError("No MBLA port found for position %d\n", idx); return false; next_index: continue; } for(PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { AmdtpPortInfo *pinfo=dynamic_cast(*it); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "idx %u: looking at port %s at position %u, location %u\n", idx, (*it)->getName().c_str(), pinfo->getPosition(), pinfo->getLocation()); if ((*it)->getPortType() == Port::E_Midi) { struct _MIDI_port_cache p; p.port = dynamic_cast(*it); if(p.port == NULL) { debugError("Port is not an AmdtpMidiPort!\n"); return false; } p.position = pinfo->getPosition(); p.location = pinfo->getLocation(); p.buffer = NULL; // to be filled by updatePortCache #ifdef DEBUG p.buffer_size = (*it)->getBufferSize(); #endif m_midi_ports.push_back(p); debugOutput(DEBUG_LEVEL_VERBOSE, "Cached port %s at position %u, location %u\n", p.port->getName().c_str(), p.position, p.location); } } return true; } //FIXME: DRY. Needs to be refactored with AmdtpReceiveStreamProcessor void AmdtpTransmitStreamProcessor::updatePortCache() { int idx; for (idx = 0; idx < m_nb_audio_ports; idx++) { struct _MBLA_port_cache& p = m_audio_ports.at(idx); AmdtpAudioPort *port = p.port; p.buffer = port->getBufferAddress(); p.enabled = !port->isDisabled(); #ifdef DEBUG p.buffer_size = port->getBufferSize(); #endif } for (idx = 0; idx < m_nb_midi_ports; idx++) { struct _MIDI_port_cache& p = m_midi_ports.at(idx); AmdtpMidiPort *port = p.port; p.buffer = port->getBufferAddress(); p.enabled = !port->isDisabled(); #ifdef DEBUG p.buffer_size = port->getBufferSize(); #endif } } } // end of namespace Streaming libffado-2.4.5/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.h0000644000175000001440000001414414206145246025260 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_AMDTPTRANSMITSTREAMPROCESSOR__ #define __FFADO_AMDTPTRANSMITSTREAMPROCESSOR__ /** * This class implements IEC61883-6 / AM824 / AMDTP based streaming */ #include "config.h" #include "AmdtpStreamProcessor-common.h" namespace Streaming { class Port; class AmdtpAudioPort; class AmdtpMidiPort; /*! \brief The Base Class for an AMDTP transmit stream processor This class implements a TransmitStreamProcessor that multiplexes Ports into AMDTP streams. */ class AmdtpTransmitStreamProcessor : public StreamProcessor { public: /** * Create a AMDTP transmit StreamProcessor * @param port 1394 port * @param framerate frame rate * @param dimension number of substreams in the ISO stream * (midi-muxed is only one stream) */ AmdtpTransmitStreamProcessor(FFADODevice &parent, int dimension); virtual ~AmdtpTransmitStreamProcessor() {}; enum eChildReturnValue generatePacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr); enum eChildReturnValue generatePacketData(unsigned char *data, unsigned int *length); enum eChildReturnValue generateEmptyPacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr); enum eChildReturnValue generateEmptyPacketData(unsigned char *data, unsigned int *length); enum eChildReturnValue generateSilentPacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr); enum eChildReturnValue generateSilentPacketData(unsigned char *data, unsigned int *length); virtual bool prepareChild(); #if AMDTP_ALLOW_PAYLOAD_IN_NODATA_XMIT public: void sendPayloadForNoDataPackets(bool b) {m_send_nodata_payload = b;}; #endif public: virtual unsigned int getEventSize() {return 4;}; virtual unsigned int getMaxPacketSize() {return 4 * (2 + getSytInterval() * m_dimension);}; virtual unsigned int getEventsPerFrame() { return m_dimension; }; virtual unsigned int getNominalFramesPerPacket() {return getSytInterval();}; // transmit control parameters virtual int getMaxCyclesToTransmitEarly() {return m_max_cycles_to_transmit_early;}; virtual void setMaxCyclesToTransmitEarly(int x) {m_max_cycles_to_transmit_early = x;}; virtual unsigned int getTransferDelay() {return m_transmit_transfer_delay;}; virtual void setTransferDelay(unsigned int x) {m_transmit_transfer_delay = x;}; virtual int getMinCyclesBeforePresentation() {return m_min_cycles_before_presentation;}; virtual void setMinCyclesBeforePresentation(int x) {m_min_cycles_before_presentation = x;}; protected: bool processWriteBlock(char *data, unsigned int nevents, unsigned int offset); bool transmitSilenceBlock(char *data, unsigned int nevents, unsigned int offset); private: unsigned int fillNoDataPacketHeader(struct iec61883_packet *packet, unsigned int* length); unsigned int fillDataPacketHeader(struct iec61883_packet *packet, unsigned int* length, uint32_t ts); int transmitBlock(char *data, unsigned int nevents, unsigned int offset); void encodeAudioPortsSilence(quadlet_t *data, unsigned int offset, unsigned int nevents); void encodeAudioPortsFloat(quadlet_t *data, unsigned int offset, unsigned int nevents); void encodeAudioPortsInt24(quadlet_t *data, unsigned int offset, unsigned int nevents); void encodeMidiPortsSilence(quadlet_t *data, unsigned int offset, unsigned int nevents); void encodeMidiPorts(quadlet_t *data, unsigned int offset, unsigned int nevents); unsigned int getFDF(); unsigned int getSytInterval(); struct iec61883_cip m_cip_status; int m_dimension; unsigned int m_syt_interval; int m_fdf; unsigned int m_dbc; #if AMDTP_ALLOW_PAYLOAD_IN_NODATA_XMIT private: bool m_send_nodata_payload; #endif int m_max_cycles_to_transmit_early; unsigned int m_transmit_transfer_delay; int m_min_cycles_before_presentation; private: // local port caching for performance struct _MBLA_port_cache { AmdtpAudioPort* port; void* buffer; bool enabled; #ifdef DEBUG unsigned int buffer_size; #endif }; std::vector m_audio_ports; int m_nb_audio_ports; struct _MIDI_port_cache { AmdtpMidiPort* port; void* buffer; bool enabled; unsigned int position; unsigned int location; #ifdef DEBUG unsigned int buffer_size; #endif }; std::vector m_midi_ports; int m_nb_midi_ports; bool initPortCache(); void updatePortCache(); }; } // end of namespace Streaming #endif /* __FFADO_AMDTPTRANSMITSTREAMPROCESSOR__ */ libffado-2.4.5/src/libstreaming/amdtp-oxford/0000755000175000001440000000000014206145612020534 5ustar jwoitheuserslibffado-2.4.5/src/libstreaming/amdtp-oxford/AmdtpOxfordReceiveStreamProcessor.cpp0000644000175000001440000002437514206145246030064 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "AmdtpOxfordReceiveStreamProcessor.h" #include "../StreamProcessorManager.h" #include "devicemanager.h" #include "libieee1394/cycletimer.h" #define DLL_PI (3.141592653589793238) #define DLL_SQRT2 (1.414213562373095049) #define DLL_2PI (2.0 * DLL_PI) // DLL bandwidth in Hz #define DLL_BANDWIDTH_HZ 1.0 namespace Streaming { /* --------------------- RECEIVE ----------------------- */ /* The issues with the FCA202 (and possibly other oxford FW92x devices) are: * - they transmit in non-blocking mode * - the timestamps are not correct * * This requires some workarounds. * * The approach is to 'convert' non-blocking into blocking so * that we can use the existing streaming system. * * The streamprocessor has a "pseudo-packet" buffer that will collect * all frames present. Once one SYT_INTERVAL of frames is present, we indicate * that a packet has been received. * * To overcome the timestamping issue we use the time-of-arrival as a timestamp. * */ AmdtpOxfordReceiveStreamProcessor::AmdtpOxfordReceiveStreamProcessor(FFADODevice &parent, int dimension) : AmdtpReceiveStreamProcessor(parent, dimension) , m_next_packet_timestamp ( 0xFFFFFFFF ) , m_temp_buffer( NULL ) , m_packet_size_bytes( 0 ) , m_payload_buffer( NULL ) , m_dll_e2 ( 0 ) , m_dll_b ( 0 ) , m_dll_c ( 0 ) , m_expected_time_of_receive ( 0xFFFFFFFF ) , m_nominal_ticks_per_frame( 0 ) {} AmdtpOxfordReceiveStreamProcessor::~AmdtpOxfordReceiveStreamProcessor() { if(m_temp_buffer) ffado_ringbuffer_free(m_temp_buffer); if(m_payload_buffer) free(m_payload_buffer); } bool AmdtpOxfordReceiveStreamProcessor::prepareChild() { debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this); int packet_payload_size_events = m_dimension * getSytInterval(); // allocate space for four packets payload. The issue is that it can be // that we receive a bit too much payload such that the total is more // than one packet // // adi@2011-1-14: Holger Dehnhardt says that using 4*4*2 instead of 4*4 // makes his Mackie Onyx work FFADO_ASSERT( m_temp_buffer == NULL ); if( !(m_temp_buffer = ffado_ringbuffer_create( packet_payload_size_events * 4 * 4 * 2))) { debugFatal("Could not allocate memory event ringbuffer\n"); return false; } m_next_packet_timestamp = 0xFFFFFFFF; m_packet_size_bytes = getSytInterval() * m_dimension * sizeof(quadlet_t); m_payload_buffer = (char *)malloc(m_packet_size_bytes); if(m_payload_buffer == NULL) { debugFatal("could not allocate memory for payload buffer\n"); return false; } // init the DLL unsigned int nominal_frames_per_second = m_StreamProcessorManager.getNominalRate(); m_nominal_ticks_per_frame = (double)TICKS_PER_SECOND / (double)nominal_frames_per_second; m_dll_e2 = m_nominal_ticks_per_frame * (double)getSytInterval(); double tupdate = m_nominal_ticks_per_frame * (double)getSytInterval(); double bw_rel = DLL_BANDWIDTH_HZ / (double)TICKS_PER_SECOND * tupdate; if(bw_rel >= 0.5) { debugError("Requested bandwidth out of range: %f > %f\n", DLL_BANDWIDTH_HZ / (double)TICKS_PER_SECOND, 0.5 / tupdate); return false; } m_dll_b = bw_rel * (DLL_SQRT2 * DLL_2PI); m_dll_c = bw_rel * bw_rel * DLL_2PI * DLL_2PI; return AmdtpReceiveStreamProcessor::prepareChild(); } /** * Processes packet header to extract timestamps and so on * * this will be abused to copy the payload into the temporary buffer * once the buffer is full, we trigger a data-read operation * * @param data * @param length * @param channel * @param tag * @param sy * @param pkt_ctr CTR value when packet was received * @return */ enum StreamProcessor::eChildReturnValue AmdtpOxfordReceiveStreamProcessor::processPacketHeader(unsigned char *data, unsigned int length, unsigned char tag, unsigned char sy, uint32_t pkt_ctr) { struct iec61883_packet *packet = (struct iec61883_packet *) data; FFADO_ASSERT(packet); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Packet at %03lu %04lu %04lu\n", CYCLE_TIMER_GET_SECS(pkt_ctr), CYCLE_TIMER_GET_CYCLES(pkt_ctr), CYCLE_TIMER_GET_OFFSET(pkt_ctr)); bool ok = (packet->fdf != 0xFF) && (packet->fmt == 0x10) && (packet->dbs > 0) && (length >= 2*sizeof(quadlet_t)); if(ok) { // there is payload debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Packet with payload\n"); unsigned int frames_in_tempbuffer = ffado_ringbuffer_read_space(m_temp_buffer) / sizeof (quadlet_t) / m_dimension; // if the next packet tsp isn't present, generate one now if (m_next_packet_timestamp == 0xFFFFFFFF) { uint64_t toa = CYCLE_TIMER_TO_TICKS(pkt_ctr); // add some extra time, to ensure causality // in a normal system, the SYT's are max 2 cycles apart toa = addTicks(toa, 2*TICKS_PER_CYCLE); // correct for the frames already in the buffer toa = substractTicks(toa, (uint64_t)(m_nominal_ticks_per_frame * frames_in_tempbuffer)); // toa now contains the CTR in ticks of the first frame in the buffer // if we calculate it from the time-of-arrival of the current packet // init if required if (m_expected_time_of_receive >= 0xFFFFFFFE) { m_expected_time_of_receive = substractTicks(toa, (uint64_t)m_dll_e2); } // time-of-arrival as timestamp is very jittery, filter this a bit double err = diffTicks(toa, m_expected_time_of_receive); // if err is too large, reinitialize as this is most likely a discontinuity in // the streams (e.g. packet lost, xrun) if (err > m_dll_e2 * 2.0 || err < -m_dll_e2 * 2.0 ) { err = 0.0; m_expected_time_of_receive = toa; } m_next_packet_timestamp = m_expected_time_of_receive; double corr = (m_dll_b * err + m_dll_e2); if (corr > 0) { m_expected_time_of_receive = addTicks(m_expected_time_of_receive, (uint64_t)corr); } else { m_expected_time_of_receive = substractTicks(m_expected_time_of_receive, (uint64_t)(-corr)); } m_dll_e2 += m_dll_c * err; debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Generated TSP: %16" PRIu64 " %" PRId64 " %d %d\n", m_next_packet_timestamp, m_next_packet_timestamp-m_last_timestamp, (int)frames_in_tempbuffer, (int)(((length / sizeof (quadlet_t)) - 2) / m_dimension)); } // add the payload to the temporary ringbuffer quadlet_t *payload_start = (quadlet_t *)(data + 8); FFADO_ASSERT(m_dimension == packet->dbs); unsigned int nevents = ((length / sizeof (quadlet_t)) - 2) / m_dimension; unsigned int write_size = nevents * sizeof(quadlet_t) * m_dimension; debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Payload: %u events, going to write %u bytes\n", nevents, write_size); unsigned int written = 0; if ((written = ffado_ringbuffer_write(m_temp_buffer, (char *)payload_start, write_size)) < write_size) { debugFatal("Temporary ringbuffer full (wrote %u bytes of %u)\n", written, write_size); return eCRV_Error; } // now figure out if we have sufficient frames in the tempbuffer to construct a packet unsigned int quadlets_in_tempbuffer = frames_in_tempbuffer * sizeof (quadlet_t); if (quadlets_in_tempbuffer >= m_syt_interval * m_dimension) { // yes debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Sufficient frames in buffer: %u (need %u)\n", quadlets_in_tempbuffer/m_dimension, m_syt_interval); m_last_timestamp = m_next_packet_timestamp; m_next_packet_timestamp = 0xFFFFFFFF; // next cycle should generate a new timestamp // read the 'packet' into the packet payload buffer ffado_ringbuffer_read(m_temp_buffer, m_payload_buffer, m_packet_size_bytes); // signal a packet reception return eCRV_OK; } else { // no debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Insufficient frames in buffer: %u (need %u)\n", quadlets_in_tempbuffer/m_dimension, m_syt_interval); return eCRV_Invalid; } } else { return eCRV_Invalid; } } /** * extract the data from the packet * * instead of using the data from the packet, we use the data from our 'internal' packet * * @pre the IEC61883 packet is valid according to isValidPacket * @param data * @param length * @param channel * @param tag * @param sy * @param pkt_ctr * @return */ enum StreamProcessor::eChildReturnValue AmdtpOxfordReceiveStreamProcessor::processPacketData(unsigned char *data, unsigned int length) { struct iec61883_packet *packet = (struct iec61883_packet *) data; assert(packet); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Processing data\n"); // put whatever is in the payload buffer at the moment into the timestamped buffer if(m_data_buffer->writeFrames(m_syt_interval, m_payload_buffer, m_last_timestamp)) { return eCRV_OK; } else { return eCRV_XRun; } } } // end of namespace Streaming libffado-2.4.5/src/libstreaming/amdtp-oxford/AmdtpOxfordReceiveStreamProcessor.h0000644000175000001440000000526514206145246027526 0ustar jwoitheusers/* * Copyright (C) 2005-2009 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_AMDTPOXFORDRECEIVESTREAMPROCESSOR__ #define __FFADO_AMDTPOXFORDRECEIVESTREAMPROCESSOR__ /** * This class implements IEC61883-6 / AM824 / AMDTP based streaming * * but with all quircks required to make the oxford FW-92x devices working */ #include "debugmodule/debugmodule.h" #include "../amdtp/AmdtpReceiveStreamProcessor.h" #include "libutil/ringbuffer.h" namespace Streaming { /*! \brief The Base Class for an Oxford AMDTP receive stream processor This class implements a ReceiveStreamProcessor that demultiplexes AMDTP streams into Ports. (Oxford style) */ class AmdtpOxfordReceiveStreamProcessor : public AmdtpReceiveStreamProcessor { public: /** * Create a Oxford AMDTP receive StreamProcessor * @param port 1394 port * @param dimension number of substreams in the ISO stream * (midi-muxed is only one stream) */ AmdtpOxfordReceiveStreamProcessor(FFADODevice &parent, int dimension); virtual ~AmdtpOxfordReceiveStreamProcessor(); virtual enum eChildReturnValue processPacketHeader(unsigned char *data, unsigned int length, unsigned char tag, unsigned char sy, uint32_t pkt_ctr); virtual enum eChildReturnValue processPacketData(unsigned char *data, unsigned int length); virtual bool prepareChild(); protected: // packet start timestamp uint64_t m_next_packet_timestamp; // this contains the payload data ffado_ringbuffer_t *m_temp_buffer; unsigned int m_packet_size_bytes; char *m_payload_buffer; // dll stuff double m_dll_e2; float m_dll_b; float m_dll_c; uint64_t m_expected_time_of_receive; float m_nominal_ticks_per_frame; }; } // end of namespace Streaming #endif /* __FFADO_AMDTPOXFORDRECEIVESTREAMPROCESSOR__ */ libffado-2.4.5/src/libstreaming/digidesign/0000755000175000001440000000000014206145612020236 5ustar jwoitheuserslibffado-2.4.5/src/libstreaming/digidesign/DigidesignPort.cpp0000644000175000001440000000173714206145246023670 0ustar jwoitheusers/* * Copyright (C) 2005-2008, 2011 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "DigidesignPort.h" #include namespace Streaming { } // end of namespace Streaming libffado-2.4.5/src/libstreaming/digidesign/DigidesignPort.h0000644000175000001440000000427314206145246023333 0ustar jwoitheusers/* * Copyright (C) 2005-2008, 2011 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_DIGIDESIGNPORT__ #define __FFADO_DIGIDESIGNPORT__ /** * This file implements the ports used in Digidesign devices */ #include "DigidesignPortInfo.h" #include "../generic/Port.h" #include "debugmodule/debugmodule.h" namespace Streaming { /*! \brief The Base Class for Digidesignu Audio Port */ class DigidesignAudioPort : public AudioPort, public DigidesignPortInfo { public: DigidesignAudioPort(PortManager &m, std::string name, enum E_Direction direction, int position, int size) : AudioPort(m, name, direction), DigidesignPortInfo( position, size) // TODO: add more port information parameters here if nescessary {}; virtual ~DigidesignAudioPort() {}; }; /*! \brief The Base Class for a Digidesign Midi Port */ class DigidesignMidiPort : public MidiPort, public DigidesignPortInfo { public: DigidesignMidiPort(PortManager &m, std::string name, enum E_Direction direction, int position) : MidiPort(m, name, direction), DigidesignPortInfo(position, 0) // TODO: add more port information parameters here if nescessary {}; virtual ~DigidesignMidiPort() {}; }; } // end of namespace Streaming #endif /* __FFADO_DIGIDESIGNPORT__ */ libffado-2.4.5/src/libstreaming/digidesign/DigidesignPortInfo.cpp0000644000175000001440000000172014206145246024474 0ustar jwoitheusers/* * Copyright (C) 2005-2008, 2011 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "DigidesignPortInfo.h" namespace Streaming { } // end of namespace Streaming libffado-2.4.5/src/libstreaming/digidesign/DigidesignPortInfo.h0000644000175000001440000000376114206145246024150 0ustar jwoitheusers/* * Copyright (C) 2005-2008, 2011 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_DIGIDESIGNPORTINFO__ #define __FFADO_DIGIDESIGNPORTINFO__ #include "debugmodule/debugmodule.h" #include namespace Streaming { /*! \brief Class containing the stream information for a Digidesign channel Contains the information that enables the decoding routine to find this port's data in the ISO events */ class DigidesignPortInfo { public: /** * Initialize Digidesign portinfo * should not be called directly, is inherited by digidesign 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 */ DigidesignPortInfo( int position, int size) : m_position(position), m_size(size) {}; virtual ~DigidesignPortInfo() {}; int getPosition() {return m_position;}; int getSize() {return m_size;}; protected: int m_position; int m_size; }; } // end of namespace Streaming #endif /* __FFADO_DIGIDESIGNPORTINFO__ */ libffado-2.4.5/src/libstreaming/digidesign/DigidesignReceiveStreamProcessor.cpp0000644000175000001440000003323014206145246027373 0ustar jwoitheusers/* * Copyright (C) 2005-2008, 2011 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "libutil/float_cast.h" #include "DigidesignReceiveStreamProcessor.h" #include "DigidesignPort.h" #include "../StreamProcessorManager.h" #include "devicemanager.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/IsoHandlerManager.h" #include "libieee1394/cycletimer.h" #include "libutil/ByteSwap.h" #include #include #include namespace Streaming { DigidesignReceiveStreamProcessor::DigidesignReceiveStreamProcessor(FFADODevice &parent, unsigned int event_size) : StreamProcessor(parent, ePT_Receive) , m_event_size( event_size ) { // Add whatever else needs to be initialised. } unsigned int DigidesignReceiveStreamProcessor::getMaxPacketSize() { // Frame rate is accessible with something like this: // int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); // What's returned here is the maximum packet size seen at the current // frame rate. This depends both on the device and its configuration, // and is futher complicated by the IN/OUT channel asymmetry in some // devices. To avoid most of these complications, just return the // largest packet sizes seen by any supported Digidesign device. // Fill in the requsite details. return 0; } unsigned int DigidesignReceiveStreamProcessor::getNominalFramesPerPacket() { // Return the number of frames per FireWire iso packet. A "frame" here is a collection // of a single audio sample from all active audio channels. If this depends on the // sample rate, that can be obtained using something like this: // int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); return 0; } bool DigidesignReceiveStreamProcessor::prepareChild() { debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this); // If the receive stream processor requires that things be set up which // could not be done in the constructor, here is where they should be // done. Return true on success, or false if the setup failed for some // reason. In most cases, this method will do nothing. return true; } enum StreamProcessor::eChildReturnValue DigidesignReceiveStreamProcessor::processPacketHeader(unsigned char *data, unsigned int length, unsigned char tag, unsigned char sy, uint32_t pkt_ctr) { // This function will be called once for each incoming packet. It // should check to ensure the packet contains valid data and (if it is) // extract a timestamp from it. "data" points to the iso packet's // contents - no assumption is made about what constitutes a "header" // because each device's protocol is different. Note that the FireWire // ISO header is not included in "data". // // The return value should be one of the eCRV_* constants. The two // most commonly used here are: // eCRV_Invalid = unrecognised packet. No further processing needed. // eCRV_OK = packet contains audio data and requires processing. // // The decision as to what constitutes a valid data packet depends on // the format of the iso packets sent by the Digidesign hardware. // // Other parameters to this function contain selected information from // the FireWire ISO header which came with this packet: // - length = length in bytes of the content pointed to by "data". // - tag = the iso packet header's "tag" field. // - sy = the sy field from the iso packet header. // - pkt_ctr = the value of the iso timer at the time the packet was // received by the PC. // // If a valid packet has been received from the Digidesign device, // this method should set the object's m_last_timestamp field to the // timestamp of the last frame in the packet. Determining this is // device specific. Some devices embed this in the stream, while // others require that it be synthesised. FFADO uses this timestamp // to keep the transmit stream in sync with the receive stream. // An example implementation: // // if (packet is valid) { // m_last_timestamp = something useful // return eCRV_OK; // } else { // return eCRV_Invalid; // } return eCRV_Invalid; } enum StreamProcessor::eChildReturnValue DigidesignReceiveStreamProcessor::processPacketData(unsigned char *data, unsigned int length) { // This method is called once per ISO packet which has been flagged as // valid by processPacketHeader(). "data" points to "length" bytes // of data. "data" will in general have the same content as was // presented to processPacketHeader(). // // Conceptually this method is quite simple. Once you know the number // of "events" (aka frames) in the packet - either through prior // knowledge or by calculation based on the packet length - one simply // calls the associated data buffer's writeFrames() method to get the // data from the iso packet and into the internal buffer. The details // as to how this is done are encapsulated in later methods. // First we either calculate or obtain the number of events in the packet. unsigned int n_events = 0; // Call writeFrames() to process the data. m_last_timestamp should have // been set up by processPacketHeader(). The pointer passed to // writeFrames (data in this case) should for convenience be the pointer // to the start of the first frame's data. If for example the packet // starts with its own 8-byte packet header (as opposed to the standard // iso packet header which is stripped off before this method is // called), (data+8) would be supplied instead. if(m_data_buffer->writeFrames(n_events, (char *)(data), m_last_timestamp)) { return eCRV_OK; } else { return eCRV_XRun; } } /*********************************************** * Encoding/Decoding API * ***********************************************/ /** * \brief write received events to the port ringbuffers. */ bool DigidesignReceiveStreamProcessor::processReadBlock(char *data, unsigned int nevents, unsigned int offset) { // This function is called by the Encoding/Decoding engine encapsulated // by the call to writeFrames(). It should extract data from the "data" // array into the device's port ringbuffers. A "port" in this context // is analogous to a jack port - one has one port for each audio channel // provided by the device. // // Each "port" is processed in turn. The decodeDigidesign*() methods // are called to do the actual work. // // "offset" is an offset (in frames) from the start of the ring buffer // where the incoming data should be copied to. "nevents" is the // number of events (aka frames) which need to be processed for each // port. bool no_problem=true; for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { if((*it)->isDisabled()) {continue;}; Port *port=(*it); switch(port->getPortType()) { case Port::E_Audio: if(decodeDigidesignEventsToPort(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not decode packet data to port %s\n",(*it)->getName().c_str()); no_problem=false; } break; case Port::E_Midi: if(decodeDigidesignMidiEventsToPort(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not decode packet midi data to port %s\n",(*it)->getName().c_str()); no_problem=false; } break; default: // ignore break; } } return no_problem; } signed int DigidesignReceiveStreamProcessor::decodeDigidesignEventsToPort(DigidesignAudioPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { // Decode "nevents" samples corresponding to the audio port "p" from the // device datastream "data" into each port's ringbuffer at an offset of // "offset" samples from the ringbuffer's origin. Return value should // be 0. // // In theory the type of data handled by the ringbuffers can be // any one of the eADT_* settings. This can be determined by calling // m_StreamProcessorManager.getAudioDataType(). // For use with JACK (the most common use case of FFADO) this will // always be StreamProcessorManager::eADT_Float, but one should at // least allow for the possibility of others, even if one chooses not // to support them yet. // // The getPosition() port method returns the "position" field of the // port. This is used to describe where in the frame the channel's data // is to be found. Other fields can be added to DigidesignAudioPort to // store other details which might be required to decode the data // packets. In general "position" is about the only one normally // needed. Whether "position" is in bytes, frames, or something else is // really determined by the contents of this method. Choose whatever // is convenient. unsigned int j=0; // The following is an example implementation showing the general idea. // It will need to be changed to suit the protocol of the Digidesign // devices. It assumes that data is supplied by the device in packed // 24-bit integers in big endian format, and that the port's "position" // is in bytes. Note that the m_event_size object member is assumed to // have been set up previous to be the event size in bytes. If for a // given implementation it is more convenient to use a "signed int *" // for src_data one could have m_event_size measured in quadlets (32-bit // integers). Again, it doesn't really matter so long as you're // consistent. // 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. unsigned char *src_data; src_data = (unsigned char *)data + p->getPosition(); switch(m_StreamProcessorManager.getAudioDataType()) { case StreamProcessorManager::eADT_Float: { const float multiplier = 1.0f / (float)(0x7FFFFF); float *buffer=(float *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); buffer+=offset; for (j = 0; j < nevents; j += 1) { // decode max nsamples signed int v = (*src_data<<16)+(*(src_data+1)<<8)+*(src_data+2); /* Sign-extend highest bit of incoming 24-bit integer */ if (*src_data & 0x80) v |= 0xff000000; *buffer = v * multiplier; buffer++; src_data += m_event_size; } } break; case StreamProcessorManager::eADT_Int24: { quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); // Offset is in frames, but each port is only a single // channel, so the number of frames is the same as the // number of quadlets to offset (assuming the port buffer // uses one quadlet per sample, which is the case currently). buffer+=offset; for(j = 0; j < nevents; j += 1) { // Decode nsamples *buffer = (*src_data<<16)+(*(src_data+1)<<8)+*(src_data+2); // Sign-extend highest bit of 24-bit int. // 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; default: // Unsupported type. break; } return 0; } int DigidesignReceiveStreamProcessor::decodeDigidesignMidiEventsToPort( DigidesignMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { // As for decodeDigidesignEventsToPort() except this method // deals with MIDI streams. Depending on how MIDI is sent by // the device, this method may be structured similarly to // decodeDigidesignEventsToPort() or it may be completely // different (as it is for MOTU devices for example). Return // value should be zero. return 0; } } // end of namespace Streaming libffado-2.4.5/src/libstreaming/digidesign/DigidesignReceiveStreamProcessor.h0000644000175000001440000000604714206145246027046 0ustar jwoitheusers/* * Copyright (C) 2005-2008, 2011 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_DIGIDESIGNRECEIVESTREAMPROCESSOR__ #define __FFADO_DIGIDESIGNRECEIVESTREAMPROCESSOR__ /** * This class implements Digidesign streaming */ #include "debugmodule/debugmodule.h" #include "../generic/StreamProcessor.h" #include "../util/cip.h" namespace Streaming { class DigidesignAudioPort; class DigidesignMidiPort; /*! * \brief The Base Class for a Digidesign receive stream processor * * This class implements the outgoing stream processing for * Digidesign devices * */ class DigidesignReceiveStreamProcessor : public StreamProcessor { public: /** * Create a Digidesign receive StreamProcessor * @param port 1394 port * @param event_size the size in bytes of a single "frame" in the audio stream */ DigidesignReceiveStreamProcessor(FFADODevice &parent, unsigned int event_size); virtual ~DigidesignReceiveStreamProcessor() {}; enum eChildReturnValue processPacketHeader(unsigned char *data, unsigned int length, unsigned char tag, unsigned char sy, uint32_t pkt_ctr); enum eChildReturnValue processPacketData(unsigned char *data, unsigned int length); virtual bool prepareChild(); public: virtual unsigned int getEventSize() {return m_event_size;}; virtual unsigned int getMaxPacketSize(); virtual unsigned int getEventsPerFrame() { return 1; }; 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 decodeDigidesignEventsToPort(DigidesignAudioPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); int decodeDigidesignMidiEventsToPort(DigidesignMidiPort *, 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_DIGIDESIGNRECEIVESTREAMPROCESSOR__ */ libffado-2.4.5/src/libstreaming/digidesign/DigidesignTransmitStreamProcessor.cpp0000644000175000001440000007365414206145246027630 0ustar jwoitheusers/* * Copyright (C) 2005-2008, 2011 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "libutil/float_cast.h" #include "DigidesignTransmitStreamProcessor.h" #include "DigidesignPort.h" #include "../StreamProcessorManager.h" #include "devicemanager.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/IsoHandlerManager.h" #include "libieee1394/cycletimer.h" #include "libutil/ByteSwap.h" #include #include /* Provide more intuitive access to GCC's branch predition built-ins */ #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) namespace Streaming { /* transmit */ DigidesignTransmitStreamProcessor::DigidesignTransmitStreamProcessor(FFADODevice &parent, unsigned int event_size ) : StreamProcessor(parent, ePT_Transmit ) , m_event_size( event_size ) { // Provide any other initialisation code needed. } unsigned int DigidesignTransmitStreamProcessor::getMaxPacketSize() { // Return the maximum packet size expected given the current device // configuration, in bytes. Often this will depend on the current // sample rate which can be retrieved using something like this: // // int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); return 0; } unsigned int DigidesignTransmitStreamProcessor::getNominalFramesPerPacket() { // Return the number of frames per packet. Often this will depend on // the device's current sample rate which can be obtained as per the // comment in getMaxPacketSize(). return 0; } enum StreamProcessor::eChildReturnValue DigidesignTransmitStreamProcessor::generatePacketHeader ( unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr ) { // Construct the header portion of a packet to send to the Digidesign // hardware. This normally requires the "length", "tag" and "sy" fields // to be set as needed. While "length" should be set here, "data" // probably ought to be left alone (there's another method for dealing // with the packet data). "pkt_ctr" gives the iso cycle timer // corresponding to the time that the packet will be transmitted. // // It is this method which determines whether a packet should be sent in // the iso cycle indicated by "pkt_ctr". The code which follows is // mostly boiler-plate and lifted straight from the MOTU driver (which // in turn came from the AMDTP code from memory). The basic theory as // to when a packet should be sent is very similar for all devices // because the timing requirements are effectively abstracted by the // concept of the timestamp. // // The vast majority of the logic in this method came from Pieter // Palmers, who wrote the foundational streaming infrastructure. unsigned int cycle = CYCLE_TIMER_GET_CYCLES(pkt_ctr); signed n_events = getNominalFramesPerPacket(); // Do housekeeping expected for all packets. Note that if it is later // identified that an empty packet should be sent then "length" will be // overriden in generateEmptyPacketHeader(). // // As per the FireWire standards, only set "tag" if the Digidesign // expects a CIP header in the first two bytes of "data". Similarly, // remove the "+8" from the length calculation if no CIP header is to be // included. *sy = 0x00; *tag = 1; // Set to 0 if Digidesign don't use CIP headers *length = n_events*m_event_size + 8; signed int fc; uint64_t presentation_time; unsigned int presentation_cycle; int cycles_until_presentation; uint64_t transmit_at_time; unsigned int transmit_at_cycle; int cycles_until_transmit; debugOutput ( DEBUG_LEVEL_ULTRA_VERBOSE, "Try for cycle %d\n", cycle ); // check whether the packet buffer has packets for us to send. // the base timestamp is the one of the next sample in the buffer ffado_timestamp_t ts_head_tmp; m_data_buffer->getBufferHeadTimestamp ( &ts_head_tmp, &fc ); // thread safe // the timestamp gives us the time at which we want the sample block // to be output by the device presentation_time = ( uint64_t ) ts_head_tmp; // now we calculate the time when we have to transmit the sample block transmit_at_time = substractTicks ( presentation_time, DIGIDESIGN_TRANSMIT_TRANSFER_DELAY ); // calculate the cycle this block should be presented in // (this is just a virtual calculation since at that time it should // already be in the device's buffer) presentation_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( presentation_time ) ); // calculate the cycle this block should be transmitted in transmit_at_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( transmit_at_time ) ); // we can check whether this cycle is within the 'window' we have // to send this packet. // first calculate the number of cycles left before presentation time cycles_until_presentation = diffCycles ( presentation_cycle, cycle ); // we can check whether this cycle is within the 'window' we have // to send this packet. // first calculate the number of cycles left before presentation time cycles_until_transmit = diffCycles ( transmit_at_cycle, cycle ); // two different options: // 1) there are not enough frames for one packet // => determine wether this is a problem, since we might still // have some time to send it // 2) there are enough packets // => determine whether we have to send them in this packet if ( fc < ( signed int ) getNominalFramesPerPacket() ) { // not enough frames in the buffer, // we can still postpone the queueing of the packets // if we are far enough ahead of the presentation time if ( cycles_until_presentation <= DIGIDESIGN_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=%011"PRIu64" (%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 >= DIGIDESIGN_MIN_CYCLES_BEFORE_PRESENTATION) { // we are not that late and can still try to transmit the packet fillDataPacketHeader((quadlet_t *)data, length, presentation_time); m_last_timestamp = presentation_time; return eCRV_Packet; } else // definitely too late { return eCRV_XRun; } } else if(cycles_until_transmit <= DIGIDESIGN_MAX_CYCLES_TO_TRANSMIT_EARLY) { // it's time send the packet fillDataPacketHeader((quadlet_t *)data, length, presentation_time); m_last_timestamp = presentation_time; return eCRV_Packet; } else { debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "Too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011"PRIu64" (%04u), TSP=%011"PRIu64" (%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 > DIGIDESIGN_MAX_CYCLES_TO_TRANSMIT_EARLY + 1 ) { debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "Way too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011"PRIu64" (%04u), TSP=%011"PRIu64" (%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 DigidesignTransmitStreamProcessor::generatePacketData ( unsigned char *data, unsigned int *length) { // This method should fill the "length" bytes of "data" with streaming // data from the devices ports. Similar to the receive side, this // method calls the object's data buffer readFrames() method which takes // care of calling the encoding functions to facilitate this data copy. // Treat the packet data as being in quadlets. quadlet_t *quadlet = (quadlet_t *)data; // If there's a CIP header included, skip past it. Otherwise don't // do this step. quadlet += 2; signed n_events = getNominalFramesPerPacket(); // unsigned dbs = m_event_size / 4; // Encode data into packet. If a CIP header is to be placed at the // start of "data", the pointer passed to readFrames() should be // (data+8) so audio data isn't copied to that location. if (m_data_buffer->readFrames(n_events, (char *)(data + 8))) { // If audio data was succesfully copied, deal with timestamps // embedded in the ISO stream if relevant. How this is done depends // on what the device expects. Some devices like the MOTUs // timestamp each frame, while others have a single timestamp // somewhere in the packet which applies to a particular // representative frame within the packet. // // The timestamps are usually in terms of iso cycle timer ticks, and // it's therefore often useful to know how many ticks correspond to // the interval between two frames (as deduced by the rate at which // data is arriving from the device). This can be accessed from // here with something like: // // float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getTicksPerFrame(); // // "quadlet" starts out pointing to the start of the first frame, // and it can be advanced to the next frame by adding dbs to it. return eCRV_OK; } else return eCRV_XRun; } enum StreamProcessor::eChildReturnValue DigidesignTransmitStreamProcessor::generateEmptyPacketHeader ( unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr ) { debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "XMIT EMPTY: CY=%04d, TSP=%011"PRIu64" (%04u)\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp, ( unsigned int ) TICKS_TO_CYCLES ( m_last_timestamp ) ); // An "empty" packet is one which contains no audio data. It is used // when it is determined that no audio data needs to be sent to the // device in a given iso cycle. Whether one sends empty packets or just // skips the cycle entirely depends on the device's protocol. Some // expect empty packets while others are happy if nothing is sent. // The following sets up an "empty" packet assuming that a CIP header is // included in such a packet. If this isn't the case, "tag" should be 0 // and the "length" will be 0. *sy = 0x00; *tag = 1; *length = 8; fillNoDataPacketHeader ( (quadlet_t *)data, length ); return eCRV_OK; } enum StreamProcessor::eChildReturnValue DigidesignTransmitStreamProcessor::generateEmptyPacketData ( unsigned char *data, unsigned int *length) { // By definition an empty packet doesn't contain any audio data, so // normally this method doesn't have to do anything. return eCRV_OK; } enum StreamProcessor::eChildReturnValue DigidesignTransmitStreamProcessor::generateSilentPacketHeader ( unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr ) { // This method generates a silent packet - that is, one which contains // nothing but zeros in the audio data fields. All other aspects of the // packet are the same as a regular data packet so much of the logic // from generatePacketHeader() is needed here too. The main difference // between the two methods is the source of audio data - here we just // need zeros. // // Note that not all devices require "silent" packets. If the // Digidesign interfaces don't this function may ultimtely be removed. unsigned int cycle = CYCLE_TIMER_GET_CYCLES(pkt_ctr); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "XMIT SILENT: CY=%04u, TSP=%011"PRIu64" (%04u)\n", cycle, m_last_timestamp, ( unsigned int ) TICKS_TO_CYCLES ( m_last_timestamp ) ); signed n_events = getNominalFramesPerPacket(); // Again, here's the housekeeping. If there's no CIP header needed, set "tag" // to 0 and remove the "+8" from the setting of "length" below. *sy = 0x00; *tag = 1; /* Assume the packet will have audio data. If it turns out we need an empty packet * the length will be overridden by fillNoDataPacketHeader(). */ *length = n_events*m_event_size + 8; uint64_t presentation_time; unsigned int presentation_cycle; int cycles_until_presentation; uint64_t transmit_at_time; unsigned int transmit_at_cycle; int cycles_until_transmit; /* The sample buffer is not necessarily running when silent packets are * needed, so use m_last_timestamp (the timestamp of the previously sent * data packet) as the basis for the presentation time of the next * packet. Since we're only writing zeros we don't have to deal with * buffer xruns. */ float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getTicksPerFrame(); presentation_time = addTicks(m_last_timestamp, (unsigned int)lrintf(n_events * ticks_per_frame)); transmit_at_time = substractTicks(presentation_time, DIGIDESIGN_TRANSMIT_TRANSFER_DELAY); presentation_cycle = (unsigned int)(TICKS_TO_CYCLES(presentation_time)); transmit_at_cycle = (unsigned int)(TICKS_TO_CYCLES(transmit_at_time)); cycles_until_presentation = diffCycles(presentation_cycle, cycle); cycles_until_transmit = diffCycles(transmit_at_cycle, cycle); if (cycles_until_transmit < 0) { if (cycles_until_presentation >= DIGIDESIGN_MIN_CYCLES_BEFORE_PRESENTATION) { m_last_timestamp = presentation_time; fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp); return eCRV_Packet; } else { return eCRV_XRun; } } else if (cycles_until_transmit <= DIGIDESIGN_MAX_CYCLES_TO_TRANSMIT_EARLY) { m_last_timestamp = presentation_time; fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp); return eCRV_Packet; } else { return eCRV_EmptyPacket; } return eCRV_Invalid; } enum StreamProcessor::eChildReturnValue DigidesignTransmitStreamProcessor::generateSilentPacketData ( unsigned char *data, unsigned int *length ) { // Simply set all audio data to zero since that's what's meant by // a "silent" packet. Note that for the example code given below // m_event_size is in bytes. quadlet_t *quadlet = (quadlet_t *)data; quadlet += 2; // skip the header - remove if no CIP header is used // Size of a single data frame in quadlets // unsigned dbs = m_event_size / 4; signed n_events = getNominalFramesPerPacket(); memset(quadlet, 0, n_events*m_event_size); // If there are per-frame timestamps to set up (or other things), it's // done here. "quadlet" starts out pointing to the start of the first // frame, and it can be advanced to the next frame by adding dbs to it. // // Obtaining the "ticks per frame" is sometimes useful when constructing // timestamps: // float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getTicksPerFrame(); return eCRV_OK; } unsigned int DigidesignTransmitStreamProcessor::fillDataPacketHeader ( quadlet_t *data, unsigned int* length, uint32_t ts ) { // If there's a CIP header (or a similar per-packet header distinct from // the standard iso header) this method is used to construct it. The return // value is the number of events (aka frames) to be included in the packet. quadlet_t *quadlet = (quadlet_t *)data; // Size of a single data frame in quadlets. unsigned dbs = m_event_size / 4; signed n_events = getNominalFramesPerPacket(); // Depending on the device this might have to be set to something sensible. unsigned int tx_dbc = 0; // The following shows how a CIP header can be constructed. This is // taken directly from the MOTU driver and therefore contains some // hard-coded fields as they are used by the MOTU devices. Most // importantly, the MOTUs don't always follow the ieee1394 standard when // it comes to fields in the CIP header, so this code is at best a guide // as to how things might be done. // // The entire thing can be omitted if CIP headers aren't used by the // digidesign devices. *quadlet = CondSwapToBus32(0x00000400 | ((m_Parent.get1394Service().getLocalNodeId()&0x3f)<<24) | tx_dbc | (dbs<<16)); quadlet++; *quadlet = CondSwapToBus32(0x8222ffff); quadlet++; return n_events; } unsigned int DigidesignTransmitStreamProcessor::fillNoDataPacketHeader ( quadlet_t *data, unsigned int* length ) { // This constructs any per-packet headers required in packets containing // no audio data. As for fillDataPacketHeader(), this is an example // lifted from the MOTU code to show how it might be done. // fillNoDataPacketHeader() should return the number of frames to be // transmitted in the packet, which is 0 by definition. quadlet_t *quadlet = (quadlet_t *)data; // 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 tx_dbc is the same as the previously // transmitted block. unsigned dbs = m_event_size / 4; // Depending on the device this might have to be set to something sensible. unsigned int tx_dbc = m_event_size / 4; *quadlet = CondSwapToBus32(0x00000400 | ((m_Parent.get1394Service().getLocalNodeId()&0x3f)<<24) | tx_dbc | (dbs<<16)); quadlet++; *quadlet = CondSwapToBus32(0x8222ffff); quadlet++; *length = 8; return 0; } bool DigidesignTransmitStreamProcessor::prepareChild() { debugOutput ( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this ); // Additional setup can be done here if nececssary. Normally this // method doesn't do anything but it's provided in case it proves useful // for some device. return true; } /* * Compose the event streams for the packets from the port buffers */ bool DigidesignTransmitStreamProcessor::processWriteBlock(char *data, unsigned int nevents, unsigned int offset) { bool no_problem=true; // This function is the transmit equivalent of // DigidesignReceiveStreamProcessor::processReadBlock(). It iterates // over the ports registered with the device and calls the applicable // encoding methods to transfer data from the port buffers into the // packet data which is pointed to by "data". "nevents" is the number // of events (aka frames) to transfer and "offset" is the position // within the port ring buffers to take data from. for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { // If this port is disabled, unconditionally send it silence. if((*it)->isDisabled()) { if (encodeSilencePortToDigidesignEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode silence for disabled port %s to Digidesign events\n",(*it)->getName().c_str()); // Don't treat this as a fatal error at this point } continue; } Port *port=(*it); switch(port->getPortType()) { case Port::E_Audio: if (encodePortToDigidesignEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode port %s to Digidesign events\n",(*it)->getName().c_str()); no_problem=false; } break; case Port::E_Midi: if (encodePortToDigidesignMidiEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode port %s to Midi events\n",(*it)->getName().c_str()); no_problem=false; } break; default: // ignore break; } } return no_problem; } bool DigidesignTransmitStreamProcessor::transmitSilenceBlock(char *data, unsigned int nevents, unsigned int offset) { // This is the same as the non-silence version, except that is // doesn't read from the port buffers. bool no_problem = true; for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { Port *port=(*it); switch(port->getPortType()) { case Port::E_Audio: if (encodeSilencePortToDigidesignEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode port %s to Digidesign events\n",(*it)->getName().c_str()); no_problem = false; } break; case Port::E_Midi: if (encodeSilencePortToDigidesignMidiEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode port %s to Midi events\n",(*it)->getName().c_str()); no_problem = false; } break; default: // ignore break; } } return no_problem; } int DigidesignTransmitStreamProcessor::encodePortToDigidesignEvents(DigidesignAudioPort *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 Digidesign interface. // 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 Digidesign // 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. // This code assumes that the Digidesign expects its data to be packed // 24-bit integers. If this is not the case changes will be required. unsigned int j=0; // Use char here since the target address won't necessarily be // aligned; use of an unaligned quadlet_t may cause issues on certain // architectures. Besides, the target (data going directly to the MOTU) // isn't structured in quadlets anyway; it mainly consists of packed // 24-bit integers. unsigned char *target; target = (unsigned char *)data + p->getPosition(); switch(m_StreamProcessorManager.getAudioDataType()) { default: case StreamProcessorManager::eADT_Int24: { quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); // Offset is in frames, but each port is only a single // channel, so the number of frames is the same as the // number of quadlets to offset (assuming the port buffer // uses one quadlet per sample, which is the case currently). buffer+=offset; for(j = 0; j < nevents; j += 1) { // Decode nsamples *target = (*buffer >> 16) & 0xff; *(target+1) = (*buffer >> 8) & 0xff; *(target+2) = (*buffer) & 0xff; buffer++; target+=m_event_size; } } break; case StreamProcessorManager::eADT_Float: { const float multiplier = (float)(0x7FFFFF); float *buffer=(float *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); buffer+=offset; for(j = 0; j < nevents; j += 1) { // decode max nsamples float in = *buffer; #if DIGIDESIGN_CLIP_FLOATS if (unlikely(in > 1.0)) in = 1.0; if (unlikely(in < -1.0)) in = -1.0; #endif unsigned int v = lrintf(in * multiplier); *target = (v >> 16) & 0xff; *(target+1) = (v >> 8) & 0xff; *(target+2) = v & 0xff; buffer++; target+=m_event_size; } } break; } return 0; } int DigidesignTransmitStreamProcessor::encodeSilencePortToDigidesignEvents(DigidesignAudioPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { // Encodes silence to the digidesign channel corresponding to the given // audio port. As for encodePortToDigidesignEvents() above, this // assumes that each audio data sample is a packed signed 24-bit // integer. Changes will be necessary if Digidesign uses a different // format in the packets. unsigned int j=0; unsigned char *target = (unsigned char *)data + p->getPosition(); switch (m_StreamProcessorManager.getAudioDataType()) { default: case StreamProcessorManager::eADT_Int24: case StreamProcessorManager::eADT_Float: for (j = 0; j < nevents; j++) { *target = *(target+1) = *(target+2) = 0; target += m_event_size; } break; } return 0; } int DigidesignTransmitStreamProcessor::encodePortToDigidesignMidiEvents( DigidesignMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { // Encode MIDI data into the packet to be sent to the Digidesign // hardware. Depending on the way MIDI data is formatted in the packet, // this function may take a similar form to // encodePortToDigidesignEvents(), or it may be completely different. // For example, the MOTU driver structures it quite differently due to // the way MIDI is carried in the packet. // // The return value is zero. return 0; } int DigidesignTransmitStreamProcessor::encodeSilencePortToDigidesignMidiEvents( DigidesignMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { // Write zeros to a MIDI port region of the transmit packet. unsigned int j; unsigned char *target = (unsigned char *)data + p->getPosition(); // For now, a "silent" MIDI event contains nothing but zeroes. This // may have to change if we find this isn't for some reason appropriate. for (j=0; j. * */ #ifndef __FFADO_DIGIDESIGNTRANSMITSTREAMPROCESSOR__ #define __FFADO_DIGIDESIGNTRANSMITSTREAMPROCESSOR__ /** * This class implements Digidesign based streaming */ #include "debugmodule/debugmodule.h" #include "../generic/StreamProcessor.h" #include "../util/cip.h" namespace Streaming { class Port; class DigidesignAudioPort; class DigidesignMidiPort; /*! \brief The Base Class for a Digidesign transmit stream processor This class implements a TransmitStreamProcessor that multiplexes Ports into Digidesign streams. */ class DigidesignTransmitStreamProcessor : public StreamProcessor { public: /** * Create a Digidesign transmit StreamProcessor */ DigidesignTransmitStreamProcessor(FFADODevice &parent, unsigned int event_size); virtual ~DigidesignTransmitStreamProcessor() {}; enum eChildReturnValue generatePacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr); enum eChildReturnValue generatePacketData(unsigned char *data, unsigned int *length); enum eChildReturnValue generateEmptyPacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr); enum eChildReturnValue generateEmptyPacketData(unsigned char *data, unsigned int *length); enum eChildReturnValue generateSilentPacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr); enum eChildReturnValue generateSilentPacketData(unsigned char *data, unsigned int *length); virtual bool prepareChild(); public: virtual unsigned int getEventSize() {return m_event_size;}; virtual unsigned int getMaxPacketSize(); virtual unsigned int getEventsPerFrame() { return 1; }; 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 encodePortToDigidesignEvents(DigidesignAudioPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); int encodeSilencePortToDigidesignEvents(DigidesignAudioPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); int encodePortToDigidesignMidiEvents( DigidesignMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents); int encodeSilencePortToDigidesignMidiEvents( DigidesignMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents); /* * An iso packet mostly consists of multiple events. m_event_size * is the size of a single 'event' in bytes. */ unsigned int m_event_size; }; } // end of namespace Streaming #endif /* __FFADO_DIGIDESIGNTRANSMITSTREAMPROCESSOR__ */ libffado-2.4.5/src/libstreaming/generic/0000755000175000001440000000000014206145612017544 5ustar jwoitheuserslibffado-2.4.5/src/libstreaming/generic/Port.cpp0000644000175000001440000001106214206145246021177 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "Port.h" #include "PortManager.h" #include #include namespace Streaming { IMPL_DEBUG_MODULE( Port, Port, DEBUG_LEVEL_NORMAL ); Port::Port(PortManager& m, std::string name, enum E_PortType porttype, enum E_Direction direction) : m_Name( name ) , m_disabled( true ) , m_buffersize( 0 ) , m_PortType( porttype ) , m_Direction( direction ) , m_buffer( NULL ) , m_manager( m ) , m_State( E_Created ) { m_manager.registerPort(this); } Port::~Port() { debugOutput( DEBUG_LEVEL_VERBOSE, "deleting port %s\n", getName().c_str()); m_manager.unregisterPort(this); } /** * The idea is that you set all port parameters, and then initialize the port. * This allocates all resources and makes the port usable. However, you can't * change the port properties anymore after this. * * @return true if successfull. false if not (all resources are freed). */ bool Port::init() { debugOutput( DEBUG_LEVEL_VERBOSE, "Initialize port %s\n", m_Name.c_str()); if (m_State != E_Created) { debugFatal("Port (%s) not in E_Created state: %d\n", m_Name.c_str(), m_State); return false; } if (m_buffersize == 0) { debugFatal("Cannot initialize a port with buffersize=0\n"); return false; } m_State = E_Initialized; return true; } bool Port::reset() { return true; } bool Port::setName(std::string name) { debugOutput( DEBUG_LEVEL_VERBOSE, "Setting name to %s for port %s\n",name.c_str(),m_Name.c_str()); if (m_State != E_Created) { debugFatal("Port (%s) not in E_Created state: %d\n",m_Name.c_str(),m_State); return false; } m_Name=name; return true; } bool Port::setBufferSize(unsigned int newsize) { debugOutput( DEBUG_LEVEL_VERBOSE, "Setting buffersize to %d for port %s\n",newsize,m_Name.c_str()); if (m_State != E_Created && m_disabled == false) { debugFatal("Port (%s) not in E_Created/disabled state: %d\n",m_Name.c_str(),m_State); return false; } m_buffersize=newsize; return true; } unsigned int Port::getEventSize() { return 4; // whether it's float, int24, midi or control, it's 4 } // buffer handling api's for pointer buffers /** * Get the buffer address * * @param buff */ void *Port::getBufferAddress() { return m_buffer; }; /** * Set the external buffer address. * * @param buff */ void Port::setBufferAddress(void *buff) { m_buffer=buff; } /// Enable the port. (this can be called anytime) void Port::enable() { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Enabling port %s...\n",m_Name.c_str()); m_disabled = false; } /// Disable the port. (this can be called anytime) void Port::disable() { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Disabling port %s...\n",m_Name.c_str()); m_disabled = true; } /** * Obtain the port type in string format * @return type name of the port */ std::string Port::getPortTypeName() { switch(m_PortType) { case E_Audio: return "Audio"; case E_Midi: return "MIDI"; case E_Control: return "Control"; default: return "Invalid"; } } void Port::show() { debugOutput(DEBUG_LEVEL_VERBOSE,"Name : %s\n", m_Name.c_str()); debugOutput(DEBUG_LEVEL_VERBOSE,"Enabled? : %d\n", m_disabled==false); debugOutput(DEBUG_LEVEL_VERBOSE,"State? : %d\n", m_State); debugOutput(DEBUG_LEVEL_VERBOSE,"Buffer Size : %d\n", m_buffersize); debugOutput(DEBUG_LEVEL_VERBOSE,"Event Size : %d\n", getEventSize()); debugOutput(DEBUG_LEVEL_VERBOSE,"Port Type : %d\n", m_PortType); debugOutput(DEBUG_LEVEL_VERBOSE,"Direction : %d\n", m_Direction); } void Port::setVerboseLevel(int l) { setDebugLevel(l); } } libffado-2.4.5/src/libstreaming/generic/Port.h0000644000175000001440000001204214206145246020643 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_PORT__ #define __FFADO_PORT__ #include "libutil/ringbuffer.h" #include "debugmodule/debugmodule.h" #include #include namespace Streaming { class PortManager; /*! \brief The Base Class for Ports Ports are the entities that provide the interface between the ISO streaming layer and the datatype-specific layer. You can define port types by subclassing the base port class. After creating a port, you have to set its parameters and then call the init() function. This is because a port needs information from two sources to operate: 1) the stream composition information from the AvDevice 2) the streaming API setup (buffer type, data type, ...) \note There are not much virtual functions here because of the high frequency of calling. We try to do everything with a base class getter, and a child class setter. If this isn't possible, we do a static_cast. This however can only be done inside the streamprocessor that handles the specific sub-class types of the ports. i.e. by design you should make sure that the static_cast will be OK. */ class Port { public: /*! \brief The port type */ enum E_PortType { E_Audio, E_Midi, E_Control, }; /*! \brief The port direction */ enum E_Direction { E_Playback, E_Capture, }; Port(PortManager&, std::string name, enum E_PortType, enum E_Direction); virtual ~Port(); /// Enable the port. (this can be called anytime) void enable(); /// Disable the port. (this can be called anytime) void disable(); /// is the port disabled? (this can be called anytime) bool isDisabled() {return m_disabled;}; /*! \brief Initialize the port */ bool init(); bool prepare() {return true;}; bool reset(); std::string getName() {return m_Name;}; bool setName(std::string name); /** * \brief returns the size of the events in the port buffer, in bytes * */ unsigned int getEventSize(); enum E_PortType getPortType() {return m_PortType;}; ///< returns the port type (is fixed) std::string getPortTypeName(); enum E_Direction getDirection() {return m_Direction;}; ///< returns the direction (is fixed) /** * \brief returns the size of the port buffer * * counted in number of E_DataType units (events), not in bytes * */ unsigned int getBufferSize() {return m_buffersize;}; /** * \brief sets the size of the port buffer * * counted in number of E_DataType units, not in bytes * * if there is an external buffer assigned, it should * be large enough * if there is an internal buffer, it will be resized * * \note use before calling init() */ virtual bool setBufferSize(unsigned int); void setBufferAddress(void *buff); void *getBufferAddress(); PortManager& getManager() { return m_manager; }; virtual void setVerboseLevel(int l); virtual void show(); protected: std::string m_Name; ///< Port name, [at construction] bool m_disabled; ///< is the port disabled?, [anytime] unsigned int m_buffersize; enum E_PortType m_PortType; enum E_Direction m_Direction; void *m_buffer; PortManager& m_manager; DECLARE_DEBUG_MODULE; // the state machine protected: enum EStates { E_Created, E_Initialized, E_Running, E_Error }; enum EStates m_State; }; /*! \brief The Base Class for an Audio Port */ class AudioPort : public Port { public: AudioPort(PortManager& m, std::string name, enum E_Direction direction) : Port(m, name, E_Audio, direction) {}; virtual ~AudioPort() {}; }; /*! \brief The Base Class for a Midi Port */ class MidiPort : public Port { public: MidiPort(PortManager& m, std::string name, enum E_Direction direction) : Port(m, name, E_Midi, direction) {}; virtual ~MidiPort() {}; }; /*! \brief The Base Class for a control port */ class ControlPort : public Port { public: ControlPort(PortManager& m, std::string name, enum E_Direction direction) : Port(m, name, E_Control, direction) {}; virtual ~ControlPort() {}; }; } #endif /* __FFADO_PORT__ */ libffado-2.4.5/src/libstreaming/generic/PortManager.cpp0000644000175000001440000001436014206145246022476 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "PortManager.h" #include "Port.h" #include #include #include namespace Streaming { IMPL_DEBUG_MODULE( PortManager, PortManager, DEBUG_LEVEL_NORMAL ); PortManager::PortManager() { } PortManager::~PortManager() { flushDebugOutput(); // delete all ports that are still registered to the manager while (m_Ports.size()) { // This will also remove the port from m_Ports via // PortManager::unregister(). delete m_Ports.front(); } for ( Util::FunctorVectorIterator it = m_UpdateHandlers.begin(); it != m_UpdateHandlers.end(); ++it ) { Util::Functor* func = *it; delete func; } } bool PortManager::makeNameUnique(Port *port) { bool done = false; int idx = 0; std::string portname_orig = port->getName(); while(!done && idx < 10000) { bool is_unique=true; for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { is_unique &= !((*it)->getName() == port->getName()); } if (is_unique) { done = true; } else { std::ostringstream portname; portname << portname_orig << idx++; port->setName(portname.str()); } } if(idx < 10000) return true; else return false; } /** * * @param port * @return */ bool PortManager::registerPort(Port *port) { assert(port); debugOutput( DEBUG_LEVEL_VERBOSE, "Adding port %s, type: %d, dir: %d\n", port->getName().c_str(), port->getPortType(), port->getDirection()); port->setVerboseLevel(getDebugLevel()); if (makeNameUnique(port)) { m_Ports.push_back(port); callUpdateHandlers(); return true; } else { return false; } } bool PortManager::unregisterPort(Port *port) { assert(port); debugOutput( DEBUG_LEVEL_VERBOSE, "unregistering port %s\n",port->getName().c_str()); for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { if(*it == port) { m_Ports.erase(it); callUpdateHandlers(); return true; } } debugOutput( DEBUG_LEVEL_VERBOSE, "port %s not found \n",port->getName().c_str()); return false; //not found } int PortManager::getPortCount(enum Port::E_PortType type) { int count=0; for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { if ( (*it)->getPortType() == type ) { count++; } } return count; } int PortManager::getPortCount() { int count = 0; count += m_Ports.size(); return count; } Port * PortManager::getPortAtIdx(unsigned int index) { return m_Ports.at(index); } void PortManager::setVerboseLevel(int i) { setDebugLevel(i); for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { (*it)->setVerboseLevel(i); } } bool PortManager::resetPorts() { debugOutput( DEBUG_LEVEL_VERBOSE, "reset ports\n"); for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { if(!(*it)->reset()) { debugFatal("Could not reset port %s",(*it)->getName().c_str()); return false; } } return true; } bool PortManager::initPorts() { debugOutput( DEBUG_LEVEL_VERBOSE, "init ports\n"); for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { if(!(*it)->init()) { debugFatal("Could not init port %s\n", (*it)->getName().c_str()); return false; } } return true; } bool PortManager::preparePorts() { debugOutput( DEBUG_LEVEL_VERBOSE, "preparing ports\n"); for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { if(!(*it)->prepare()) { debugFatal("Could not prepare port %s",(*it)->getName().c_str()); return false; } } return true; } bool PortManager::addPortManagerUpdateHandler( Util::Functor* functor ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Adding PortManagerUpdate handler (%p)\n", functor); m_UpdateHandlers.push_back( functor ); return true; } bool PortManager::remPortManagerUpdateHandler( Util::Functor* functor ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Removing PortManagerUpdate handler (%p)\n", functor); for ( Util::FunctorVectorIterator it = m_UpdateHandlers.begin(); it != m_UpdateHandlers.end(); ++it ) { if ( *it == functor ) { debugOutput(DEBUG_LEVEL_VERBOSE, " found\n"); m_UpdateHandlers.erase( it ); return true; } } debugOutput(DEBUG_LEVEL_VERBOSE, " not found\n"); return false; } Util::Functor* PortManager::getUpdateHandlerForPtr(void *ptr) { for ( Util::FunctorVectorIterator it = m_UpdateHandlers.begin(); it != m_UpdateHandlers.end(); ++it ) { if ( (*it)->matchCallee(ptr) ) { debugOutput(DEBUG_LEVEL_VERBOSE, " found\n"); return *it; } } return NULL; } void PortManager::callUpdateHandlers() { for ( Util::FunctorVectorIterator it = m_UpdateHandlers.begin(); it != m_UpdateHandlers.end(); ++it ) { Util::Functor* func = *it; debugOutput(DEBUG_LEVEL_VERBOSE, "Calling PortManagerUpdate handler (%p)\n", func); ( *func )(); } } } libffado-2.4.5/src/libstreaming/generic/PortManager.h0000644000175000001440000000411014206145246022133 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_PORTMANAGER__ #define __FFADO_PORTMANAGER__ #include "Port.h" #include "libutil/Functors.h" #include "debugmodule/debugmodule.h" #include namespace Streaming { typedef std::vector PortVector; typedef std::vector::iterator PortVectorIterator; /*! \brief The Base Class for any class that maintains a collection of ports. Contains helper classes that allow the easy maintaining of Port collections. */ class PortManager { public: PortManager(); virtual ~PortManager(); virtual bool makeNameUnique(Port *port); virtual bool registerPort(Port *port); virtual bool unregisterPort(Port *port); int getPortCount(enum Port::E_PortType); int getPortCount(); Port *getPortAtIdx(unsigned int index); virtual bool resetPorts(); virtual bool initPorts(); virtual bool preparePorts(); virtual void setVerboseLevel(int l); bool addPortManagerUpdateHandler( Util::Functor* functor ); bool remPortManagerUpdateHandler( Util::Functor* functor ); Util::Functor* getUpdateHandlerForPtr(void *ptr); // ugly!! protected: void callUpdateHandlers(); PortVector m_Ports; Util::FunctorVector m_UpdateHandlers; DECLARE_DEBUG_MODULE; }; } #endif /* __FFADO_PORTMANAGER__ */ libffado-2.4.5/src/libstreaming/generic/StreamProcessor.cpp0000644000175000001440000022646214206145246023422 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "StreamProcessor.h" #include "../StreamProcessorManager.h" #include "devicemanager.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/IsoHandlerManager.h" #include "libieee1394/cycletimer.h" #include "libutil/Time.h" #include "libutil/Atomic.h" #include #include #define SIGNAL_ACTIVITY_SPM { \ m_StreamProcessorManager.signalActivity(); \ } #define SIGNAL_ACTIVITY_ISO_XMIT { \ m_IsoHandlerManager.signalActivityTransmit(); \ } #define SIGNAL_ACTIVITY_ISO_RECV { \ m_IsoHandlerManager.signalActivityReceive(); \ } #define SIGNAL_ACTIVITY_ALL { \ m_StreamProcessorManager.signalActivity(); \ m_IsoHandlerManager.signalActivityTransmit(); \ m_IsoHandlerManager.signalActivityReceive(); \ } namespace Streaming { IMPL_DEBUG_MODULE( StreamProcessor, StreamProcessor, DEBUG_LEVEL_VERBOSE ); StreamProcessor::StreamProcessor(FFADODevice &parent, enum eProcessorType type) : m_processor_type ( type ) , m_state( ePS_Created ) , m_next_state( ePS_Invalid ) , m_cycle_to_switch_state( 0 ) , m_Parent( parent ) , m_1394service( parent.get1394Service() ) // local cache , m_IsoHandlerManager( parent.get1394Service().getIsoHandlerManager() ) // local cache , m_StreamProcessorManager( m_Parent.getDeviceManager().getStreamProcessorManager() ) // local cache , m_local_node_id ( 0 ) // local cache , m_channel( -1 ) , m_last_timestamp( 0 ) , m_last_timestamp2( 0 ) , m_correct_last_timestamp( false ) , m_scratch_buffer( NULL ) , m_scratch_buffer_size_bytes( 0 ) , m_ticks_per_frame( 0 ) , m_dll_bandwidth_hz ( STREAMPROCESSOR_DLL_BW_HZ ) , m_extra_buffer_frames( 0 ) , m_max_fs_diff_norm ( 0.01 ) , m_max_diff_ticks ( 50 ) , m_in_xrun( false ) { // create the timestamped buffer and register ourselves as its client m_data_buffer = new Util::TimestampedBuffer(this); } StreamProcessor::~StreamProcessor() { m_StreamProcessorManager.unregisterProcessor(this); if(!m_IsoHandlerManager.unregisterStream(this)) { debugOutput(DEBUG_LEVEL_VERBOSE,"Could not unregister stream processor with the Iso manager\n"); } if (m_data_buffer) delete m_data_buffer; if (m_scratch_buffer) delete[] m_scratch_buffer; } bool StreamProcessor::periodSizeChanged(unsigned int new_periodsize) { // This is called by the StreamProcessorManager whenever the period size // is changed via setPeriodSize(). If the stream processor needs to do // anything in response it can be done in this method. Buffer size // changes should only ever be made when streaming is not active. // // Return false if there was a problem dealing with the resize. if (m_state!=ePS_Stopped && m_state!=ePS_Created) { debugOutput(DEBUG_LEVEL_WARNING, "(%p) period change should only be done with streaming stopped\n", this); return false; } // make the scratch buffer one period of frames long m_scratch_buffer_size_bytes = new_periodsize * getEventsPerFrame() * getEventSize(); debugOutput( DEBUG_LEVEL_VERBOSE, " Allocate scratch buffer of %zd quadlets\n", m_scratch_buffer_size_bytes); if(m_scratch_buffer) delete[] m_scratch_buffer; m_scratch_buffer = new byte_t[m_scratch_buffer_size_bytes]; if(m_scratch_buffer == NULL) { debugFatal("Could not allocate scratch buffer\n"); return false; } // set the parameters of ports we can: // we want the audio ports to be period buffered, // and the midi ports to be packet buffered for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { debugOutput(DEBUG_LEVEL_VERBOSE, "Setting up port %s\n",(*it)->getName().c_str()); if(!(*it)->setBufferSize(m_StreamProcessorManager.getPeriodSize())) { debugFatal("Could not set buffer size to %d\n",m_StreamProcessorManager.getPeriodSize()); return false; } } if (!setupDataBuffer()) { debugFatal("Could not setup data buffer\n"); return false; } return updateState(); } bool StreamProcessor::handleBusResetDo() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) handling busreset\n", this); m_state = ePS_Error; // this will result in the SPM dying m_in_xrun = true; SIGNAL_ACTIVITY_ALL; return true; } bool StreamProcessor::handleBusReset() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) handling busreset\n", this); // we are sure that we're not iterated since this is called from within the ISO manager thread // lock the wait loop of the SPM, such that the client leaves us alone m_StreamProcessorManager.lockWaitLoop(); // pass on to the implementing classes bool retval = handleBusResetDo(); // resume wait loop m_StreamProcessorManager.unlockWaitLoop(); return retval; } void StreamProcessor::handlerDied() { debugWarning("Handler died for %p\n", this); m_state = ePS_Stopped; m_in_xrun = true; SIGNAL_ACTIVITY_ALL; } int StreamProcessor::getMaxFrameLatency() { 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; // ensure proper ceiling uint64_t nominal_packets = (nominal_ticks+TICKS_PER_CYCLE-1) / TICKS_PER_CYCLE; return nominal_packets; } unsigned int StreamProcessor::getPacketsPerPeriod() { return getNominalPacketsNeeded(m_StreamProcessorManager.getPeriodSize()); } unsigned int StreamProcessor::getNbPacketsIsoXmitBuffer() { // if we use one thread per packet, we can put every frame directly into the ISO buffer // the waitForClient in IsoHandler will take care of the fact that the frames are // not present in time unsigned int packets_to_prebuffer = (getPacketsPerPeriod() * (m_StreamProcessorManager.getNbBuffers())) + 10; debugOutput(DEBUG_LEVEL_VERBOSE, "Nominal prebuffer: %u\n", packets_to_prebuffer); return packets_to_prebuffer; } /*********************************************** * Buffer management and manipulation * ***********************************************/ bool StreamProcessor::setupDataBuffer() { assert(m_data_buffer); unsigned int ringbuffer_size_frames = m_StreamProcessorManager.getNbBuffers() * m_StreamProcessorManager.getPeriodSize(); ringbuffer_size_frames += m_extra_buffer_frames; ringbuffer_size_frames += 1; // to ensure that we can fit it all in there bool result = true; m_correct_last_timestamp = false; // 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() ); } // Completing the buffer's setup and calling the prepare() method is not // applicable unless the nominal rate (m_ticks_per_frame) has been // configured. This may not be the case when setupDataBuffer() is // called from prepare() via periodSizeChanged(), but will be when called // from doStop() and from periodSizeChanged() via the stream processor's // setPeriodSize() method. if (m_ticks_per_frame > 0) { result &= m_data_buffer->setNominalRate(m_ticks_per_frame); result &= m_data_buffer->setWrapValue(128L * TICKS_PER_SECOND); result &= m_data_buffer->setBandwidth(STREAMPROCESSOR_DLL_FAST_BW_HZ / (double)TICKS_PER_SECOND); result &= m_data_buffer->prepare(); // FIXME: the name debugOutput(DEBUG_LEVEL_VERBOSE, "DLL info: nominal tpf: %f, update period: %d, bandwidth: %e 1/ticks (%e Hz)\n", m_data_buffer->getNominalRate(), m_data_buffer->getUpdatePeriod(), m_data_buffer->getBandwidth(), m_data_buffer->getBandwidth() * TICKS_PER_SECOND); } return result; } void StreamProcessor::getBufferHeadTimestamp(ffado_timestamp_t *ts, signed int *fc) { m_data_buffer->getBufferHeadTimestamp(ts, fc); } void StreamProcessor::getBufferTailTimestamp(ffado_timestamp_t *ts, signed int *fc) { m_data_buffer->getBufferTailTimestamp(ts, fc); } void StreamProcessor::setBufferTailTimestamp(ffado_timestamp_t new_timestamp) { m_data_buffer->setBufferTailTimestamp(new_timestamp); } void StreamProcessor::setBufferHeadTimestamp(ffado_timestamp_t new_timestamp) { m_data_buffer->setBufferHeadTimestamp(new_timestamp); } int StreamProcessor::getBufferFill() { return m_data_buffer->getBufferFill(); } void StreamProcessor::setExtraBufferFrames(unsigned int frames) { debugOutput(DEBUG_LEVEL_VERBOSE, "Setting extra buffer to %d frames\n", frames); m_extra_buffer_frames = frames; } unsigned int StreamProcessor::getExtraBufferFrames() { return m_extra_buffer_frames; } uint64_t StreamProcessor::getTimeAtPeriod() { if (getType() == ePT_Receive) { ffado_timestamp_t next_period_boundary = m_data_buffer->getTimestampFromHead(m_StreamProcessorManager.getPeriodSize()); #ifdef DEBUG ffado_timestamp_t ts; signed int fc; m_data_buffer->getBufferTailTimestamp(&ts,&fc); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "=> NPD=" TIMESTAMP_FORMAT_SPEC ", LTS=" TIMESTAMP_FORMAT_SPEC ", FC=%5u, TPF=%f\n", next_period_boundary, ts, fc, getTicksPerFrame() ); #endif return (uint64_t)next_period_boundary; } else { ffado_timestamp_t next_period_boundary = m_data_buffer->getTimestampFromTail((m_StreamProcessorManager.getNbBuffers()-1) * m_StreamProcessorManager.getPeriodSize()); #ifdef DEBUG ffado_timestamp_t ts; signed int fc; m_data_buffer->getBufferTailTimestamp(&ts,&fc); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "=> NPD=" TIMESTAMP_FORMAT_SPEC ", LTS=" TIMESTAMP_FORMAT_SPEC ", FC=%5u, TPF=%f\n", next_period_boundary, ts, fc, getTicksPerFrame() ); #endif return (uint64_t)next_period_boundary; } } float StreamProcessor::getTicksPerFrame() { assert(m_data_buffer != NULL); return m_data_buffer->getRate(); } void StreamProcessor::setTicksPerFrame(float tpf) { assert(m_data_buffer != NULL); m_data_buffer->setRate(tpf); } bool StreamProcessor::setDllBandwidth(float bw) { m_dll_bandwidth_hz = bw; return true; } bool StreamProcessor::canClientTransferFrames(unsigned int nbframes) { bool can_transfer; unsigned int fc = m_data_buffer->getFrameCounter(); if (getType() == ePT_Receive) { can_transfer = (fc >= nbframes); } else { // there has to be enough space to put the frames in can_transfer = m_data_buffer->getBufferSize() - fc > nbframes; // or the buffer is transparent can_transfer |= m_data_buffer->isTransparent(); } #ifdef DEBUG if (!can_transfer) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) cannot transfer since fc == %u, nbframes == %u\n", this, ePTToString(getType()), fc, nbframes); } #endif return can_transfer; } /*********************************************** * I/O API * ***********************************************/ // Packet transfer API enum raw1394_iso_disposition StreamProcessor::putPacket(unsigned char *data, unsigned int length, unsigned char channel, unsigned char tag, unsigned char sy, uint32_t pkt_ctr, unsigned int dropped_cycles) { // bypass based upon state #ifdef DEBUG if (m_state == ePS_Invalid) { debugError("Should not have state %s\n", ePSToString(m_state) ); return RAW1394_ISO_ERROR; } #endif // FIXME: isn't this also an error condition? if (m_state == ePS_Created) { return RAW1394_ISO_DEFER; } if (m_state == ePS_Error) { debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "skip due to error state\n"); return RAW1394_ISO_OK; } // store the previous timestamp m_last_timestamp2 = m_last_timestamp; // NOTE: synchronized switching is restricted to a 0.5 sec span (4000 cycles) // it happens on the first 'good' cycle for the wait condition // or on the first received cycle that is received afterwards (might be a problem) // check whether we are waiting for a stream to be disabled if(m_state == ePS_WaitingForStreamDisable) { // we then check whether we have to switch on this cycle if (diffCycles(CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_cycle_to_switch_state) >= 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to DryRunning\n"); m_next_state = ePS_DryRunning; if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } } else { // not time to disable yet } // the received data can be discarded while waiting for the stream // to be disabled // similarly for dropped packets return RAW1394_ISO_OK; } // check whether we are waiting for a stream to be enabled else if(m_state == ePS_WaitingForStreamEnable && m_next_state == ePS_WaitingForStreamEnable) { // we then check whether we have to switch on this cycle if (diffCycles(CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_cycle_to_switch_state) >= 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to Running\n"); m_next_state = ePS_Running; if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } } else { // not time to enable yet } // we are dryRunning hence data should be processed in any case } // check the packet header enum eChildReturnValue result = processPacketHeader(data, length, tag, sy, pkt_ctr); // handle dropped cycles if(dropped_cycles) { // make sure the last_timestamp is corrected m_correct_last_timestamp = true; if (m_state == ePS_Running) { // this is an xrun situation m_in_xrun = true; debugOutput(DEBUG_LEVEL_NORMAL, "Should update state to WaitingForStreamDisable due to dropped packet xrun\n"); m_cycle_to_switch_state = CYCLE_TIMER_GET_CYCLES(pkt_ctr) + 1; // switch in the next cycle m_next_state = ePS_WaitingForStreamDisable; // execute the requested change if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } } } if (result == eCRV_OK) { #ifdef DEBUG if (m_last_timestamp > 0 && m_last_timestamp2 > 0) { int64_t tsp_diff = diffTicks(m_last_timestamp, m_last_timestamp2); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "TSP diff: %" PRId64 "\n", tsp_diff); double tsp_diff_d = tsp_diff; double fs_syt = 1.0/tsp_diff_d; fs_syt *= (double)getNominalFramesPerPacket() * (double)TICKS_PER_USEC * 1e6; double fs_nom = (double)m_StreamProcessorManager.getNominalRate(); double fs_diff = fs_nom - fs_syt; double fs_diff_norm = fs_diff/fs_nom; debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "Nom fs: %12f, Instantanous fs: %12f, diff: %12f (%12f)\n", fs_nom, fs_syt, fs_diff, fs_diff_norm); if (fs_diff_norm > m_max_fs_diff_norm || fs_diff_norm < -m_max_fs_diff_norm) { debugWarning( "Instantanous samplerate more than %0.0f%% off nominal. [Nom fs: %12f, Instantanous fs: %12f, diff: %12f (%12f)]\n", m_max_fs_diff_norm*100, fs_nom, fs_syt, fs_diff, fs_diff_norm); } int ticks_per_packet = (int)(getTicksPerFrame() * getNominalFramesPerPacket()); int diff = diffTicks(m_last_timestamp, m_last_timestamp2); // display message if the difference between two successive tick // values is more than 50 ticks. 1 sample at 48k is 512 ticks // so 50 ticks = 10%, which is a rather large jitter value. if(diff-ticks_per_packet > m_max_diff_ticks || diff-ticks_per_packet < -m_max_diff_ticks) { debugOutput(DEBUG_LEVEL_VERBOSE, "cy %04d rather large TSP difference TS=%011" PRIu64 " => TS=%011" PRIu64 " (%d, nom %d)\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp2, m_last_timestamp, diff, ticks_per_packet); // !!!HACK!!! FIXME: this is the result of a failure in wrapping/unwrapping somewhere // it's definitely a bug. // try to fix up the timestamp int64_t last_timestamp_fixed; // first try to add one second last_timestamp_fixed = addTicks(m_last_timestamp, TICKS_PER_SECOND); diff = diffTicks(last_timestamp_fixed, m_last_timestamp2); if(diff-ticks_per_packet < 50 && diff-ticks_per_packet > -50) { debugWarning("cy %04d rather large TSP difference TS=%011" PRIu64 " => TS=%011" PRIu64 " (%d, nom %d)\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp2, m_last_timestamp, diff, ticks_per_packet); debugWarning("HACK: fixed by adding one second of ticks. This is a bug being run-time fixed.\n"); m_last_timestamp = last_timestamp_fixed; } else { // if that didn't work, try to subtract one second last_timestamp_fixed = substractTicks(m_last_timestamp, TICKS_PER_SECOND); diff = diffTicks(last_timestamp_fixed, m_last_timestamp2); if(diff-ticks_per_packet < 50 && diff-ticks_per_packet > -50) { debugWarning("cy %04d rather large TSP difference TS=%011" PRIu64 " => TS=%011" PRIu64 " (%d, nom %d)\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp2, m_last_timestamp, diff, ticks_per_packet); debugWarning("HACK: fixed by subtracing one second of ticks. This is a bug being run-time fixed.\n"); m_last_timestamp = last_timestamp_fixed; } } } debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "%04u %011" PRIu64 " %011" PRIu64 " %d %d\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp2, m_last_timestamp, diff, ticks_per_packet); } #endif debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "RECV: CY=%04u TS=%011" PRIu64 "\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp); if(m_correct_last_timestamp) { // they represent a discontinuity in the timestamps, and hence are // to be dealt with debugOutput(DEBUG_LEVEL_NORMAL, "(%p) Correcting timestamp for dropped cycles, discarding packet...\n", this); m_data_buffer->setBufferTailTimestamp(substractTicks(m_last_timestamp, (uint64_t)(getNominalFramesPerPacket() * getTicksPerFrame()))); m_correct_last_timestamp = false; } // check whether we are waiting for a stream to startup // this requires that the packet is good if(m_state == ePS_WaitingForStream) { // since we have a packet with an OK header, // we can indicate that the stream started up // we then check whether we have to switch on this cycle if (diffCycles(CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_cycle_to_switch_state) >= 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to DryRunning due to good packet\n"); // hence go to the dryRunning state m_next_state = ePS_DryRunning; if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } } else { // not time (yet) to switch state } // in both cases we don't want to process the data return RAW1394_ISO_OK; } // check whether a state change has been requested // note that only the wait state changes are synchronized with the cycles else if(m_state != m_next_state) { debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state from %s to %s\n", ePSToString(m_state), ePSToString(m_next_state)); // execute the requested change if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } // don't process the data when waiting for a stream if(m_state == ePS_WaitingForStream) { return RAW1394_ISO_OK; } } // for all states that reach this we are allowed to // do protocol specific data reception enum eChildReturnValue result2 = processPacketData(data, length); // if an xrun occured, switch to the dryRunning state and // allow for the xrun to be picked up if (result2 == eCRV_XRun) { debugOutput(DEBUG_LEVEL_NORMAL, "processPacketData xrun\n"); m_in_xrun = true; debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStreamDisable due to data xrun\n"); m_cycle_to_switch_state = CYCLE_TIMER_GET_CYCLES(pkt_ctr)+1; // switch in the next cycle m_next_state = ePS_WaitingForStreamDisable; // execute the requested change if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } return RAW1394_ISO_DEFER; } else if(result2 == eCRV_OK) { return RAW1394_ISO_OK; } else { debugError("Invalid response\n"); return RAW1394_ISO_ERROR; } } else if(result == eCRV_Invalid) { // apparently we don't have to do anything when the packets are not valid return RAW1394_ISO_OK; } else { debugError("Invalid response\n"); return RAW1394_ISO_ERROR; } debugError("reached the unreachable\n"); return RAW1394_ISO_ERROR; } enum raw1394_iso_disposition StreamProcessor::getPacket(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr, unsigned int dropped_cycles, unsigned int skipped, unsigned int max_length) { if (pkt_ctr == 0xFFFFFFFF) { *tag = 0; *sy = 0; *length = 0; return RAW1394_ISO_OK; } if (m_state == ePS_Error) { debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "skip due to error state\n"); return RAW1394_ISO_OK; } uint64_t prev_timestamp; // note that we can ignore skipped cycles since // the protocol will take care of that if (dropped_cycles > 0) { // HACK: this should not be necessary, since the header generation functions should trigger the xrun. // but apparently there are some issues with the 1394 stack m_in_xrun = true; if(m_state == ePS_Running) { debugShowBackLogLines(200); debugOutput(DEBUG_LEVEL_NORMAL, "dropped packets xrun (%u)\n", dropped_cycles); debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStreamDisable due to dropped packets xrun\n"); m_cycle_to_switch_state = CYCLE_TIMER_GET_CYCLES(pkt_ctr) + 1; m_next_state = ePS_WaitingForStreamDisable; // execute the requested change if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } goto send_empty_packet; } } #ifdef DEBUG // bypass based upon state if (m_state == ePS_Invalid) { debugError("Should not have state %s\n", ePSToString(m_state) ); return RAW1394_ISO_ERROR; } #endif // FIXME: can this happen? if (m_state == ePS_Created) { *tag = 0; *sy = 0; *length = 0; return RAW1394_ISO_DEFER; } // normal processing // store the previous timestamp // keep the old value here, update m_last_timestamp2 only when // a valid packet will be sent prev_timestamp = m_last_timestamp; // NOTE: synchronized switching is restricted to a 0.5 sec span (4000 cycles) // it happens on the first 'good' cycle for the wait condition // or on the first received cycle that is received afterwards (might be a problem) // check whether we are waiting for a stream to be disabled if(m_state == ePS_WaitingForStreamDisable) { // we then check whether we have to switch on this cycle if (diffCycles(CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_cycle_to_switch_state) >= 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to DryRunning\n"); m_next_state = ePS_DryRunning; if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } } // generate the silent packet header enum eChildReturnValue result = generateSilentPacketHeader(data, length, tag, sy, pkt_ctr); if (result == eCRV_Packet) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "XMIT SILENT: CY=%04u TS=%011" PRIu64 "\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp); // assumed not to xrun generateSilentPacketData(data, length); return RAW1394_ISO_OK; // FIXME: PP: I think this [empty packet] should also be a possibility // JMW: yes, it should. MOTU needs it for a clean shutdown. } else if (result == eCRV_EmptyPacket) { goto send_empty_packet; } else { debugError("Invalid return value: %d\n", result); return RAW1394_ISO_ERROR; } } // check whether we are waiting for a stream to be enabled else if(m_state == ePS_WaitingForStreamEnable && m_next_state == ePS_WaitingForStreamEnable) { // we then check whether we have to switch on this cycle if (diffCycles(CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_cycle_to_switch_state) >= 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to Running\n"); m_next_state = ePS_Running; if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } } else { // not time to enable yet } // we are dryRunning hence data should be processed in any case } // check whether we are waiting for a stream to startup else if(m_state == ePS_WaitingForStream) { // check whether we have to switch on this cycle if ((diffCycles(CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_cycle_to_switch_state) >= 0)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStream to DryRunning\n"); // hence go to the dryRunning state m_next_state = ePS_DryRunning; if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } } else { // not time (yet) to switch state } } else if(m_state == ePS_Running) { // check the packet header enum eChildReturnValue result = generatePacketHeader(data, length, tag, sy, pkt_ctr); if (result == eCRV_Packet || result == eCRV_Defer) { debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "XMIT: CY=%04u TS=%011" PRIu64 "\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp); // valid packet timestamp m_last_timestamp2 = prev_timestamp; // check whether a state change has been requested // note that only the wait state changes are synchronized with the cycles if(m_state != m_next_state) { debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state from %s to %s\n", ePSToString(m_state), ePSToString(m_next_state)); // execute the requested change if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } } enum eChildReturnValue result2 = generatePacketData(data, length); // if an xrun occured, switch to the dryRunning state and // allow for the xrun to be picked up if (result2 == eCRV_XRun) { debugOutput(DEBUG_LEVEL_NORMAL, "generatePacketData xrun\n"); m_in_xrun = true; debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStreamDisable due to data xrun\n"); m_cycle_to_switch_state = CYCLE_TIMER_GET_CYCLES(pkt_ctr) + 1; // switch in the next cycle m_next_state = ePS_WaitingForStreamDisable; // execute the requested change if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } goto send_empty_packet; } #ifdef DEBUG if (m_last_timestamp > 0 && m_last_timestamp2 > 0) { int64_t tsp_diff = diffTicks(m_last_timestamp, m_last_timestamp2); debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "TSP diff: %" PRId64 "\n", tsp_diff); double tsp_diff_d = tsp_diff; double fs_syt = 1.0/tsp_diff_d; fs_syt *= (double)getNominalFramesPerPacket() * (double)TICKS_PER_USEC * 1e6; double fs_nom = (double)m_StreamProcessorManager.getNominalRate(); double fs_diff = fs_nom - fs_syt; double fs_diff_norm = fs_diff/fs_nom; debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "Nom fs: %12f, Instantanous fs: %12f, diff: %12f (%12f)\n", fs_nom, fs_syt, fs_diff, fs_diff_norm); // debugOutput(DEBUG_LEVEL_VERBOSE, "Diff fs: %12f, m_last_timestamp: %011" PRIu64 ", m_last_timestamp2: %011" PRIu64 "\n", // fs_diff, m_last_timestamp, m_last_timestamp2); if (fs_diff_norm > m_max_fs_diff_norm || fs_diff_norm < -m_max_fs_diff_norm) { debugWarning( "Instantanous samplerate more than %0.0f%% off nominal. [Nom fs: %12f, Instantanous fs: %12f, diff: %12f (%12f)]\n", m_max_fs_diff_norm*100, fs_nom, fs_syt, fs_diff, fs_diff_norm); } int ticks_per_packet = (int)(getTicksPerFrame() * getNominalFramesPerPacket()); int diff = diffTicks(m_last_timestamp, m_last_timestamp2); // display message if the difference between two successive tick // values is more than 50 ticks. 1 sample at 48k is 512 ticks // so 50 ticks = 10%, which is a rather large jitter value. if(diff-ticks_per_packet > m_max_diff_ticks || diff-ticks_per_packet < -m_max_diff_ticks) { debugOutput(DEBUG_LEVEL_VERBOSE, "cy %04d, rather large TSP difference TS=%011" PRIu64 " => TS=%011" PRIu64 " (%d, nom %d)\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp2, m_last_timestamp, diff, ticks_per_packet); } debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "%04d %011" PRIu64 " %011" PRIu64 " %d %d\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp2, m_last_timestamp, diff, ticks_per_packet); } #endif // skip queueing packets if we detect that there are not enough frames // available if(result2 == eCRV_Defer || result == eCRV_Defer) { return RAW1394_ISO_DEFER; } else { return RAW1394_ISO_OK; } } else if (result == eCRV_XRun) { // pick up the possible xruns debugOutput(DEBUG_LEVEL_NORMAL, "generatePacketHeader xrun\n"); m_in_xrun = true; debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStreamDisable due to header xrun\n"); m_cycle_to_switch_state = CYCLE_TIMER_GET_CYCLES(pkt_ctr) + 1; // switch in the next cycle m_next_state = ePS_WaitingForStreamDisable; // execute the requested change if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } } else if (result == eCRV_EmptyPacket) { if(m_state != m_next_state) { debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state from %s to %s\n", ePSToString(m_state), ePSToString(m_next_state)); // execute the requested change if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } } goto send_empty_packet; } else if (result == eCRV_Again) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "have to retry cycle %d\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr)); if(m_state != m_next_state) { debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state from %s to %s\n", ePSToString(m_state), ePSToString(m_next_state)); // execute the requested change if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } } // usleep(125); // only when using thread-per-handler // return RAW1394_ISO_AGAIN; generateEmptyPacketHeader(data, length, tag, sy, pkt_ctr); generateEmptyPacketData(data, length); return RAW1394_ISO_DEFER; } else { debugError("Invalid return value: %d\n", result); return RAW1394_ISO_ERROR; } } // we are not running, so send an empty packet // we should generate a valid packet any time send_empty_packet: // note that only the wait state changes are synchronized with the cycles if(m_state != m_next_state) { debugOutput(DEBUG_LEVEL_VERBOSE, "Should update '%s' state from %s to %s\n", getTypeString(), ePSToString(m_state), ePSToString(m_next_state)); // execute the requested change if (!updateState()) { // we are allowed to change the state directly debugError("Could not update state!\n"); return RAW1394_ISO_ERROR; } } debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "XMIT EMPTY: CY=%04u\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr)); generateEmptyPacketHeader(data, length, tag, sy, pkt_ctr); generateEmptyPacketData(data, length); return RAW1394_ISO_OK; } void StreamProcessor::packetsStopped() { m_state = ePS_Stopped; m_next_state = ePS_Stopped; } // Frame Transfer API /** * Transfer a block of frames from the event buffer to the port buffers * @param nbframes number of frames to transfer * @param ts the timestamp that the LAST frame in the block should have * @return */ bool StreamProcessor::getFrames(unsigned int nbframes, int64_t ts) { bool result; debugOutputExtreme( DEBUG_LEVEL_VERBOSE, "(%p, %s) getFrames(%d, %11" PRIu64 ")\n", this, getTypeString(), nbframes, ts); assert( getType() == ePT_Receive ); if(isDryRunning()) result = getFramesDry(nbframes, ts); else result = getFramesWet(nbframes, ts); SIGNAL_ACTIVITY_ISO_RECV; return result; } bool StreamProcessor::getFramesWet(unsigned int nbframes, int64_t ts) { // FIXME: this should be done somewhere else #ifdef DEBUG uint64_t ts_expected; signed int fc; int32_t lag_ticks; float lag_frames; // in order to sync up multiple received streams, we should // use the ts parameter. It specifies the time of the block's // last sample. float srate = m_StreamProcessorManager.getSyncSource().getTicksPerFrame(); assert(srate != 0.0); int64_t this_block_length_in_ticks = (int64_t)(((float)nbframes) * srate); ffado_timestamp_t ts_head_tmp; m_data_buffer->getBufferHeadTimestamp(&ts_head_tmp, &fc); ts_expected = addTicks((uint64_t)ts_head_tmp, this_block_length_in_ticks); lag_ticks = diffTicks(ts, ts_expected); lag_frames = (((float)lag_ticks) / srate); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "stream (%p): drifts %6d ticks = %10.5f frames (rate=%10.5f), %" PRId64 ", %" PRIu64 ", %d\n", this, lag_ticks, lag_frames, srate, ts, ts_expected, fc); if (lag_frames >= 1.0) { // the stream lags debugOutput(DEBUG_LEVEL_VERBOSE, "stream (%p): lags with %6d ticks = %10.5f frames (rate=%10.5f), %" PRId64 ", %" PRIu64 ", %d\n", this, lag_ticks, lag_frames, srate, ts, ts_expected, fc); } else if (lag_frames <= -1.0) { // the stream leads debugOutput(DEBUG_LEVEL_VERBOSE, "stream (%p): leads with %6d ticks = %10.5f frames (rate=%10.5f), %" PRId64 ", %" PRIu64 ", %d\n", this, lag_ticks, lag_frames, srate, ts, ts_expected, fc); } #endif // ask the buffer to process nbframes of frames // using it's registered client's processReadBlock(), // which should be ours m_data_buffer->blockProcessReadFrames(nbframes); return true; } bool StreamProcessor::getFramesDry(unsigned int nbframes, int64_t ts) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "stream (%p): dry run %d frames (@ ts=%" PRId64 ")\n", this, nbframes, ts); // dry run on this side means that we put silence in all enabled ports // since there is do data put into the ringbuffer in the dry-running state return provideSilenceBlock(nbframes, 0); } bool StreamProcessor::dropFrames(unsigned int nbframes, int64_t ts) { bool result; debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "StreamProcessor::dropFrames(%d, %" PRId64 ")\n", nbframes, ts); result = m_data_buffer->dropFrames(nbframes); SIGNAL_ACTIVITY_ISO_RECV; return result; } bool StreamProcessor::putFrames(unsigned int nbframes, int64_t ts) { bool result; debugOutputExtreme( DEBUG_LEVEL_VERBOSE, "(%p, %s) putFrames(%d, %11" PRIu64 ")\n", this, getTypeString(), nbframes, ts); assert( getType() == ePT_Transmit ); if(isDryRunning()) result = putFramesDry(nbframes, ts); else result = putFramesWet(nbframes, ts); SIGNAL_ACTIVITY_ISO_XMIT; return result; } bool StreamProcessor::putFramesWet(unsigned int nbframes, int64_t ts) { debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, "StreamProcessor::putFramesWet(%d, %" PRIu64 ")\n", nbframes, ts); // transfer the data m_data_buffer->blockProcessWriteFrames(nbframes, ts); debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, " New timestamp: %" PRIu64 "\n", ts); return true; // FIXME: what about failure? } bool StreamProcessor::putFramesDry(unsigned int nbframes, int64_t ts) { debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, "StreamProcessor::putFramesDry(%d, %" PRIu64 ")\n", nbframes, ts); // do nothing return true; } bool StreamProcessor::putSilenceFrames(unsigned int nbframes, int64_t ts) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "StreamProcessor::putSilenceFrames(%d, %" PRIu64 ")\n", nbframes, ts); size_t bytes_per_frame = getEventSize() * getEventsPerFrame(); unsigned int scratch_buffer_size_frames = m_scratch_buffer_size_bytes / bytes_per_frame; if (nbframes > scratch_buffer_size_frames) { debugError("nframes (%u) > scratch_buffer_size_frames (%u)\n", nbframes, scratch_buffer_size_frames); } assert(m_scratch_buffer); if(!transmitSilenceBlock((char *)m_scratch_buffer, nbframes, 0)) { debugError("Could not prepare silent block\n"); return false; } if(!m_data_buffer->writeFrames(nbframes, (char *)m_scratch_buffer, ts)) { debugError("Could not write silent block\n"); return false; } SIGNAL_ACTIVITY_ISO_XMIT; return true; } bool StreamProcessor::shiftStream(int nbframes) { // FIXME: this is not a good idea since the ISO thread is also writing the buffer // resulting in multiple writers (not supported) bool result; if(nbframes == 0) return true; if(nbframes > 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) dropping %d frames\n", this, nbframes); result = m_data_buffer->dropFrames(nbframes); } else { result = false; return result; debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) adding %d frames\n", this, nbframes); while(nbframes++) { result &= m_data_buffer->writeDummyFrame(); } } SIGNAL_ACTIVITY_ALL; return result; } /** * @brief write silence events to the stream ringbuffers. */ bool StreamProcessor::provideSilenceBlock(unsigned int nevents, unsigned int offset) { bool no_problem=true; for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { if((*it)->isDisabled()) {continue;}; if(provideSilenceToPort((*it), offset, nevents)) { debugWarning("Could not put silence into to port %s",(*it)->getName().c_str()); no_problem=false; } } return no_problem; } int StreamProcessor::provideSilenceToPort(Port *p, unsigned int offset, unsigned int nevents) { unsigned int j=0; switch(p->getPortType()) { default: debugError("Invalid port type: %d\n", p->getPortType()); return -1; case Port::E_Midi: case Port::E_Control: { quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); buffer+=offset; for(j = 0; j < nevents; j += 1) { *(buffer)=0; buffer++; } } break; case Port::E_Audio: switch(m_StreamProcessorManager.getAudioDataType()) { case StreamProcessorManager::eADT_Int24: { quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); buffer+=offset; for(j = 0; j < nevents; j += 1) { *(buffer)=0; buffer++; } } break; case StreamProcessorManager::eADT_Float: { float *buffer=(float *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); buffer+=offset; for(j = 0; j < nevents; j += 1) { *buffer = 0.0; buffer++; } } break; } break; } return 0; } /*********************************************** * State related API * ***********************************************/ bool StreamProcessor::init() { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "init...\n"); if(!m_IsoHandlerManager.registerStream(this)) { debugOutput(DEBUG_LEVEL_VERBOSE,"Could not register stream processor with the Iso manager\n"); return false; } if(!m_StreamProcessorManager.registerProcessor(this)) { debugOutput(DEBUG_LEVEL_VERBOSE,"Could not register stream processor with the SP manager\n"); return false; } // initialization can be done without requesting it // from the packet loop m_next_state = ePS_Created; return true; } bool StreamProcessor::prepare() { debugOutput( DEBUG_LEVEL_VERBOSE, "Prepare SP (%p)...\n", this); if (periodSizeChanged(m_StreamProcessorManager.getPeriodSize()) == false) return false; // the API specific settings of the ports should already be set, // as this is called from the processorManager->prepare() // so we can init the ports if(!PortManager::initPorts()) { debugFatal("Could not initialize ports\n"); return false; } if (!prepareChild()) { debugFatal("Could not prepare child\n"); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "Prepared for:\n"); debugOutput( DEBUG_LEVEL_VERBOSE, " Samplerate: %d [DLL Bandwidth: %f Hz]\n", m_StreamProcessorManager.getNominalRate(), m_dll_bandwidth_hz); debugOutput( DEBUG_LEVEL_VERBOSE, " PeriodSize: %d, NbBuffers: %d\n", m_StreamProcessorManager.getPeriodSize(), m_StreamProcessorManager.getNbBuffers()); debugOutput( DEBUG_LEVEL_VERBOSE, " Port: %d, Channel: %d\n", m_1394service.getPort(), m_channel); // initialization can be done without requesting it // from the packet loop m_next_state = ePS_Stopped; return updateState(); } bool StreamProcessor::scheduleStateTransition(enum eProcessorState state, uint64_t time_instant) { // first set the time, since in the packet loop we first check m_state == m_next_state before // using the time m_cycle_to_switch_state = TICKS_TO_CYCLES(time_instant); m_next_state = state; // wake up any threads that might be waiting on data in the buffers // since a state transition can cause data to become available SIGNAL_ACTIVITY_ALL; return true; } bool StreamProcessor::waitForState(enum eProcessorState state, unsigned int timeout_ms) { debugOutput(DEBUG_LEVEL_VERBOSE, "Waiting for state %s\n", ePSToString(state)); int cnt = timeout_ms; while (m_state != state && cnt) { SleepRelativeUsec(1000); cnt--; } if(cnt==0) { debugOutput(DEBUG_LEVEL_VERBOSE, " Timeout\n"); return false; } return true; } bool StreamProcessor::scheduleStartDryRunning(int64_t t) { uint64_t tx; if (t < 0) { tx = addTicks(m_1394service.getCycleTimerTicks(), 200 * TICKS_PER_CYCLE); } else { tx = t; } uint64_t start_handler_ticks = substractTicks(tx, 100 * TICKS_PER_CYCLE); debugOutput(DEBUG_LEVEL_VERBOSE,"for %s SP (%p)\n", ePTToString(getType()), this); #ifdef DEBUG uint64_t now = m_1394service.getCycleTimerTicks(); debugOutput(DEBUG_LEVEL_VERBOSE," Now : %011" PRIu64 " (%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 : %011" PRIu64 " (%03us %04uc %04ut)\n", tx, (unsigned int)TICKS_TO_SECS(tx), (unsigned int)TICKS_TO_CYCLES(tx), (unsigned int)TICKS_TO_OFFSET(tx)); #endif if (m_state == ePS_Stopped) { if(!m_IsoHandlerManager.startHandlerForStream( this, TICKS_TO_CYCLES(start_handler_ticks))) { debugError("Could not start handler for SP %p\n", this); return false; } return scheduleStateTransition(ePS_WaitingForStream, tx); } else if (m_state == ePS_Running) { return scheduleStateTransition(ePS_WaitingForStreamDisable, tx); } else if (m_state == ePS_DryRunning) { debugOutput(DEBUG_LEVEL_VERBOSE, " %p already in DryRunning state\n", this); return true; } else if (m_state == ePS_WaitingForStreamEnable) { debugOutput(DEBUG_LEVEL_VERBOSE, " %p still waiting to switch to Running state\n", this); // this will happen immediately return scheduleStateTransition(ePS_DryRunning, tx); } else if (m_state == ePS_WaitingForStreamDisable) { debugOutput(DEBUG_LEVEL_VERBOSE, " %p already waiting to switch to DryRunning state\n", this); return true; } else { debugError("Cannot switch to ePS_DryRunning from %s\n", ePSToString(m_state)); return false; } } bool StreamProcessor::scheduleStartRunning(int64_t t) { uint64_t tx; if (t < 0) { tx = addTicks(m_1394service.getCycleTimerTicks(), 200 * TICKS_PER_CYCLE); } else { tx = t; } debugOutput(DEBUG_LEVEL_VERBOSE,"for %s SP (%p)\n", ePTToString(getType()), this); #ifdef DEBUG uint64_t now = m_1394service.getCycleTimerTicks(); debugOutput(DEBUG_LEVEL_VERBOSE," Now : %011" PRIu64 " (%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 : %011" PRIu64 " (%03us %04uc %04ut)\n", tx, (unsigned int)TICKS_TO_SECS(tx), (unsigned int)TICKS_TO_CYCLES(tx), (unsigned int)TICKS_TO_OFFSET(tx)); #endif return scheduleStateTransition(ePS_WaitingForStreamEnable, tx); } bool StreamProcessor::scheduleStopDryRunning(int64_t t) { uint64_t tx; if (t < 0) { tx = addTicks(m_1394service.getCycleTimerTicks(), 2000 * TICKS_PER_CYCLE); } else { tx = t; } debugOutput(DEBUG_LEVEL_VERBOSE,"for %s SP (%p)\n", ePTToString(getType()), this); #ifdef DEBUG uint64_t now = m_1394service.getCycleTimerTicks(); debugOutput(DEBUG_LEVEL_VERBOSE," Now : %011" PRIu64 " (%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 : %011" PRIu64 " (%03us %04uc %04ut)\n", tx, (unsigned int)TICKS_TO_SECS(tx), (unsigned int)TICKS_TO_CYCLES(tx), (unsigned int)TICKS_TO_OFFSET(tx)); #endif return scheduleStateTransition(ePS_Stopped, tx); } bool StreamProcessor::scheduleStopRunning(int64_t t) { uint64_t tx; if (t < 0) { tx = addTicks(m_1394service.getCycleTimerTicks(), 2000 * TICKS_PER_CYCLE); } else { tx = t; } debugOutput(DEBUG_LEVEL_VERBOSE,"for %s SP (%p)\n", ePTToString(getType()), this); #ifdef DEBUG uint64_t now = m_1394service.getCycleTimerTicks(); debugOutput(DEBUG_LEVEL_VERBOSE," Now : %011" PRIu64 " (%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 : %011" PRIu64 " (%03us %04uc %04ut)\n", tx, (unsigned int)TICKS_TO_SECS(tx), (unsigned int)TICKS_TO_CYCLES(tx), (unsigned int)TICKS_TO_OFFSET(tx)); #endif return scheduleStateTransition(ePS_WaitingForStreamDisable, tx); } bool StreamProcessor::startDryRunning(int64_t t) { if(getState() == ePS_DryRunning) { // already in the correct state return true; } if(!scheduleStartDryRunning(t)) { debugError("Could not schedule transition\n"); return false; } if(!waitForState(ePS_DryRunning, 2000)) { debugError(" Timeout while waiting for %s\n", ePSToString(ePS_DryRunning)); return false; } return true; } bool StreamProcessor::startRunning(int64_t t) { if(getState() == ePS_Running) { // already in the correct state return true; } if(!scheduleStartRunning(t)) { debugError("Could not schedule transition\n"); return false; } if(!waitForState(ePS_Running, 2000)) { debugError(" Timeout while waiting for %s\n", ePSToString(ePS_Running)); return false; } return true; } bool StreamProcessor::stopDryRunning(int64_t t) { if(getState() == ePS_Stopped) { // already in the correct state return true; } if(!scheduleStopDryRunning(t)) { debugError("Could not schedule transition\n"); return false; } if(!waitForState(ePS_Stopped, 2000)) { debugError(" Timeout while waiting for %s\n", ePSToString(ePS_Stopped)); return false; } return true; } bool StreamProcessor::stopRunning(int64_t t) { if(getState() == ePS_DryRunning) { // already in the correct state return true; } if(!scheduleStopRunning(t)) { debugError("Could not schedule transition\n"); return false; } if(!waitForState(ePS_DryRunning, 2000)) { debugError(" Timeout while waiting for %s\n", ePSToString(ePS_DryRunning)); return false; } return true; } // internal state API /** * @brief Enter the ePS_Stopped state * @return true if successful, false if not * * @pre none * * @post the buffer and the isostream are ready for use. * @post all dynamic structures have been allocated successfully * @post the buffer is transparent and empty, and all parameters are set * to the correct initial/nominal values. * */ bool StreamProcessor::doStop() { assert(m_data_buffer); float ticks_per_frame; debugOutput(DEBUG_LEVEL_VERBOSE, "Enter from state: %s\n", ePSToString(m_state)); bool result = true; switch(m_state) { case ePS_Created: // prepare the framerate estimate ticks_per_frame = (TICKS_PER_SECOND*1.0) / ((float)m_StreamProcessorManager.getNominalRate()); m_ticks_per_frame = ticks_per_frame; m_local_node_id= m_1394service.getLocalNodeId() & 0x3f; m_correct_last_timestamp = false; debugOutput(DEBUG_LEVEL_VERBOSE, "Initializing remote ticks/frame to %f\n", ticks_per_frame); result &= setupDataBuffer(); 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; } // clear all data result &= m_data_buffer->clearBuffer(); // make the buffer transparent m_data_buffer->setTransparent(true); // reset all ports result &= PortManager::preparePorts(); m_state = ePS_Stopped; #ifdef DEBUG if (getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { debugOutput(DEBUG_LEVEL_VERBOSE, "State switch complete, dumping SP info...\n"); dumpInfo(); } #endif SIGNAL_ACTIVITY_ALL; return result; } /** * @brief Enter the ePS_WaitingForStream state * @return true if successful, false if not * * @pre all dynamic data structures are allocated successfully * * @post * */ bool StreamProcessor::doWaitForRunningStream() { debugOutput(DEBUG_LEVEL_VERBOSE, "Enter from state: %s\n", ePSToString(m_state)); switch(m_state) { case ePS_Stopped: // we have to start waiting for an incoming stream // this basically means nothing, the state change will // be picked up by the packet iterator break; default: debugError("Entry from invalid state: %s\n", ePSToString(m_state)); return false; } m_state = ePS_WaitingForStream; #ifdef DEBUG if (getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { debugOutput(DEBUG_LEVEL_VERBOSE, "State switch complete, dumping SP info...\n"); dumpInfo(); } #endif SIGNAL_ACTIVITY_ALL; return true; } /** * @brief Enter the ePS_DryRunning state * @return true if successful, false if not * * @pre * * @post * */ bool StreamProcessor::doDryRunning() { bool result = true; debugOutput(DEBUG_LEVEL_VERBOSE, "Enter from state: %s\n", ePSToString(m_state)); switch(m_state) { case ePS_WaitingForStream: // a running stream has been detected debugOutput(DEBUG_LEVEL_VERBOSE, "StreamProcessor %p started dry-running\n", this); m_local_node_id = m_1394service.getLocalNodeId() & 0x3f; if (getType() == ePT_Receive) { // this to ensure that there is no discontinuity when starting to // update the DLL based upon the received packets m_data_buffer->setBufferTailTimestamp(m_last_timestamp); } else { // FIXME: PC=master mode will have to do something here I guess... } break; case ePS_WaitingForStreamEnable: // when xrunning at startup result &= m_data_buffer->clearBuffer(); m_data_buffer->setTransparent(true); break; case ePS_WaitingForStreamDisable: result &= m_data_buffer->clearBuffer(); m_data_buffer->setTransparent(true); // If the stream has been running, clear the previous timestamps // as they won't be relevant after a restart m_last_timestamp = m_last_timestamp2 = 0; break; default: debugError("Entry from invalid state: %s\n", ePSToString(m_state)); return false; } m_state = ePS_DryRunning; #ifdef DEBUG if (getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { debugOutput(DEBUG_LEVEL_VERBOSE, "State switch complete, dumping SP info...\n"); dumpInfo(); } #endif SIGNAL_ACTIVITY_ALL; return result; } /** * @brief Enter the ePS_WaitingForStreamEnable state * @return true if successful, false if not * * @pre * * @post * */ bool StreamProcessor::doWaitForStreamEnable() { debugOutput(DEBUG_LEVEL_VERBOSE, "Enter from state: %s\n", ePSToString(m_state)); unsigned int ringbuffer_size_frames = m_StreamProcessorManager.getNbBuffers() * m_StreamProcessorManager.getPeriodSize(); ringbuffer_size_frames += m_extra_buffer_frames; ringbuffer_size_frames += 1; // to ensure that we can fit it all in there 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 // clear the buffer / resize it to the most recent // size setting if(!m_data_buffer->resizeBuffer(ringbuffer_size_frames)) { debugError("Could not resize data buffer\n"); return false; } if (getType() == ePT_Transmit) { ringbuffer_size_frames = m_StreamProcessorManager.getNbBuffers() * m_StreamProcessorManager.getPeriodSize(); ringbuffer_size_frames += m_extra_buffer_frames; debugOutput(DEBUG_LEVEL_VERBOSE, "Prefill transmit SP %p with %u frames (xmit prebuffer = %d)\n", this, ringbuffer_size_frames, m_extra_buffer_frames); // prefill the buffer if(!transferSilence(ringbuffer_size_frames)) { debugFatal("Could not prefill transmit stream\n"); return false; } } break; default: debugError("Entry from invalid state: %s\n", ePSToString(m_state)); return false; } m_state = ePS_WaitingForStreamEnable; #ifdef DEBUG if (getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { debugOutput(DEBUG_LEVEL_VERBOSE, "State switch complete, dumping SP info...\n"); dumpInfo(); } #endif SIGNAL_ACTIVITY_ALL; return true; } /** * @brief Enter the ePS_Running state * @return true if successful, false if not * * @pre * * @post * */ bool StreamProcessor::doRunning() { bool result = true; debugOutput(DEBUG_LEVEL_VERBOSE, "Enter from state: %s\n", ePSToString(m_state)); switch(m_state) { case ePS_WaitingForStreamEnable: // a running stream has been detected debugOutput(DEBUG_LEVEL_VERBOSE, "StreamProcessor %p started running\n", this); m_in_xrun = false; m_local_node_id = m_1394service.getLocalNodeId() & 0x3f; // reduce the DLL bandwidth to what we require result &= m_data_buffer->setBandwidth(m_dll_bandwidth_hz / (double)TICKS_PER_SECOND); // enable the data buffer m_data_buffer->setTransparent(false); m_last_timestamp2 = 0; // NOTE: no use in checking if we just started running break; default: debugError("Entry from invalid state: %s\n", ePSToString(m_state)); return false; } m_state = ePS_Running; #ifdef DEBUG if (getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { debugOutput(DEBUG_LEVEL_VERBOSE, "State switch complete, dumping SP info...\n"); dumpInfo(); } #endif SIGNAL_ACTIVITY_ALL; return result; } /** * @brief Enter the ePS_WaitingForStreamDisable state * @return true if successful, false if not * * @pre * * @post * */ bool StreamProcessor::doWaitForStreamDisable() { debugOutput(DEBUG_LEVEL_VERBOSE, "Enter from state: %s\n", ePSToString(m_state)); switch(m_state) { case ePS_Running: // the thread will do the transition break; default: debugError("Entry from invalid state: %s\n", ePSToString(m_state)); return false; } m_state = ePS_WaitingForStreamDisable; #ifdef DEBUG if (getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { debugOutput(DEBUG_LEVEL_VERBOSE, "State switch complete, dumping SP info...\n"); dumpInfo(); } #endif SIGNAL_ACTIVITY_ALL; return true; } /** * @brief Updates the state machine and calls the necessary transition functions * @return true if successful, false if not */ bool StreamProcessor::updateState() { bool result = false; // copy the current state locally since it could change value, // and that's something we don't want to happen inbetween tests // if m_next_state changes during this routine, we know for sure // that the previous state change was at least attempted correctly. enum eProcessorState next_state = m_next_state; debugOutput(DEBUG_LEVEL_VERBOSE, "Do state transition: %s => %s\n", ePSToString(m_state), ePSToString(next_state)); if (m_state == next_state) { debugWarning("ignoring identity state update from/to %s\n", ePSToString(m_state) ); return true; } // after creation, only initialization is allowed if (m_state == ePS_Created) { if(next_state != ePS_Stopped) { goto updateState_exit_with_error; } // do init here result = doStop(); if (result) {return true;} else goto updateState_exit_change_failed; } // after initialization, only WaitingForRunningStream is allowed if (m_state == ePS_Stopped) { if(next_state != ePS_WaitingForStream) { goto updateState_exit_with_error; } result = doWaitForRunningStream(); if (result) {return true;} else goto updateState_exit_change_failed; } // after WaitingForStream, only ePS_DryRunning is allowed // this means that the stream started running if (m_state == ePS_WaitingForStream) { if(next_state != ePS_DryRunning) { goto updateState_exit_with_error; } result = doDryRunning(); if (result) {return true;} else goto updateState_exit_change_failed; } // from ePS_DryRunning we can go to: // - ePS_Stopped if something went wrong during DryRunning // - ePS_WaitingForStreamEnable if there is a requested to enable if (m_state == ePS_DryRunning) { if((next_state != ePS_Stopped) && (next_state != ePS_WaitingForStreamEnable)) { goto updateState_exit_with_error; } if (next_state == ePS_Stopped) { result = doStop(); } else { result = doWaitForStreamEnable(); } if (result) {return true;} else goto updateState_exit_change_failed; } // from ePS_WaitingForStreamEnable we can go to: // - ePS_DryRunning if something went wrong while waiting // - ePS_Running if the stream enabled correctly if (m_state == ePS_WaitingForStreamEnable) { if((next_state != ePS_DryRunning) && (next_state != ePS_Running)) { goto updateState_exit_with_error; } if (next_state == ePS_DryRunning) { result = doDryRunning(); } else { result = doRunning(); } if (result) {return true;} else goto updateState_exit_change_failed; } // from ePS_Running we can only start waiting for a disabled stream if (m_state == ePS_Running) { if(next_state != ePS_WaitingForStreamDisable) { goto updateState_exit_with_error; } result = doWaitForStreamDisable(); if (result) {return true;} else goto updateState_exit_change_failed; } // from ePS_WaitingForStreamDisable we can go to DryRunning if (m_state == ePS_WaitingForStreamDisable) { if(next_state != ePS_DryRunning) { goto updateState_exit_with_error; } result = doDryRunning(); if (result) {return true;} else goto updateState_exit_change_failed; } // if we arrive here there is an error updateState_exit_with_error: debugError("Invalid state transition: %s => %s\n", ePSToString(m_state), ePSToString(next_state)); SIGNAL_ACTIVITY_ALL; return false; updateState_exit_change_failed: debugError("State transition failed: %s => %s\n", ePSToString(m_state), ePSToString(next_state)); SIGNAL_ACTIVITY_ALL; return false; } bool StreamProcessor::canProducePacket() { return canProduce(getNominalFramesPerPacket()); } bool StreamProcessor::canProducePeriod() { return canProduce(m_StreamProcessorManager.getPeriodSize()); } bool StreamProcessor::canProduce(unsigned int nframes) { if(m_in_xrun) return true; if(m_state == ePS_Running && m_next_state == ePS_Running) { // can we put a certain amount of frames into the buffer? unsigned int bufferspace = m_data_buffer->getBufferSpace(); if(bufferspace >= nframes) { return true; } else return false; } else { if(getType() == ePT_Transmit) { // if we are an xmit SP, we cannot accept frames // when not running return false; } else { // if we are a receive SP, we can always accept frames // when not running return true; } } } bool StreamProcessor::canConsumePacket() { return canConsume(getNominalFramesPerPacket()); } bool StreamProcessor::canConsumePeriod() { return canConsume(m_StreamProcessorManager.getPeriodSize()); } bool StreamProcessor::canConsume(unsigned int nframes) { if(m_in_xrun) return true; if(m_state == ePS_Running && m_next_state == ePS_Running) { // check whether we already fullfil the criterion unsigned int bufferfill = m_data_buffer->getBufferFill(); if(bufferfill >= nframes) { return true; } else return false; } else { if(getType() == ePT_Transmit) { // if we are an xmit SP, and we're not running, // we can always provide frames return true; } else { // if we are a receive SP, we can't provide frames // when not running return false; } } } /*********************************************** * Helper routines * ***********************************************/ // FIXME: I think this can be removed and replaced by putSilenceFrames bool StreamProcessor::transferSilence(unsigned int nframes) { bool retval; #ifdef DEBUG signed int fc; ffado_timestamp_t ts_tail_tmp; m_data_buffer->getBufferTailTimestamp(&ts_tail_tmp, &fc); if (fc != 0) { debugWarning("Prefilling a buffer that already contains %d frames\n", fc); } #endif // prepare a buffer of silence char *dummybuffer = (char *)calloc(getEventSize(), nframes * getEventsPerFrame()); transmitSilenceBlock(dummybuffer, nframes, 0); // add the silence data to the ringbuffer if(m_data_buffer->preloadFrames(nframes, dummybuffer, true)) { retval = true; } else { debugWarning("Could not write to event buffer\n"); retval = false; } free(dummybuffer); return retval; } /** * @brief convert a eProcessorState to a string * @param s the state * @return a char * describing the state */ const char * StreamProcessor::ePSToString(enum eProcessorState s) { switch (s) { case ePS_Invalid: return "ePS_Invalid"; case ePS_Created: return "ePS_Created"; case ePS_Stopped: return "ePS_Stopped"; case ePS_WaitingForStream: return "ePS_WaitingForStream"; case ePS_DryRunning: return "ePS_DryRunning"; case ePS_WaitingForStreamEnable: return "ePS_WaitingForStreamEnable"; case ePS_Running: return "ePS_Running"; case ePS_WaitingForStreamDisable: return "ePS_WaitingForStreamDisable"; case ePS_Error: return "ePS_Error"; default: return "error: unknown state"; } } /** * @brief convert a eProcessorType to a string * @param t the type * @return a char * describing the state */ const char * StreamProcessor::ePTToString(enum eProcessorType t) { switch (t) { case ePT_Receive: return "Receive"; case ePT_Transmit: return "Transmit"; default: return "error: unknown type"; } } /*********************************************** * Debug * ***********************************************/ void StreamProcessor::dumpInfo() { #ifdef DEBUG debugOutputShort( DEBUG_LEVEL_NORMAL, " StreamProcessor %p, %s:\n", this, ePTToString(m_processor_type)); debugOutputShort( DEBUG_LEVEL_NORMAL, " Port, Channel : %d, %d\n", m_1394service.getPort(), m_channel); m_IsoHandlerManager.dumpInfoForStream(this); uint64_t now = m_1394service.getCycleTimerTicks(); debugOutputShort( DEBUG_LEVEL_NORMAL, " Now : %011" PRIu64 " (%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()); #endif m_data_buffer->dumpInfo(); } void StreamProcessor::printBufferInfo() { debugOutput(DEBUG_LEVEL_NORMAL, "(%p, %8s) fc: %d fill: %u\n", this, getTypeString(), m_data_buffer->getFrameCounter(), m_data_buffer->getBufferFill() ); } void StreamProcessor::setVerboseLevel(int l) { setDebugLevel(l); PortManager::setVerboseLevel(l); m_data_buffer->setVerboseLevel(l); debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); } } // end of namespace libffado-2.4.5/src/libstreaming/generic/StreamProcessor.h0000644000175000001440000004141514206145246023060 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_STREAMPROCESSOR__ #define __FFADO_STREAMPROCESSOR__ #include "ffadodevice.h" #include "PortManager.h" #include "libutil/StreamStatistics.h" #include "libutil/TimestampedBuffer.h" #include "libutil/OptionContainer.h" #include "debugmodule/debugmodule.h" #include class Ieee1394Service; class IsoHandlerManager; namespace Streaming { class StreamProcessorManager; /*! \brief Class providing a generic interface for Stream Processors A stream processor multiplexes or demultiplexes an ISO stream into a collection of ports. This class should be subclassed, and the relevant functions should be overloaded. */ class StreamProcessor : public PortManager, public Util::TimestampedBufferClient, public Util::OptionContainer { public: ///> the streamprocessor type enum eProcessorType { ePT_Receive, ePT_Transmit }; ///> returns the type of the streamprocessor virtual enum eProcessorType getType() { return m_processor_type; }; ///> notification of a buffer size change virtual bool periodSizeChanged(unsigned int new_periodsize); private: // this can only be set by the constructor enum eProcessorType m_processor_type; // pretty printing const char *ePTToString(enum eProcessorType); protected: ///> the state the streamprocessor is in enum eProcessorState { ePS_Invalid, ePS_Created, ePS_Stopped, ePS_WaitingForStream, ePS_DryRunning, ePS_WaitingForStreamEnable, ePS_Running, ePS_WaitingForStreamDisable, ePS_Error, }; ///> set the SP state to a specific value void setState(enum eProcessorState); ///> get the SP state enum eProcessorState getState() {return m_state;}; private: enum eProcessorState m_state; // state switching enum eProcessorState m_next_state; unsigned int m_cycle_to_switch_state; bool updateState(); // pretty printing const char *ePSToString(enum eProcessorState); bool doStop(); bool doWaitForRunningStream(); bool doDryRunning(); bool doWaitForStreamEnable(); bool doRunning(); bool doWaitForStreamDisable(); bool scheduleStateTransition(enum eProcessorState state, uint64_t time_instant); bool waitForState(enum eProcessorState state, unsigned int timeout); public: //--- state stuff bool isRunning() {return m_state == ePS_Running;}; bool isDryRunning() {return m_state == ePS_DryRunning;}; bool isStopped() {return m_state == ePS_Stopped;}; bool isWaitingForStream() {return m_state == ePS_WaitingForStream;}; bool inError() {return m_state == ePS_Error;}; // 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(); bool handleBusReset(); // the one to be implemented by the child class virtual bool handleBusResetDo(); FFADODevice& getParent() {return m_Parent;}; public: // constructor/destructor StreamProcessor(FFADODevice &parent, enum eProcessorType type); virtual ~StreamProcessor(); protected: FFADODevice& m_Parent; Ieee1394Service& m_1394service; IsoHandlerManager& m_IsoHandlerManager; StreamProcessorManager& m_StreamProcessorManager; unsigned int m_local_node_id; public: // the public receive/transmit functions // the transmit interface accepts frames and provides packets // implement these for a transmit SP // leave default for a receive SP // the receive interface accepts packets and provides frames // these are implemented by the parent SP enum raw1394_iso_disposition putPacket(unsigned char *data, unsigned int length, unsigned char channel, unsigned char tag, unsigned char sy, uint32_t pkt_ctr, unsigned int dropped); enum raw1394_iso_disposition getPacket(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr, unsigned int dropped, unsigned int skipped, unsigned int max_length); bool getFrames(unsigned int nbframes, int64_t ts); ///< transfer the buffer contents to the client bool putFrames(unsigned int nbframes, int64_t ts); ///< transfer the client contents to the buffer void packetsStopped(); // Called once when the underlying stream has stopped, to update the state. bool canProducePacket(); bool canProducePeriod(); bool canProduce(unsigned int nframes); bool canConsumePacket(); bool canConsumePeriod(); bool canConsume(unsigned int nframes); public: /** * @brief drop nframes from the internal buffer as if they were transferred to the client side * * Gets nframes of frames from the buffer as done by getFrames(), but does not transfer them * to the client side. Instead they are discarded. * * @param nframes number of frames * @return true if the operation was successful */ bool dropFrames(unsigned int nframes, int64_t ts); /** * @brief put silence frames into the internal buffer * * Puts nframes of frames into the buffer as done by putFrames(), but does not transfer them * from the client side. Instead, silent frames are used. * * @param nframes number of frames * @return true if the operation was successful */ bool putSilenceFrames(unsigned int nbframes, int64_t ts); /** * @brief Shifts the stream with the specified number of frames * * Used to align several streams to each other. It comes down to * making sure the head timestamp corresponds to the timestamp of * one master stream * * @param nframes the number of frames to shift * @return true if successful */ bool shiftStream(int nframes); protected: // the helper receive/transmit functions enum eChildReturnValue { eCRV_OK, eCRV_Invalid, eCRV_Packet, eCRV_EmptyPacket, eCRV_XRun, eCRV_Again, eCRV_Defer, eCRV_Error, }; // to be implemented by the children // the following methods are to be implemented by receive SP subclasses virtual enum eChildReturnValue processPacketHeader(unsigned char *data, unsigned int length, unsigned char tag, unsigned char sy, uint32_t pkt_ctr) {debugWarning("call not allowed\n"); return eCRV_Invalid;}; virtual enum eChildReturnValue processPacketData(unsigned char *data, unsigned int length) {debugWarning("call not allowed\n"); return eCRV_Invalid;}; virtual bool processReadBlock(char *data, unsigned int nevents, unsigned int offset) {debugWarning("call not allowed\n"); return false;}; // the following methods are to be implemented by transmit SP subclasses virtual enum eChildReturnValue generatePacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr) {debugWarning("call not allowed\n"); return eCRV_Invalid;}; virtual enum eChildReturnValue generatePacketData(unsigned char *data, unsigned int *length) {debugWarning("call not allowed\n"); return eCRV_Invalid;}; virtual enum eChildReturnValue generateEmptyPacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr) {debugWarning("call not allowed\n"); return eCRV_Invalid;}; virtual enum eChildReturnValue generateEmptyPacketData(unsigned char *data, unsigned int *length) {debugWarning("call not allowed\n"); return eCRV_Invalid;}; virtual enum eChildReturnValue generateSilentPacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr) {debugWarning("call not allowed\n"); return eCRV_Invalid;}; virtual enum eChildReturnValue generateSilentPacketData(unsigned char *data, unsigned int *length) {debugWarning("call not allowed\n"); return eCRV_Invalid;}; virtual bool processWriteBlock(char *data, unsigned int nevents, unsigned int offset) {debugWarning("call not allowed\n"); return false;}; virtual bool transmitSilenceBlock(char *data, unsigned int nevents, unsigned int offset) {debugWarning("call not allowed\n"); return false;}; protected: // some generic helpers int provideSilenceToPort(Port *p, unsigned int offset, unsigned int nevents); bool provideSilenceBlock(unsigned int nevents, unsigned int offset); private: bool getFramesDry(unsigned int nbframes, int64_t ts); bool getFramesWet(unsigned int nbframes, int64_t ts); bool putFramesDry(unsigned int nbframes, int64_t ts); bool putFramesWet(unsigned int nbframes, int64_t ts); bool transferSilence(unsigned int size); public: // move to private? bool xrunOccurred() { return m_in_xrun; }; void handlerDied(); // the ISO interface (can we get rid of this?) public: int getChannel() {return m_channel;}; bool setChannel(int c) {m_channel = c; return true;}; virtual unsigned int getNbPacketsIsoXmitBuffer(); virtual unsigned int getPacketsPerPeriod(); virtual unsigned int getMaxPacketSize() = 0; private: int m_channel; protected: // FIXME: move to private uint64_t m_last_timestamp; /// last timestamp (in ticks) private: uint64_t m_last_timestamp2; /// last timestamp (in ticks) protected: bool m_correct_last_timestamp; uint64_t m_last_timestamp_at_period_ticks; // FIXME: still used? //--- data buffering and accounting public: bool setupDataBuffer(); void getBufferHeadTimestamp ( ffado_timestamp_t *ts, signed int *fc ); void getBufferTailTimestamp ( ffado_timestamp_t *ts, signed int *fc ); void setBufferTailTimestamp ( ffado_timestamp_t new_timestamp ); void setBufferHeadTimestamp ( ffado_timestamp_t 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 of the next period boundary (in internal units) * * Returns the time of the next period boundary, in internal units, i.e. * in ticks of the 1394 clock of the bus the device is attached to. * 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 internal units */ uint64_t getTimeAtPeriod(); /** * For RECEIVE: * this is the extra amount of space in the receive buffer * * For XMIT: * Sets the number of frames that should be prebuffered * into the ISO transmit buffers. A higher number here means * more reliable operation. It also means higher latency * * @param frames */ void setExtraBufferFrames(unsigned int frames); unsigned int getExtraBufferFrames(); /** * @brief get the maximal frame latency * * The maximum frame latency is the maximum time that will elapse * between the frame being received by the 1394 stack, and the moment this * frame is presented to the StreamProcessor. * * For transmit SP's this is the maximum time that a frame is requested by * the handler ahead of the time the frame is intended to be transmitted. * * This is useful to figure out how longer than the actual reception time * we have to wait before trying to read the frame from the SP. * * @return maximal frame latency */ int getMaxFrameLatency(); float getTicksPerFrame(); void setTicksPerFrame(float tpf); bool setDllBandwidth(float bw); int getBufferFill(); // Child implementation interface /** * @brief prepare the child SP * @return true if successful, false otherwise * @pre the m_manager pointer points to a valid manager * @post getEventsPerFrame() returns the correct value * @post getEventSize() returns the correct value * @post getUpdatePeriod() returns the correct value * @post processPacketHeader(...) can be called * @post processPacketData(...) can be called */ virtual bool prepareChild() = 0; /** * @brief get the number of events contained in one frame * @return the number of events contained in one frame */ virtual unsigned int getEventsPerFrame() = 0; /** * @brief get the size of one frame in bytes * @return the size of one frame in bytes */ virtual unsigned int getEventSize() = 0; /** * @brief get the nominal number of frames in a packet * * This is the amount of frames that is nominally present * in one packet. It is recommended that in the receive handler * you write this amount of frames when a valid packet has * been received. (although this is not mandatory) * * @return the nominal number of frames in a packet */ virtual unsigned int getNominalFramesPerPacket() = 0; /** * @brief get the nominal number of packets needed for a certain amount of frames * @return the nominal number of packet necessary */ virtual unsigned int getNominalPacketsNeeded(unsigned int nframes); protected: float m_ticks_per_frame; float m_dll_bandwidth_hz; unsigned int m_extra_buffer_frames; float m_max_fs_diff_norm; signed int m_max_diff_ticks; private: bool m_in_xrun; public: // debug stuff virtual void dumpInfo(); virtual void printBufferInfo(); virtual void setVerboseLevel(int l); const char *getStateString() {return ePSToString(getState());}; const char *getTypeString() {return ePTToString(getType());}; DECLARE_DEBUG_MODULE; }; } #endif /* __FFADO_STREAMPROCESSOR__ */ libffado-2.4.5/src/libstreaming/motu/0000755000175000001440000000000014206145612017114 5ustar jwoitheuserslibffado-2.4.5/src/libstreaming/motu/MotuPort.cpp0000644000175000001440000000172314206145246021417 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "MotuPort.h" #include namespace Streaming { } // end of namespace Streaming libffado-2.4.5/src/libstreaming/motu/MotuPort.h0000644000175000001440000000506414206145246021066 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_MOTUPORT__ #define __FFADO_MOTUPORT__ /** * This file implements the ports used in Motu devices */ #include "MotuPortInfo.h" #include "../generic/Port.h" #include "debugmodule/debugmodule.h" namespace Streaming { /*! \brief The Base Class for Motu Audio Port */ class MotuAudioPort : public AudioPort, public MotuPortInfo { public: MotuAudioPort(PortManager &m, std::string name, enum E_Direction direction, int position, int size) : AudioPort(m, name, direction), MotuPortInfo( position, size) // TODO: add more port information parameters here if nescessary {}; virtual ~MotuAudioPort() {}; }; /*! \brief The Base Class for an Motu Midi Port */ class MotuMidiPort : public MidiPort, public MotuPortInfo { public: MotuMidiPort(PortManager &m, std::string name, enum E_Direction direction, int position) : MidiPort(m, name, direction), MotuPortInfo(position, 0) // TODO: add more port information parameters here if nescessary {}; virtual ~MotuMidiPort() {}; }; /*! \brief The Base Class for an Motu Control Port */ class MotuControlPort : public ControlPort, public MotuPortInfo { public: MotuControlPort(PortManager &m, std::string name, enum E_Direction direction, int position) : ControlPort(m, name, direction), MotuPortInfo(position, 2) // TODO: add more port information parameters here if nescessary {}; virtual ~MotuControlPort() {}; }; } // end of namespace Streaming #endif /* __FFADO_MOTUPORT__ */ libffado-2.4.5/src/libstreaming/motu/MotuPortInfo.cpp0000644000175000001440000000170414206145246022232 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "MotuPortInfo.h" namespace Streaming { } // end of namespace Streaming libffado-2.4.5/src/libstreaming/motu/MotuPortInfo.h0000644000175000001440000000464514206145246021706 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_MOTUPORTINFO__ #define __FFADO_MOTUPORTINFO__ #include "debugmodule/debugmodule.h" #include namespace Streaming { /*! \brief Class containing the stream information for a Motu channel Contains the information that enables the decoding routine to find this port's data in the ISO events */ class MotuPortInfo { public: /** * Sometimes a channel can have multiple formats, depending on the * device configuration (e.g. an SPDIF port could be plain audio in 24bit integer * or AC3 passthrough in IEC compliant frames.) * * This kind of enum allows to discriminate these formats when decoding * If all channels always have the same format, you won't be needing this */ // enum E_Formats { // E_MBLA, // Multibit linear audio // E_Midi, // MIDI // }; /** * Initialize Motu portinfo * should not be called directly, is inherited by motu ports * * the position parameter is an example * the name parameter is mandatory * * @param position Start position of port's data in iso event * @param format Format of data in iso event * @param size Size in bits of port's data in iso event * @return */ MotuPortInfo( int position, int size) : m_position(position), m_size(size) {}; virtual ~MotuPortInfo() {}; int getPosition() {return m_position;}; int getSize() {return m_size;}; protected: int m_position; int m_size; }; } // end of namespace Streaming #endif /* __FFADO_MOTUPORTINFO__ */ libffado-2.4.5/src/libstreaming/motu/MotuReceiveStreamProcessor.cpp0000644000175000001440000005650214206145246025136 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "libutil/float_cast.h" #include "MotuReceiveStreamProcessor.h" #include "MotuPort.h" #include "../StreamProcessorManager.h" #include "devicemanager.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/IsoHandlerManager.h" #include "libieee1394/cycletimer.h" #include "libutil/ByteSwap.h" #include "../../motu/motu_avdevice.h" #include #include #include /* Provide more intuitive access to GCC's branch predition built-ins */ #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) namespace Streaming { // A macro to extract specific bits from a native endian quadlet #define get_bits(_d,_start,_len) (((_d)>>((_start)-(_len)+1)) & ((1<<(_len))-1)) // Convert an SPH timestamp as received from the MOTU to a full timestamp in ticks. static inline uint32_t sphRecvToFullTicks(uint32_t sph, uint32_t ct_now) { uint32_t timestamp = CYCLE_TIMER_TO_TICKS(sph & 0x1ffffff); uint32_t now_cycles = CYCLE_TIMER_GET_CYCLES(ct_now); uint32_t ts_sec = CYCLE_TIMER_GET_SECS(ct_now); // If the cycles have wrapped, correct ts_sec so it represents when timestamp // was received. The timestamps sent by the MOTU are always 1 or two cycles // in advance of the cycle timer (reasons unknown at this stage). In addition, // iso buffering can delay the arrival of packets for quite a number of cycles // (have seen a delay >12 cycles). // Every so often we also see sph wrapping ahead of ct_now, so deal with that // too. if (unlikely(CYCLE_TIMER_GET_CYCLES(sph) > now_cycles + 1000)) { if (likely(ts_sec)) ts_sec--; else ts_sec = 127; } else if (unlikely(now_cycles > CYCLE_TIMER_GET_CYCLES(sph) + 1000)) { if (unlikely(ts_sec == 127)) ts_sec = 0; else ts_sec++; } return timestamp + ts_sec*TICKS_PER_SECOND; } MotuReceiveStreamProcessor::MotuReceiveStreamProcessor(FFADODevice &parent, unsigned int event_size) : StreamProcessor(parent, ePT_Receive) , m_event_size( event_size ) , mb_head ( 0 ) , mb_tail ( 0 ) { // The model needs to be easily visible since the interpretation of the // receive stream is dependent on the model in a slight but important // way. Motu::MotuDevice *dev = static_cast(&parent); m_motu_model = dev->m_motu_model; memset(&m_devctrls, 0, sizeof(m_devctrls)); } unsigned int MotuReceiveStreamProcessor::getMaxPacketSize() { int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); // What's returned here is the maximum packet size seen at the current // frame rate. This depends both on the device and its configuration, // and is futher complicated by the IN/OUT channel asymmetry in some // devices. To avoid most of these complications, just return the // largest packet sizes seen by any supported MOTU device. Presently // all these sizes come from the receive side of the 828mk3. // Previously the figures came from the Traveler (I think). return framerate<=48000?904:(framerate<=96000?1416:1672); } 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); return true; } /** * Processes packet header to extract timestamps and check if the packet is valid * @param data * @param length * @param channel * @param tag * @param sy * @param cycle * @return */ enum StreamProcessor::eChildReturnValue MotuReceiveStreamProcessor::processPacketHeader(unsigned char *data, unsigned int length, unsigned char tag, unsigned char sy, uint32_t pkt_ctr) { static int len_shown = 0; static int ts_print_cx = 0; static int pktcx = 0; if (length > 8) { // The iso data blocks from the MOTUs comprise a CIP-like // header followed by a number of events (8 for 1x rates, 16 // for 2x rates, 32 for 4x rates). quadlet_t *quadlet = (quadlet_t *)data; unsigned int dbs = get_bits(CondSwapFromBus32(quadlet[0]), 23, 8); // Size of one event in terms of fdf_size unsigned int fdf_size = get_bits(CondSwapFromBus32(quadlet[1]), 23, 8) == 0x22 ? 32:0; // Event unit size in bits // Don't even attempt to process a packet if it isn't what we expect // from a MOTU. Yes, an FDF value of 32 bears little relationship // to the actual data (24 bit integer) sent by the MOTU - it's one // of those areas where MOTU have taken a curious detour around the // standards. Do this check early on because for invalid packets // dbs may not be what we expect, potentially causing issues later // on. // // Mark 3 devices are a little different. Previous generations set // the fdf_size to 32. Mark 3 devices appear to set fdf_size to 0. // Allow for this at present. However, if too many variations // appear we might have to omit this test in the interests of // brevity. if (tag!=1 || (fdf_size!=32 && fdf_size!=0) || dbs==0) { return eCRV_Invalid; } // m_event_size is the event size in bytes unsigned int n_events = (length-8) / m_event_size; // Acquire the timestamp of the last frame in the packet just // received. Since every frame from the MOTU has its own timestamp // we can just pick it straight from the packet. uint32_t last_sph = CondSwapFromBus32(*(quadlet_t *)(data+8+(n_events-1)*m_event_size)); m_last_timestamp = sphRecvToFullTicks(last_sph, m_Parent.get1394Service().getCycleTimer()); // To assist in debugging the packet format from new devices, periodically // dump a packet when debug is active. Originally written to debug the // Ultralite mk3 it has been retained since it could prove useful in // future. if ((!len_shown || pktcx==0) && getDebugLevel()>0 ) { unsigned int i; fprintf(stderr, "Packet from MOTU: length=%d, eventsize=%d, n_events=%d\n", length, m_event_size, n_events); for (i=0; i0 ) { debugOutput(DEBUG_LEVEL_VERBOSE,"last ts=0x%08x\n", last_sph); ts_print_cx++; } return eCRV_OK; } else { return eCRV_Invalid; } } /** * extract the data from the packet * @pre the IEC61883 packet is valid according to isValidPacket * @param data * @param length * @param channel * @param tag * @param sy * @param pkt_ctr * @return */ enum StreamProcessor::eChildReturnValue MotuReceiveStreamProcessor::processPacketData(unsigned char *data, unsigned int length) { // m_event_size should never be zero unsigned int n_events = (length-8) / m_event_size; // 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: %" PRIu64 "ticks | tpf=%f\n", m_last_timestamp, getTicksPerFrame()); } #endif if(m_data_buffer->writeFrames(n_events, (char *)(data+8), m_last_timestamp)) { return eCRV_OK; } else { return eCRV_XRun; } } /*********************************************** * Encoding/Decoding API * ***********************************************/ /** * \brief write received events to the port ringbuffers. */ bool MotuReceiveStreamProcessor::processReadBlock(char *data, unsigned int nevents, unsigned int offset) { bool no_problem=true; /* Scan incoming block for device control events. These do not seem to * be present on the 828Mk1 (or at the very least are in a different * location). */ if (m_motu_model != Motu::MOTU_MODEL_828MkI) decodeMotuCtrlEvents(data, nevents); for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { if((*it)->isDisabled()) {continue;}; Port *port=(*it); switch(port->getPortType()) { case Port::E_Audio: if(decodeMotuEventsToPort(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not decode packet data to port %s\n",(*it)->getName().c_str()); no_problem=false; } break; case Port::E_Midi: if(decodeMotuMidiEventsToPort(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not decode packet midi data to port %s\n",(*it)->getName().c_str()); no_problem=false; } break; default: // ignore break; } } return no_problem; } signed int MotuReceiveStreamProcessor::decodeMotuEventsToPort(MotuAudioPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j=0; // Use char here since a port's source address won't necessarily be // aligned; use of an unaligned quadlet_t may cause issues on // certain architectures. Besides, the source (data coming directly // from the MOTU) isn't structured in quadlets anyway; it mainly // consists of packed 24-bit integers. unsigned char *src_data; src_data = (unsigned char *)data + p->getPosition(); switch(m_StreamProcessorManager.getAudioDataType()) { default: case StreamProcessorManager::eADT_Int24: { quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); // Offset is in frames, but each port is only a single // channel, so the number of frames is the same as the // number of quadlets to offset (assuming the port buffer // uses one quadlet per sample, which is the case currently). buffer+=offset; for(j = 0; j < nevents; j += 1) { // Decode nsamples *buffer = (*src_data<<16)+(*(src_data+1)<<8)+*(src_data+2); // Sign-extend highest bit of 24-bit int. // This isn't strictly needed since E_Int24 is a 24-bit, // but doing so shouldn't break anything and makes the data // easier to deal with during debugging. if (*src_data & 0x80) *buffer |= 0xff000000; buffer++; src_data+=m_event_size; } } break; case StreamProcessorManager::eADT_Float: { const float multiplier = 1.0f / (float)(0x7FFFFF); float *buffer=(float *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); buffer+=offset; for(j = 0; j < nevents; j += 1) { // decode max nsamples signed int v = (*src_data<<16)+(*(src_data+1)<<8)+*(src_data+2); /* Sign-extend highest bit of incoming 24-bit integer */ if (*src_data & 0x80) v |= 0xff000000; *buffer = v * multiplier; buffer++; src_data+=m_event_size; } } break; } return 0; } int MotuReceiveStreamProcessor::decodeMotuMidiEventsToPort( MotuMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j = 0; unsigned char *src = NULL; quadlet_t *buffer = (quadlet_t *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); buffer += offset; // Zero the buffer memset(buffer, 0, nevents*sizeof(*buffer)); // Get MIDI bytes if present in any frames within the packet. MOTU MIDI // data is sent as part of a 3-byte sequence starting at the port's // position. Some MOTUs (eg: the 828MkII) send more than one MIDI byte // in some packets. Since the FFADO MIDI layer requires a MIDI byte in // only every 8th buffer position we allow for this by buffering the // incoming data. The buffer is small since it only has to cover for // short-term excursions in the data rate. Since the MIDI data // originates on a physical MIDI bus the overall data rate is limited by // the baud rate of that bus (31250), which is no more than one byte in // 8 even for 1x sample rates. src = (unsigned char *)data + p->getPosition(); // We assume that the buffer has been set up in such a way that the first // element is correctly aligned for FFADOs MIDI layer. The requirement // is that actual MIDI bytes must be aligned to multiples of 8 samples. while (j < nevents) { /* Most events don't have MIDI data bytes */ if (unlikely((*src & MOTU_KEY_MASK_MIDI) == MOTU_KEY_MASK_MIDI)) { // A MIDI byte is in *(src+2). Bit 24 is used to flag MIDI data // as present once the data makes it to the output buffer. midibuffer[mb_head++] = 0x01000000 | *(src+2); mb_head &= RX_MIDIBUFFER_SIZE-1; if (unlikely(mb_head == mb_tail)) { debugWarning("MOTU rx MIDI buffer overflow\n"); /* Dump oldest byte. This overflow can only happen if the * rate coming in from the hardware MIDI port grossly * exceeds the official MIDI baud rate of 31250 bps, so it * should never occur in practice. */ mb_tail = (mb_tail + 1) & (RX_MIDIBUFFER_SIZE-1); } } /* Write to the buffer if we're at an 8-sample boundary */ if (unlikely(!(j & 0x07))) { if (mb_head != mb_tail) { *buffer = midibuffer[mb_tail++]; mb_tail &= RX_MIDIBUFFER_SIZE-1; } buffer += 8; } j++; src += m_event_size; } return 0; } int MotuReceiveStreamProcessor::decodeMotuCtrlEvents( char *data, unsigned int nevents) { unsigned int j = 0; unsigned char *src = NULL; unsigned char *arg = NULL; // Get control bytes if present in any frames within the packet. The // device control messages start at (zero-based) byte 0x04 in the data // stream. src = (unsigned char *)data + 0x04; arg = src+1; while (j < nevents) { unsigned int control_key = *src & ~MOTU_KEY_MASK_MIDI; if (m_devctrls.status == MOTU_DEVCTRL_INVALID) { // Start syncing on reception of the sequence sync key which indicates // mix bus 1 values are pending. Acquisition will start when we see the // first channel gain key after this. if (control_key==MOTU_KEY_SEQ_SYNC && *arg==MOTU_KEY_SEQ_SYNC_MIXBUS1) { debugOutput(DEBUG_LEVEL_VERBOSE, "syncing device control status stream\n"); m_devctrls.status = MOTU_DEVCTRL_SYNCING; } } else if (m_devctrls.status == MOTU_DEVCTRL_SYNCING) { // Start acquiring when we see a channel gain key for mixbus 1. if (control_key == MOTU_KEY_SEQ_SYNC) { // Keep mixbus index updated since we don't execute the main parser until // we move to the initialising state. Since we don't dereference this until // we know it's equal to 0 there's no need for bounds checking here. m_devctrls.mixbus_index = *arg; } else if (control_key==MOTU_KEY_CHANNEL_GAIN && m_devctrls.mixbus_index==0) { debugOutput(DEBUG_LEVEL_VERBOSE, "initialising device control status\n"); m_devctrls.status = MOTU_DEVCTRL_INIT; } } else if (m_devctrls.status == MOTU_DEVCTRL_INIT) { // Consider ourselves fully initialised when a control sequence sync key // arrives which takes things back to mixbus 1. if (control_key==MOTU_KEY_SEQ_SYNC && *arg==MOTU_KEY_SEQ_SYNC_MIXBUS1 && m_devctrls.mixbus_index>0) { debugOutput(DEBUG_LEVEL_VERBOSE, "device control status valid: n_mixbuses=%d, n_channels=%d\n", m_devctrls.n_mixbuses, m_devctrls.n_channels); m_devctrls.status = MOTU_DEVCTRL_VALID; } } if (m_devctrls.status==MOTU_DEVCTRL_INIT || m_devctrls.status==MOTU_DEVCTRL_VALID) { unsigned int i; switch (control_key) { case MOTU_KEY_SEQ_SYNC: if (m_devctrls.mixbus_index < MOTUFW_MAX_MIXBUSES) { if (m_devctrls.n_channels==0 && m_devctrls.mixbus[m_devctrls.mixbus_index].channel_gain_index!=0) { m_devctrls.n_channels = m_devctrls.mixbus[m_devctrls.mixbus_index].channel_gain_index; } } /* Mix bus to configure next is in bits 5-7 of the argument */ m_devctrls.mixbus_index = (*arg >> 5); if (m_devctrls.mixbus_index >= MOTUFW_MAX_MIXBUSES) { debugWarning("MOTU cuemix value parser error: mix bus index %d exceeded maximum %d\n", m_devctrls.mixbus_index, MOTUFW_MAX_MIXBUSES); } else { if (m_devctrls.n_mixbuses < m_devctrls.mixbus_index+1) { m_devctrls.n_mixbuses = m_devctrls.mixbus_index+1; } m_devctrls.mixbus[m_devctrls.mixbus_index].channel_gain_index = m_devctrls.mixbus[m_devctrls.mixbus_index].channel_pan_index = m_devctrls.mixbus[m_devctrls.mixbus_index].channel_control_index = 0; } break; case MOTU_KEY_CHANNEL_GAIN: i = m_devctrls.mixbus[m_devctrls.mixbus_index].channel_gain_index++; if (m_devctrls.mixbus_index= MOTUFW_MAX_MIXBUS_CHANNELS) { debugWarning("MOTU cuemix value parser error: channel gain index %d exceeded maximum %d\n", i, MOTUFW_MAX_MIXBUS_CHANNELS); } break; case MOTU_KEY_CHANNEL_PAN: i = m_devctrls.mixbus[m_devctrls.mixbus_index].channel_pan_index++; if (m_devctrls.mixbus_index= MOTUFW_MAX_MIXBUS_CHANNELS) { debugWarning("MOTU cuemix value parser error: channel pan index %d exceeded maximum %d\n", i, MOTUFW_MAX_MIXBUS_CHANNELS); } break; case MOTU_KEY_CHANNEL_CTRL: i = m_devctrls.mixbus[m_devctrls.mixbus_index].channel_control_index++; if (m_devctrls.mixbus_index= MOTUFW_MAX_MIXBUS_CHANNELS) { debugWarning("MOTU cuemix value parser error: channel control index %d exceeded maximum %d\n", i, MOTUFW_MAX_MIXBUS_CHANNELS); } break; case MOTU_KEY_MIXBUS_GAIN: if (m_devctrls.mixbus_index < MOTUFW_MAX_MIXBUSES) { m_devctrls.mixbus[m_devctrls.mixbus_index].bus_gain = *arg; } break; case MOTU_KEY_MIXBUS_DEST: if (m_devctrls.mixbus_index < MOTUFW_MAX_MIXBUSES) { m_devctrls.mixbus[m_devctrls.mixbus_index].bus_dest = *arg; } break; case MOTU_KEY_MAINOUT_VOL: m_devctrls.main_out_volume = *arg; break; case MOTU_KEY_PHONES_VOL: m_devctrls.phones_volume = *arg; break; case MOTU_KEY_PHONES_DEST: m_devctrls.phones_assign = *arg; break; case MOTU_KEY_INPUT_6dB_BOOST: m_devctrls.input_6dB_boost = *arg; break; case MOTU_KEY_INPUT_REF_LEVEL: m_devctrls.input_ref_level = *arg; break; case MOTU_KEY_MIDI: // MIDI is dealt with elsewhere, so just pass it over break; default: // Uncomment to debug unknown keys // debugOutput(DEBUG_LEVEL_VERBOSE, "Unknown MOTU key %d, value %d\n", // control_key, (signed int)(*arg)); // Ignore any unknown keys or those we don't care about, at // least for now. break; } } j++; src += m_event_size; arg += m_event_size; } return 0; } } // end of namespace Streaming libffado-2.4.5/src/libstreaming/motu/MotuReceiveStreamProcessor.h0000644000175000001440000001453614206145246024604 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_MOTURECEIVESTREAMPROCESSOR__ #define __FFADO_MOTURECEIVESTREAMPROCESSOR__ /** * This class implements MOTU streaming */ #include "debugmodule/debugmodule.h" #include "../generic/StreamProcessor.h" #include "../util/cip.h" namespace Streaming { #define MOTUFW_MAX_MIXBUSES 4 #define MOTUFW_MAX_MIXBUS_CHANNELS 20 #define MOTU_CHANNEL_NORMAL 0x00 #define MOTU_CHANNEL_MUTE 0x01 #define MOTU_CHANNEL_SOLO 0x02 #define MOTU_CHANNEL_PAIRED 0x08 #define MOTU_CHANNEL_PAN_LEFT 0x00 #define MOTU_CHANNEL_PAN_RIGHT 0x80 #define MOTU_CHANNEL_PAN_CENTRE 0x40 #define MOTU_DEST_DISABLED 0x00 #define MOTU_DEST_HEADPHONE 0x01 #define MOTU_DEST_ANALOG1_2 0x02 #define MOTU_DEST_ANALOG3_4 0x03 #define MOTU_DEST_ANALOG5_6 0x04 #define MOTU_DEST_ANALOG7_8 0x05 #define MOTU_DEST_AESEBU 0x06 #define MOTU_DEST_SPDIF 0x07 #define MOTU_DEST_ADAT1_1_2 0x08 #define MOTU_DEST_ADAT1_3_4 0x09 #define MOTU_DEST_ADAT1_5_6 0x0a #define MOTU_DEST_ADAT1_7_8 0x0b #define MOTU_DEST_MUTE 0x10 enum EMotuDevCtrlStatus { MOTU_DEVCTRL_INVALID = 0x00, MOTU_DEVCTRL_SYNCING = 0x01, MOTU_DEVCTRL_INIT = 0x02, MOTU_DEVCTRL_VALID = 0x03, }; struct MotuDevControls { unsigned int status; unsigned int input_6dB_boost; unsigned int input_ref_level; unsigned int input_20dB_pad; unsigned int input_gaintrim[MOTUFW_MAX_MIXBUS_CHANNELS]; unsigned char input_gaintrim_index; struct MixBus { unsigned char channel_gain[MOTUFW_MAX_MIXBUS_CHANNELS]; unsigned char channel_gain_index; unsigned char channel_pan[MOTUFW_MAX_MIXBUS_CHANNELS]; unsigned char channel_pan_index; unsigned char channel_control[MOTUFW_MAX_MIXBUS_CHANNELS]; unsigned char channel_control_index; unsigned char bus_gain; unsigned char bus_dest; } mixbus[MOTUFW_MAX_MIXBUSES]; unsigned char mixbus_index; unsigned char main_out_volume; unsigned char phones_volume; unsigned char phones_assign; unsigned char n_mixbuses; unsigned char n_channels; }; enum EMotuCtrlKeys { MOTU_KEY_MIDI = 0x01, MOTU_KEY_SEQ_SYNC = 0x0c, MOTU_KEY_CHANNEL_GAIN = 0x14, MOTU_KEY_CHANNEL_PAN = 0x1c, MOTU_KEY_CHANNEL_CTRL = 0x24, MOTU_KEY_MIXBUS_GAIN = 0x2c, MOTU_KEY_MIXBUS_DEST = 0x34, MOTU_KEY_MAINOUT_VOL = 0x3c, MOTU_KEY_PHONES_VOL = 0x44, MOTU_KEY_PHONES_DEST = 0x4c, MOTU_KEY_INPUT_6dB_BOOST = 0x6c, MOTU_KEY_INPUT_REF_LEVEL = 0x74, MOTU_KEY_MASK_MIDI = 0x01, }; enum EMotuSeqSyncMixbuses { MOTU_KEY_SEQ_SYNC_MIXBUS1 = 0x00, MOTU_KEY_SEQ_SYNC_MIXBUS2 = 0x20, MOTU_KEY_SEQ_SYNC_MIXBUS3 = 0x40, MOTU_KEY_SEQ_SYNC_MIXBUS4 = 0x60, }; class MotuAudioPort; class MotuMidiPort; /*! * \brief The Base Class for a MOTU receive stream processor * * This class implements the outgoing stream processing for * motu devices * */ class MotuReceiveStreamProcessor : public StreamProcessor { public: /** * Create a MOTU receive StreamProcessor * @param port 1394 port * @param dimension number of substreams in the ISO stream * (midi-muxed is only one stream) */ MotuReceiveStreamProcessor(FFADODevice &parent, unsigned int event_size); virtual ~MotuReceiveStreamProcessor() {}; enum eChildReturnValue processPacketHeader(unsigned char *data, unsigned int length, unsigned char tag, unsigned char sy, uint32_t pkt_ctr); enum eChildReturnValue processPacketData(unsigned char *data, unsigned int length); virtual bool prepareChild(); public: virtual unsigned int getEventSize() {return m_event_size;}; virtual unsigned int getMaxPacketSize(); virtual unsigned int getEventsPerFrame() { return 1; }; virtual unsigned int getNominalFramesPerPacket(); protected: bool processReadBlock(char *data, unsigned int nevents, unsigned int offset); private: bool decodePacketPorts(quadlet_t *data, unsigned int nevents, unsigned int dbc); int decodeMotuEventsToPort(MotuAudioPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); int decodeMotuMidiEventsToPort(MotuMidiPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); int decodeMotuCtrlEvents(char *data, unsigned int nevents); /* * An iso packet mostly consists of multiple events. m_event_size * is the size of a single 'event' in bytes. */ unsigned int m_event_size; signed int m_motu_model; struct MotuDevControls m_devctrls; /* A small MIDI buffer to cover for the case where we need to span a * period. This can only occur if more than one MIDI byte is sent per * packet, but this has been observed with some MOTUs (eg: 828MkII). * Since the long-term average data rate must be close to the MIDI spec * since it's coming from a physical MIDI port this buffer doesn't have * to be particularly large. The size is a power of 2 for optimisation * reasons. */ #define RX_MIDIBUFFER_SIZE_EXP 6 #define RX_MIDIBUFFER_SIZE (1<. * */ #include "config.h" #include "libutil/float_cast.h" #include "MotuTransmitStreamProcessor.h" #include "MotuPort.h" #include "../StreamProcessorManager.h" #include "devicemanager.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/IsoHandlerManager.h" #include "libieee1394/cycletimer.h" #include "libutil/ByteSwap.h" #include "../../motu/motu_avdevice.h" #include #include // Set to 1 to enable the generation of a 1 kHz test tone in analog output 1. Even with // this defined to 1 the test tone will now only be produced if run with a non-zero // debug level. #define TESTTONE 1 #if TESTTONE #include #endif /* Provide more intuitive access to GCC's branch predition built-ins */ #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) namespace Streaming { // A macro to extract specific bits from a native endian quadlet #define get_bits(_d,_start,_len) (((_d)>>((_start)-(_len)+1)) & ((1<<(_len))-1)) // Convert a full timestamp into an SPH timestamp as required by the MOTU static inline uint32_t fullTicksToSph(int64_t timestamp) { return TICKS_TO_CYCLE_TIMER(timestamp) & 0x1ffffff; } /* transmit */ MotuTransmitStreamProcessor::MotuTransmitStreamProcessor(FFADODevice &parent, unsigned int event_size ) : StreamProcessor(parent, ePT_Transmit ) , m_event_size( event_size ) , m_motu_model( 0 ) , m_tx_dbc( 0 ) , mb_head( 0 ) , mb_tail( 0 ) , midi_lock( 0 ) { int srate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); /* Work out how many audio samples should be left between MIDI data bytes in order * to stay under the MIDI hardware baud rate of 31250. MIDI data is transmitted * using 10 bits per byte (including the start/stop bit) so this gives us 3125 bytes * per second. If we send to the MOTU at a faster rate than this, some MIDI bytes * will be dropped or corrupted in interesting ways. */ midi_tx_period = lrintf(ceil((float)srate / 3125)); /* As for the receive case, the transmit code requires easy access to the * MOTU model. */ Motu::MotuDevice *dev = static_cast(&parent); m_motu_model = dev->m_motu_model; /* The MOTU wants events to be sized in multiples of 4 bytes. Because * the channel data is 24 bit, this sometimes necessitates X pad bytes be * added where X is not a multiple of 3. Store the number of such bytes * for use later. * * The 828mk1 starts audio data at byte 4 while all other models start at * 10. Since the difference between these is a multiple of 3 it doesn't * matter which we use as the data origin. */ m_event_pad_bytes = (event_size - 4) % 3; } unsigned int MotuTransmitStreamProcessor::getMaxPacketSize() { int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); // All devices tend to have larger rx packets than tx packets, so for // now just use the rx numbers here. return framerate<=48000?904:(framerate<=96000?1416:1672); } unsigned int MotuTransmitStreamProcessor::getNominalFramesPerPacket() { int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); return framerate<=48000?8:(framerate<=96000?16:32); } enum StreamProcessor::eChildReturnValue MotuTransmitStreamProcessor::generatePacketHeader ( unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr ) { unsigned int cycle = CYCLE_TIMER_GET_CYCLES(pkt_ctr); // The number of events per packet expected by the MOTU is solely // dependent on the current sample rate. An 'event' is one sample from // all channels plus possibly other midi and control data. signed n_events = getNominalFramesPerPacket(); // Do housekeeping expected for all packets sent to the MOTU, even // for packets containing no audio data. *sy = 0x00; *tag = 1; // All MOTU packets have a CIP-like header *length = n_events*m_event_size + 8; signed int fc; uint64_t presentation_time; unsigned int presentation_cycle; int cycles_until_presentation; uint64_t transmit_at_time; unsigned int transmit_at_cycle; int cycles_until_transmit; debugOutput ( DEBUG_LEVEL_ULTRA_VERBOSE, "Try for cycle %d\n", cycle ); // check whether the packet buffer has packets for us to send. // the base timestamp is the one of the next sample in the buffer ffado_timestamp_t ts_head_tmp; m_data_buffer->getBufferHeadTimestamp ( &ts_head_tmp, &fc ); // thread safe // the timestamp gives us the time at which we want the sample block // to be output by the device presentation_time = ( uint64_t ) ts_head_tmp; // now we calculate the time when we have to transmit the sample block transmit_at_time = substractTicks ( presentation_time, MOTU_TRANSMIT_TRANSFER_DELAY ); // calculate the cycle this block should be presented in // (this is just a virtual calculation since at that time it should // already be in the device's buffer) presentation_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( presentation_time ) ); // calculate the cycle this block should be transmitted in transmit_at_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( transmit_at_time ) ); // we can check whether this cycle is within the 'window' we have // to send this packet. // first calculate the number of cycles left before presentation time cycles_until_presentation = diffCycles ( presentation_cycle, cycle ); // we can check whether this cycle is within the 'window' we have // to send this packet. // first calculate the number of cycles left before presentation time cycles_until_transmit = diffCycles ( transmit_at_cycle, cycle ); // two different options: // 1) there are not enough frames for one packet // => determine wether this is a problem, since we might still // have some time to send it // 2) there are enough packets // => determine whether we have to send them in this packet if ( fc < ( signed int ) getNominalFramesPerPacket() ) { // not enough frames in the buffer, // we can still postpone the queueing of the packets // if we are far enough ahead of the presentation time if ( cycles_until_presentation <= MOTU_MIN_CYCLES_BEFORE_PRESENTATION ) { debugOutput ( DEBUG_LEVEL_VERBOSE, "Insufficient frames (P): N=%02d, CY=%04u, TC=%04u, CUT=%04d\n", fc, cycle, transmit_at_cycle, cycles_until_transmit ); // we are too late return eCRV_XRun; } else { debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "Insufficient frames (NP): N=%02d, CY=%04u, TC=%04u, CUT=%04d\n", fc, cycle, transmit_at_cycle, cycles_until_transmit ); // there is still time left to send the packet // we want the system to give this packet another go at a later time instant return eCRV_Again; } } else { // there are enough frames, so check the time they are intended for // all frames have a certain 'time window' in which they can be sent // this corresponds to the range of the timestamp mechanism: // we can send a packet 15 cycles in advance of the 'presentation time' // in theory we can send the packet up till one cycle before the presentation time, // however this is not very smart. // There are 3 options: // 1) the frame block is too early // => send an empty packet // 2) the frame block is within the window // => send it // 3) the frame block is too late // => discard (and raise xrun?) // get next block of frames and repeat if(cycles_until_transmit < 0) { // we are too late debugOutput(DEBUG_LEVEL_VERBOSE, "Too late: CY=%04u, TC=%04u, CUT=%04d, TSP=%011" PRIu64 " (%04u)\n", cycle, transmit_at_cycle, cycles_until_transmit, presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time) ); // however, if we can send this sufficiently before the presentation // time, it could be harmless. // NOTE: dangerous since the device has no way of reporting that it didn't get // this packet on time. if(cycles_until_presentation >= MOTU_MIN_CYCLES_BEFORE_PRESENTATION) { // we are not that late and can still try to transmit the packet fillDataPacketHeader((quadlet_t *)data, length, presentation_time); m_last_timestamp = presentation_time; return eCRV_Packet; } else // definitely too late { return eCRV_XRun; } } else if(cycles_until_transmit <= MOTU_MAX_CYCLES_TO_TRANSMIT_EARLY) { // it's time send the packet fillDataPacketHeader((quadlet_t *)data, length, presentation_time); m_last_timestamp = presentation_time; return eCRV_Packet; } else { debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "Too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011" PRIu64 " (%04u), TSP=%011" PRIu64 " (%04u)\n", cycle, transmit_at_cycle, cycles_until_transmit, transmit_at_time, ( unsigned int ) TICKS_TO_CYCLES ( transmit_at_time ), presentation_time, ( unsigned int ) TICKS_TO_CYCLES ( presentation_time ) ); #ifdef DEBUG if ( cycles_until_transmit > MOTU_MAX_CYCLES_TO_TRANSMIT_EARLY + 1 ) { debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "Way too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011" PRIu64 " (%04u), TSP=%011" PRIu64 " (%04u)\n", cycle, transmit_at_cycle, cycles_until_transmit, transmit_at_time, ( unsigned int ) TICKS_TO_CYCLES ( transmit_at_time ), presentation_time, ( unsigned int ) TICKS_TO_CYCLES ( presentation_time ) ); } #endif // we are too early, send only an empty packet return eCRV_EmptyPacket; } } return eCRV_Invalid; } enum StreamProcessor::eChildReturnValue MotuTransmitStreamProcessor::generatePacketData ( unsigned char *data, unsigned int *length) { quadlet_t *quadlet = (quadlet_t *)data; quadlet += 2; // skip the header // Size of a single data frame in quadlets unsigned dbs = m_event_size / 4; // The number of events per packet expected by the MOTU is solely // dependent on the current sample rate. An 'event' is one sample from // all channels plus possibly other midi and control data. signed n_events = getNominalFramesPerPacket(); if (m_data_buffer->readFrames(n_events, (char *)(data + 8))) { float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getTicksPerFrame(); #if TESTTONE /* Now things are beginning to stabilise, make things easier for others by only playing * the test tone when run with a non-zero debug level. */ if (getDebugLevel() > 0) { // FIXME: remove this hacked in 1 kHz test signal to // analog-1 when testing is complete. signed int i, int_tpf = lrintf(ticks_per_frame); unsigned char *sample = data+8+16; for (i=0; i= 24576000) { a_cx -= 24576000; } } } #endif // Set up each frames's SPH. for (int i=0; i < n_events; i++, quadlet += dbs) { int64_t ts_frame = addTicks(m_last_timestamp, (unsigned int)lrintf(i * ticks_per_frame)); *quadlet = CondSwapToBus32(fullTicksToSph(ts_frame)); } /* Zero any pad bytes which, due to their number not being a multiple * of 3, do not have a port associated with them. Such bytes will * never be written to in processWriteBlock() when the buffers are * prepared, which means they could contain random data when put * into the transmit packet unless explicitly zeroed here. */ if (m_event_pad_bytes != 0) { unsigned char *s = data+8+m_event_size-m_event_pad_bytes; for (int i=0; i 0) { static signed int pktcx = 0; unsigned int i; if (pktcx == 0) { fprintf(stderr, "Packet to MOTU: length=%d, eventsize=%d, n_events=%d\n", *length, m_event_size, n_events); for (i=0; i<*length; i++) { if ((i&0x000f) == 0) fprintf(stderr, "%08x ", i); fprintf(stderr, "%02x ", data[i]); if ((i&0x000f) == 7) fprintf(stderr, "- "); if ((i&0x000f) == 0xf) fprintf(stderr, "\n"); } fprintf(stderr, "\n"); } if (++pktcx == 8000) pktcx=0; } return eCRV_OK; } else return eCRV_XRun; } enum StreamProcessor::eChildReturnValue MotuTransmitStreamProcessor::generateEmptyPacketHeader ( unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr ) { debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "XMIT EMPTY: CY=%04d, TSP=%011" PRIu64 " (%04u)\n", (int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp, ( unsigned int ) TICKS_TO_CYCLES ( m_last_timestamp ) ); // Do housekeeping expected for all packets sent to the MOTU, even // for packets containing no audio data. *sy = 0x00; *tag = 1; // All MOTU packets have a CIP-like header *length = 8; fillNoDataPacketHeader ( (quadlet_t *)data, length ); return eCRV_OK; } enum StreamProcessor::eChildReturnValue MotuTransmitStreamProcessor::generateEmptyPacketData ( unsigned char *data, unsigned int *length) { return eCRV_OK; // no need to do anything } enum StreamProcessor::eChildReturnValue MotuTransmitStreamProcessor::generateSilentPacketHeader ( unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr ) { unsigned int cycle = CYCLE_TIMER_GET_CYCLES(pkt_ctr); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "XMIT SILENT: CY=%04u, TSP=%011" PRIu64 " (%04u)\n", cycle, m_last_timestamp, ( unsigned int ) TICKS_TO_CYCLES ( m_last_timestamp ) ); // A "silent" packet is identical to a regular data packet except all // audio data is set to zero. The MOTU expects valid timestamps and // rate control in silent packets, so much of the timing logic from // generatePacketHeader() is needed here too - the main difference being // the source of the packet timestamp. // The number of events per packet expected by the MOTU is solely // dependent on the current sample rate. An 'event' is one sample from // all channels plus possibly other midi and control data. signed n_events = getNominalFramesPerPacket(); // Do housekeeping expected for all packets sent to the MOTU, even // for packets containing no audio data. *sy = 0x00; *tag = 1; // All MOTU packets have a CIP-like header /* Assume the packet will have audio data. If it turns out we need an empty packet * the length will be overridden by fillNoDataPacketHeader(). */ *length = n_events*m_event_size + 8; uint64_t presentation_time; unsigned int presentation_cycle; int cycles_until_presentation; uint64_t transmit_at_time; unsigned int transmit_at_cycle; int cycles_until_transmit; /* The sample buffer is not necessarily running when silent packets are * needed, so use m_last_timestamp (the timestamp of the previously sent * data packet) as the basis for the presentation time of the next * packet. Since we're only writing zeros we don't have to deal with * buffer xruns. */ float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getTicksPerFrame(); presentation_time = addTicks(m_last_timestamp, (unsigned int)lrintf(n_events * ticks_per_frame)); transmit_at_time = substractTicks(presentation_time, MOTU_TRANSMIT_TRANSFER_DELAY); presentation_cycle = (unsigned int)(TICKS_TO_CYCLES(presentation_time)); transmit_at_cycle = (unsigned int)(TICKS_TO_CYCLES(transmit_at_time)); cycles_until_presentation = diffCycles(presentation_cycle, cycle); cycles_until_transmit = diffCycles(transmit_at_cycle, cycle); if (cycles_until_transmit < 0) { if (cycles_until_presentation >= MOTU_MIN_CYCLES_BEFORE_PRESENTATION) { m_last_timestamp = presentation_time; fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp); return eCRV_Packet; } else { return eCRV_XRun; } } else if (cycles_until_transmit <= MOTU_MAX_CYCLES_TO_TRANSMIT_EARLY) { m_last_timestamp = presentation_time; fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp); return eCRV_Packet; } else { return eCRV_EmptyPacket; } return eCRV_Invalid; } enum StreamProcessor::eChildReturnValue MotuTransmitStreamProcessor::generateSilentPacketData ( unsigned char *data, unsigned int *length ) { // Simply set all audio data to zero since that's what's meant by // a "silent" packet. Note that m_event_size is in bytes for MOTU. quadlet_t *quadlet = (quadlet_t *)data; quadlet += 2; // skip the header // Size of a single data frame in quadlets unsigned dbs = m_event_size / 4; // The number of events per packet expected by the MOTU is solely // dependent on the current sample rate. An 'event' is one sample from // all channels plus possibly other midi and control data. signed n_events = getNominalFramesPerPacket(); memset(quadlet, 0, n_events*m_event_size); float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getTicksPerFrame(); // Set up each frames's SPH. for (int i=0; i < n_events; i++, quadlet += dbs) { int64_t ts_frame = addTicks(m_last_timestamp, (unsigned int)lrintf(i * ticks_per_frame)); *quadlet = CondSwapToBus32(fullTicksToSph(ts_frame)); } return eCRV_OK; } unsigned int MotuTransmitStreamProcessor::fillDataPacketHeader ( quadlet_t *data, unsigned int* length, uint32_t ts ) { quadlet_t *quadlet = (quadlet_t *)data; // Size of a single data frame in quadlets. For data sent TO the // Ultralite this is not strictly true (with m_event_size 52, dbs is set // to 13, even though data sent by the Ultralite uses 19 as one would // expect from a 52-byte event). Even so, we'll run with the assumption // that a different dbs will be fine unless proven otherwise. 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(); // For MOTU ISO packets containing data, the DBC is incremented before // being stored into the current packet's CIP-like header. m_tx_dbc += n_events; if (m_tx_dbc > 0xff) m_tx_dbc -= 0x100; // construct the packet CIP-like header. *quadlet = CondSwapToBus32(0x00000400 | ((m_Parent.get1394Service().getLocalNodeId()&0x3f)<<24) | m_tx_dbc | (dbs<<16)); quadlet++; *quadlet = CondSwapToBus32(0x8222ffff); quadlet++; // Return value is the increment applied to the DBC in case the caller // needs to know this. 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. See comment in // fillDataPacketHeader() regarding the Ultralite. unsigned dbs = m_event_size / 4; // construct the packet CIP-like header. Packets without data use // the same DBC as the previous packet, so no DBC increment is needed // here. *quadlet = CondSwapToBus32(0x00000400 | ((m_Parent.get1394Service().getLocalNodeId()&0x3f)<<24) | m_tx_dbc | (dbs<<16)); quadlet++; *quadlet = CondSwapToBus32(0x8222ffff); quadlet++; *length = 8; return 0; } bool MotuTransmitStreamProcessor::prepareChild() { debugOutput ( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this ); return true; } /* * compose the event streams for the packets from the port buffers */ bool MotuTransmitStreamProcessor::processWriteBlock(char *data, unsigned int nevents, unsigned int offset) { bool no_problem=true; unsigned int i; // Start with MIDI and control streams all zeroed. Due to the sparce // nature of these streams it is best to simply fill them in on an // as-needs basis. Note that the 828mk1 does not have MIDI ports or // extra control data - audio data starts at offset 4 instead of 10. // However, apart from wasting a few cycles in the 828mk1 case no harm // is done in zeroing these bytes - any audio channels in those // positions will overwrite the zeros subsequently. This saves the need // for a conditional which for most MOTU interfaces would always be taken. for (i=0; iisDisabled()) { if (encodeSilencePortToMotuEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode silence for disabled port %s to Motu events\n",(*it)->getName().c_str()); // Don't treat this as a fatal error at this point } continue; } Port *port=(*it); switch(port->getPortType()) { case Port::E_Audio: if (encodePortToMotuEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode port %s to Motu events\n",(*it)->getName().c_str()); no_problem=false; } break; case Port::E_Midi: if (encodePortToMotuMidiEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode port %s to Midi events\n",(*it)->getName().c_str()); no_problem=false; } break; default: // ignore break; } } return no_problem; } bool MotuTransmitStreamProcessor::transmitSilenceBlock(char *data, unsigned int nevents, unsigned int offset) { // This is the same as the non-silence version, except that is // doesn't read from the port buffers. bool no_problem = true; for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { Port *port=(*it); switch(port->getPortType()) { case Port::E_Audio: if (encodeSilencePortToMotuEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode port %s to MBLA events\n",(*it)->getName().c_str()); no_problem = false; } break; case Port::E_Midi: if (encodeSilencePortToMotuMidiEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode port %s to Midi events\n",(*it)->getName().c_str()); no_problem = false; } break; default: // ignore break; } } return no_problem; } int MotuTransmitStreamProcessor::encodePortToMotuEvents(MotuAudioPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { // Encodes nevents worth of data from the given port into the given buffer. The // format of the buffer is precisely that which will be sent to the MOTU. // The basic idea: // iterate over the ports // * get port buffer address // * loop over events // - pick right sample in event based upon PortInfo // - convert sample from Port format (E_Int24, E_Float, ..) to MOTU // native format // // We include the ability to start the transfer from the given offset within // the port (expressed in frames) so the 'efficient' transfer method can be // utilised. unsigned int j=0; // Use char here since the target address won't necessarily be // aligned; use of an unaligned quadlet_t may cause issues on certain // architectures. Besides, the target (data going directly to the MOTU) // isn't structured in quadlets anyway; it mainly consists of packed // 24-bit integers. unsigned char *target; target = (unsigned char *)data + p->getPosition(); switch(m_StreamProcessorManager.getAudioDataType()) { default: case StreamProcessorManager::eADT_Int24: { quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); // Offset is in frames, but each port is only a single // channel, so the number of frames is the same as the // number of quadlets to offset (assuming the port buffer // uses one quadlet per sample, which is the case currently). buffer+=offset; for(j = 0; j < nevents; j += 1) { // Decode nsamples *target = (*buffer >> 16) & 0xff; *(target+1) = (*buffer >> 8) & 0xff; *(target+2) = (*buffer) & 0xff; buffer++; target+=m_event_size; } } break; case StreamProcessorManager::eADT_Float: { const float multiplier = (float)(0x7FFFFF); float *buffer=(float *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); buffer+=offset; for(j = 0; j < nevents; j += 1) { // decode max nsamples float in = *buffer; #if MOTU_CLIP_FLOATS if (unlikely(in > 1.0)) in = 1.0; if (unlikely(in < -1.0)) in = -1.0; #endif unsigned int v = lrintf(in * multiplier); *target = (v >> 16) & 0xff; *(target+1) = (v >> 8) & 0xff; *(target+2) = v & 0xff; buffer++; target+=m_event_size; } } break; } return 0; } int MotuTransmitStreamProcessor::encodeSilencePortToMotuEvents(MotuAudioPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j=0; unsigned char *target = (unsigned char *)data + p->getPosition(); switch (m_StreamProcessorManager.getAudioDataType()) { default: case StreamProcessorManager::eADT_Int24: case StreamProcessorManager::eADT_Float: for (j = 0; j < nevents; j++) { *target = *(target+1) = *(target+2) = 0; target += m_event_size; } break; } return 0; } int MotuTransmitStreamProcessor::encodePortToMotuMidiEvents( MotuMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j; quadlet_t *src = (quadlet_t *)p->getBufferAddress(); src += offset; unsigned char *target = (unsigned char *)data + p->getPosition(); // Send a MIDI byte if there is one to send. MOTU MIDI data is sent using // a 3-byte sequence within a frame starting at the port's position. // A non-zero MSB indicates there is MIDI data to send. for (j=0; jgetPosition(); // For now, a "silent" MIDI event contains nothing but zeroes. This // may have to change if we find this isn't for some reason appropriate. for (j=0; j. * */ #ifndef __FFADO_MOTUTRANSMITSTREAMPROCESSOR__ #define __FFADO_MOTUTRANSMITSTREAMPROCESSOR__ /** * This class implements MOTU based streaming */ #include "debugmodule/debugmodule.h" #include "../generic/StreamProcessor.h" #include "../util/cip.h" namespace Streaming { class Port; class MotuAudioPort; class MotuMidiPort; /*! \brief The Base Class for an MOTU transmit stream processor This class implements a TransmitStreamProcessor that multiplexes Ports into MOTU streams. */ class MotuTransmitStreamProcessor : public StreamProcessor { public: /** * Create a MOTU transmit StreamProcessor */ MotuTransmitStreamProcessor(FFADODevice &parent, unsigned int event_size); virtual ~MotuTransmitStreamProcessor() {}; enum eChildReturnValue generatePacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr); enum eChildReturnValue generatePacketData(unsigned char *data, unsigned int *length); enum eChildReturnValue generateEmptyPacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr); enum eChildReturnValue generateEmptyPacketData(unsigned char *data, unsigned int *length); enum eChildReturnValue generateSilentPacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr); enum eChildReturnValue generateSilentPacketData(unsigned char *data, unsigned int *length); virtual bool prepareChild(); public: virtual unsigned int getEventSize() {return m_event_size;}; virtual unsigned int getMaxPacketSize(); virtual unsigned int getEventsPerFrame() { return 1; }; virtual unsigned int getNominalFramesPerPacket(); protected: bool processWriteBlock(char *data, unsigned int nevents, unsigned int offset); bool transmitSilenceBlock(char *data, unsigned int nevents, unsigned int offset); private: unsigned int fillNoDataPacketHeader(quadlet_t *data, unsigned int* length); unsigned int fillDataPacketHeader(quadlet_t *data, unsigned int* length, uint32_t ts); int transmitBlock(char *data, unsigned int nevents, unsigned int offset); bool encodePacketPorts(quadlet_t *data, unsigned int nevents, unsigned int dbc); int encodePortToMotuEvents(MotuAudioPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); int encodeSilencePortToMotuEvents(MotuAudioPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); int encodePortToMotuMidiEvents( MotuMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents); int encodeSilencePortToMotuMidiEvents( MotuMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents); /* * An iso packet mostly consists of multiple events. m_event_size * is the size of a single 'event' in bytes. */ unsigned int m_event_size; // To save time in the fast path, the number of pad bytes is stored // explicitly. unsigned int m_event_pad_bytes; signed int m_motu_model; // Keep track of transmission data block count unsigned int m_tx_dbc; // A simple circular buffer for outgoing MIDI data to allow // a rate control to be implemented on the data to suit the MOTU // devices. Note that this buffer's size is forced to be a power // of 2 to allow for buffer manipulation optimisations. #define MIDIBUFFER_SIZE_EXP 10 #define MIDIBUFFER_SIZE (1<. * */ #include "RmePort.h" #include namespace Streaming { } // end of namespace Streaming libffado-2.4.5/src/libstreaming/rme/RmePort.h0000644000175000001440000000407014206145246020460 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_RMEPORT__ #define __FFADO_RMEPORT__ /** * This file implements the ports used in Rme devices */ #include "RmePortInfo.h" #include "../generic/Port.h" #include "debugmodule/debugmodule.h" namespace Streaming { /*! \brief The Base Class for Rme Audio Port */ class RmeAudioPort : public AudioPort, public RmePortInfo { public: RmeAudioPort(PortManager &m, std::string name, enum E_Direction direction, int position, int size) : AudioPort(m, name, direction), RmePortInfo( position, size) // TODO: add more port information parameters here if nescessary {}; virtual ~RmeAudioPort() {}; }; /*! \brief The Base Class for an RME Midi Port */ class RmeMidiPort : public MidiPort, public RmePortInfo { public: RmeMidiPort(PortManager &m, std::string name, enum E_Direction direction, int position) : MidiPort(m, name, direction), RmePortInfo(position, 0) // TODO: add more port information parameters here if nescessary {}; virtual ~RmeMidiPort() {}; }; } // end of namespace Streaming #endif /* __FFADO_RMEPORT__ */ libffado-2.4.5/src/libstreaming/rme/RmePortInfo.cpp0000644000175000001440000000170314206145246021627 0ustar jwoitheusers/* * Copyright (C) 2005-2009 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "RmePortInfo.h" namespace Streaming { } // end of namespace Streaming libffado-2.4.5/src/libstreaming/rme/RmePortInfo.h0000644000175000001440000000463514206145246021303 0ustar jwoitheusers/* * Copyright (C) 2005-2009 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_RMEPORTINFO__ #define __FFADO_RMEPORTINFO__ #include "debugmodule/debugmodule.h" #include namespace Streaming { /*! \brief Class containing the stream information for a Rme channel Contains the information that enables the decoding routine to find this port's data in the ISO events */ class RmePortInfo { 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 Rme 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 */ RmePortInfo( int position, int size) : m_position(position), m_size(size) {}; virtual ~RmePortInfo() {}; int getPosition() {return m_position;}; int getSize() {return m_size;}; protected: int m_position; int m_size; }; } // end of namespace Streaming #endif /* __FFADO_RMEPORTINFO__ */ libffado-2.4.5/src/libstreaming/rme/RmeReceiveStreamProcessor.cpp0000644000175000001440000003761014206145246024533 0ustar jwoitheusers/* * Copyright (C) 2005-2009 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* CAUTION: this module is under active development. It has been templated * from the MOTU driver and many MOTU-specific details remain. Do not use * this file as a reference for RME devices until initial development has * been completed. */ #include "libutil/float_cast.h" #include "RmeReceiveStreamProcessor.h" #include "RmePort.h" #include "../StreamProcessorManager.h" #include "devicemanager.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/IsoHandlerManager.h" #include "libieee1394/cycletimer.h" #include "libutil/ByteSwap.h" // This is to pick up the RME_MODEL_* constants. There's probably a better // way ... #include "../../rme/rme_avdevice.h" #include #include #include /* Provide more intuitive access to GCC's branch predition built-ins */ #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) namespace Streaming { // A macro to extract specific bits from a native endian quadlet #define get_bits(_d,_start,_len) (((_d)>>((_start)-(_len)+1)) & ((1<<(_len))-1)) RmeReceiveStreamProcessor::RmeReceiveStreamProcessor(FFADODevice &parent, unsigned int model, unsigned int event_size) : StreamProcessor(parent, ePT_Receive) , n_hw_tx_buffer_samples ( -1 ) , m_rme_model( model ) , m_event_size( event_size ) , mb_head ( 0 ) , mb_tail ( 0 ) { } unsigned int RmeReceiveStreamProcessor::getMaxPacketSize() { Rme::Device *dev = static_cast(&m_Parent); // int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); // FIXME: the additional 8 bytes is not needed. // FIXME: the upper bounds of the 1x and 2x rates need to account for the // DDS capability to run fast by 4%. //if (m_rme_model == Rme::RME_MODEL_FIREFACE800) // return 8 + (framerate<=48000?784:(framerate<=96000?1200:1200)); //else // return 8 + (framerate<=48000?504:(framerate<=96000?840:1000)); // Each channel comprises a single 32-bit quadlet. Note that the return // value is in bytes. // FIXME: getFramesPerPacket() is fixed by the sample rate class. It // needs to be confirmed that the values in use are still ok even // when the DDS is set to run 4% fast (something that's not presently // implemented by FFADO, but should be at some point). return dev->getFramesPerPacket() * dev->getNumChannels() * 4; } unsigned int RmeReceiveStreamProcessor::getNominalFramesPerPacket() { int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); return framerate<=48000?7:(framerate<=96000?15:25); } #define RXDLL_BANDWIDTH (0.003) bool RmeReceiveStreamProcessor::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()); // Request that the iso streaming be started as soon as possible by the // kernel. m_IsoHandlerManager.setIsoStartCycleForStream(this, -1); // Because the timestamp DLL is driven by a very course and jittery // approximation the default error detection and bandwidth settings are // not appropriate. Hense they are overridden, with the new values // mostly determined experimentally. m_dll_bandwidth_hz = 0.25; m_max_fs_diff_norm = 100.0; m_max_diff_ticks = 30720; m_data_buffer->setMaxAbsDiff(10000); m_Parent.getDeviceManager().getStreamProcessorManager().setMaxDiffTicks(30720); return true; } /* * Processes packet header to extract timestamps and check if the packet is * valid. */ enum StreamProcessor::eChildReturnValue RmeReceiveStreamProcessor::processPacketHeader(unsigned char *data, unsigned int length, unsigned char tag, unsigned char sy, uint32_t pkt_ctr) { int64_t pkt_timestamp; // To assist with debugging static signed int rep = 0; if (rep == 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "first data packet header, len=%d\n", length); } if (length > 0) { // The iso data blocks from the RMEs comprise 24-bit audio // data encoded in 32-bit integers. The LSB of the 32-bit integers // of certain channels are used for house-keeping information. // The number of samples for each channel present in a packet // varies: 7 for 1x rates, 15 for 2x rates and 25 for 4x rates. // quadlet_t *quadlet = (quadlet_t *)data; // Timestamps are not transmitted explicitly by the RME interfaces // so we have to fake it in order to fit in with the rest of the // FFADO infrastructure. The cycle timer at the time of the // packet's arrival is taken as an approximation of the packet's // timestamp. The jitter in the resulting timestamp is of course // extremely bad, which is why the DLL's default settings for // error detection and bandwidth are overridden by prepareChild(). // // The offset in the simulated timestamp is forced to a fixed // constant value determined by experimentation for stability. This // is needed because sometimes the RME sends two packets within a // few ticks of each other but in different cycles. Being so close // this stuffs up the rx DLL since the resulting instantaneous // sample rate is extremely high. // // Finally the packet timestamp is "back dated" by a the rx // transfer delay to account for delays between the ADCs and // reception of the audio data. pkt_timestamp = CYCLE_TIMER_TO_TICKS(CYCLE_TIMER_SET_OFFSET(pkt_ctr, 0)); pkt_timestamp -= (int64_t)RME_RECEIVE_TRANSFER_DELAY; if (pkt_timestamp < 0) pkt_timestamp += 128LL*TICKS_PER_SECOND; else if (pkt_timestamp >= 128LL*(signed)TICKS_PER_SECOND) pkt_timestamp -= 128LL*TICKS_PER_SECOND; m_last_timestamp = pkt_timestamp; // Retain this as it might be helpful for future development. // //if (rep == 0) { // debugOutput(DEBUG_LEVEL_VERBOSE, " timestamp: %lld, ct=%08x (%03ld,%04ld,%04ld)\n", m_last_timestamp, pkt_ctr, // CYCLE_TIMER_GET_SECS(pkt_ctr), CYCLE_TIMER_GET_CYCLES(pkt_ctr), CYCLE_TIMER_GET_OFFSET(pkt_ctr)); // debugOutput(DEBUG_LEVEL_VERBOSE, " %02x %02x %02x %02x %02x %02x %02x %02x\n", // adata[0] & 0xff, adata[1] & 0xff, adata[2] & 0xff, adata[3] & 0xff, // adata[4] & 0xff, adata[5] & 0xff, adata[6] & 0xff, adata[7] & 0xff); // debugOutput(DEBUG_LEVEL_VERBOSE, " tx size=%d, rxcount=%d\n", // ((adata[5] & 0xff) << 8) | (adata[0] & 0xff), // ((adata[4] & 0xff) << 8) | (adata[1] & 0xff)); // n_hw_tx_buffer_samples = adata[7] & 0xff; // debugOutput(DEBUG_LEVEL_VERBOSE, " hw tx: 0x%02x\n", n_hw_tx_buffer_samples); //} rep=1; return eCRV_OK; } else { return eCRV_Invalid; } } /** * extract the data from the packet * @pre the IEC61883 packet is valid according to isValidPacket * @param data * @param length * @param channel * @param tag * @param sy * @param pkt_ctr * @return */ enum StreamProcessor::eChildReturnValue RmeReceiveStreamProcessor::processPacketData(unsigned char *data, unsigned int length) { // m_event_size should never be zero unsigned int n_events = length / m_event_size; // To assist with debugging static signed int rep = 0; // 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: %" PRIu64 "ticks | tpf=%f\n", m_last_timestamp, getTicksPerFrame()); } #endif // For debugging if (rep == 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "data packet data, length=%d, ev_size=%d, n_events=%d\n", length, m_event_size, n_events); rep = 1; } if(m_data_buffer->writeFrames(n_events, (char *)data, m_last_timestamp)) { return eCRV_OK; } else { return eCRV_XRun; } } /*********************************************** * Encoding/Decoding API * ***********************************************/ /** * \brief write received events to the port ringbuffers. */ bool RmeReceiveStreamProcessor::processReadBlock(char *data, unsigned int nevents, unsigned int offset) { bool no_problem=true; for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { if((*it)->isDisabled()) {continue;}; Port *port=(*it); switch(port->getPortType()) { case Port::E_Audio: if(decodeRmeEventsToPort(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not decode packet data to port %s\n",(*it)->getName().c_str()); no_problem=false; } break; case Port::E_Midi: if(decodeRmeMidiEventsToPort(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not decode packet midi data to port %s\n",(*it)->getName().c_str()); no_problem=false; } break; default: // ignore break; } } return no_problem; } signed int RmeReceiveStreamProcessor::decodeRmeEventsToPort(RmeAudioPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j=0; // For RME interfaces the audio data is contained in the most significant // 24 bits of a 32-bit field. Thus it makes sense to treat the source // data as 32 bit and simply mask/shift as necessary to isolate the // audio data. quadlet_t *src_data; src_data = data + p->getPosition()/4; switch(m_StreamProcessorManager.getAudioDataType()) { default: case StreamProcessorManager::eADT_Int24: { quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); // Offset is in frames, but each port is only a single // channel, so the number of frames is the same as the // number of quadlets to offset (assuming the port buffer // uses one quadlet per sample, which is the case currently). buffer+=offset; for(j = 0; j < nevents; j += 1) { // Decode nsamples *buffer = (Rme::ByteSwapFromDevice32(*src_data) >> 8) & 0x00ffffff; // Sign-extend highest bit of 24-bit int. 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 & 0x80000000) *buffer |= 0xff000000; buffer++; src_data+=m_event_size/4; } } break; case StreamProcessorManager::eADT_Float: { const float multiplier = 1.0f / (float)(0x7FFFFF); float *buffer=(float *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); buffer+=offset; for(j = 0; j < nevents; j += 1) { // decode max nsamples signed int v = (Rme::ByteSwapFromDevice32(*src_data) >> 8) & 0x00ffffff; /* Sign-extend highest bit of incoming 24-bit integer */ if (*src_data & 0x80000000) v |= 0xff000000; *buffer = v * multiplier; buffer++; src_data+=m_event_size/4; } } break; } return 0; } int RmeReceiveStreamProcessor::decodeRmeMidiEventsToPort( RmeMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j = 0; unsigned char *src = NULL; quadlet_t *buffer = (quadlet_t *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); buffer += offset; // Zero the buffer memset(buffer, 0, nevents*sizeof(*buffer)); // This code comes directly from the MOTU driver and is retained for // reference. In the long run it will not be used because the RME // does not use the iso stream to send MIDI data. Instead an ARM // is needed. // // Get MIDI bytes if present in any frames within the packet. RME MIDI // data is sent as part of a 3-byte sequence starting at the port's // position. Some RMEs (eg: the 828MkII) send more than one MIDI byte // in some packets. Since the FFADO MIDI layer requires a MIDI byte in // only every 8th buffer position we allow for this by buffering the // incoming data. The buffer is small since it only has to cover for // short-term excursions in the data rate. Since the MIDI data // originates on a physical MIDI bus the overall data rate is limited by // the baud rate of that bus (31250), which is no more than one byte in // 8 even for 1x sample rates. src = (unsigned char *)data + p->getPosition(); // We assume that the buffer has been set up in such a way that the first // element is correctly aligned for FFADOs MIDI layer. The requirement // is that actual MIDI bytes must be aligned to multiples of 8 samples. while (j < nevents) { /* Most events don't have MIDI data bytes */ // if (unlikely((*src & RME_KEY_MASK_MIDI) == RME_KEY_MASK_MIDI)) { if (0) { // A MIDI byte is in *(src+2). Bit 24 is used to flag MIDI data // as present once the data makes it to the output buffer. midibuffer[mb_head++] = 0x01000000 | *(src+2); mb_head &= RX_MIDIBUFFER_SIZE-1; if (unlikely(mb_head == mb_tail)) { debugWarning("RME rx MIDI buffer overflow\n"); /* Dump oldest byte. This overflow can only happen if the * rate coming in from the hardware MIDI port grossly * exceeds the official MIDI baud rate of 31250 bps, so it * should never occur in practice. */ mb_tail = (mb_tail + 1) & (RX_MIDIBUFFER_SIZE-1); } } /* Write to the buffer if we're at an 8-sample boundary */ if (unlikely(!(j & 0x07))) { if (mb_head != mb_tail) { *buffer = midibuffer[mb_tail++]; mb_tail &= RX_MIDIBUFFER_SIZE-1; } buffer += 8; } j++; src += m_event_size; } return 0; } } // end of namespace Streaming libffado-2.4.5/src/libstreaming/rme/RmeReceiveStreamProcessor.h0000644000175000001440000000737414206145246024204 0ustar jwoitheusers/* * Copyright (C) 2005-2009 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_RMERECEIVESTREAMPROCESSOR__ #define __FFADO_RMERECEIVESTREAMPROCESSOR__ /** * This class implements RME streaming */ #include "debugmodule/debugmodule.h" #include "../generic/StreamProcessor.h" #include "../util/cip.h" namespace Streaming { class RmeAudioPort; class RmeMidiPort; /*! * \brief The Base Class for a RME receive stream processor * * This class implements the outgoing stream processing for * motu devices * */ class RmeReceiveStreamProcessor : public StreamProcessor { public: /** * Create a RME receive StreamProcessor * @param port 1394 port * @param dimension number of substreams in the ISO stream * (midi-muxed is only one stream) */ RmeReceiveStreamProcessor(FFADODevice &parent, unsigned int model, unsigned int event_size); virtual ~RmeReceiveStreamProcessor() {}; enum eChildReturnValue processPacketHeader(unsigned char *data, unsigned int length, unsigned char tag, unsigned char sy, uint32_t pkt_ctr); enum eChildReturnValue processPacketData(unsigned char *data, unsigned int length); virtual bool prepareChild(); public: virtual unsigned int getEventSize() {return m_event_size;}; virtual unsigned int getMaxPacketSize(); virtual unsigned int getEventsPerFrame() { return 1; }; virtual unsigned int getNominalFramesPerPacket(); /* For testing only at this stage */ signed int n_hw_tx_buffer_samples; protected: bool processReadBlock(char *data, unsigned int nevents, unsigned int offset); private: bool decodePacketPorts(quadlet_t *data, unsigned int nevents, unsigned int dbc); int decodeRmeEventsToPort(RmeAudioPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); int decodeRmeMidiEventsToPort(RmeMidiPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); unsigned int m_rme_model; /* * 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; /* A small MIDI buffer to cover for the case where we need to span a * period - that is, if more than one MIDI byte is sent per packet. * Since the long-term average data rate must be close to the MIDI spec * (as it's coming from a physical MIDI port_ this buffer doesn't have * to be particularly large. The size is a power of 2 for optimisation * reasons. * * FIXME: it is yet to be determined whether this is required for RME * devices. */ #define RX_MIDIBUFFER_SIZE_EXP 6 #define RX_MIDIBUFFER_SIZE (1<. * */ /* CAUTION: this module is under active development. It has been templated * from the MOTU driver and many MOTU-specific details remain. Do not use * this file as a reference for RME devices until initial development has * been completed. */ #include "config.h" #include "libutil/float_cast.h" #include "RmeTransmitStreamProcessor.h" #include "RmePort.h" #include "../StreamProcessorManager.h" #include "devicemanager.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/IsoHandlerManager.h" #include "libieee1394/cycletimer.h" #include "libutil/ByteSwap.h" #include "../../rme/rme_avdevice.h" #include #include // Set to 1 to enable the generation of a 1 kHz test tone in analog output 1. Even with // this defined to 1 the test tone will now only be produced if run with a non-zero // debug level. #define TESTTONE 1 #if TESTTONE #include #endif /* Provide more intuitive access to GCC's branch predition built-ins */ #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) namespace Streaming { // A macro to extract specific bits from a native endian quadlet #define get_bits(_d,_start,_len) (((_d)>>((_start)-(_len)+1)) & ((1<<(_len))-1)) /* transmit */ RmeTransmitStreamProcessor::RmeTransmitStreamProcessor(FFADODevice &parent, unsigned int model, unsigned int event_size ) : StreamProcessor(parent, ePT_Transmit ) , m_rme_model( model) , m_event_size( event_size ) , m_tx_dbc( 0 ) , mb_head( 0 ) , mb_tail( 0 ) , midi_lock( 0 ) , streaming_has_run ( 0 ) , streaming_has_dryrun ( 0 ) , streaming_start_count ( 0 ) { int srate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); /* Work out how many audio samples should be left between MIDI data bytes * in order to stay under the MIDI hardware baud rate of 31250. MIDI data * is transmitted using 10 bits per byte (including the start/stop bit) so * this gives us 3125 bytes per second. */ midi_tx_period = lrintf(ceil((float)srate / 3125)); } unsigned int RmeTransmitStreamProcessor::getMaxPacketSize() { Rme::Device *dev = static_cast(&m_Parent); // Each channel comprises a single 32-bit quadlet. Note that the return // value is in bytes. // FIXME: getFramesPerPacket() is fixed by the sample rate class. It // needs to be confirmed that the values in use are still ok even // when the DDS is set to run 4% fast (something that's not presently // implemented by FFADO, but should be at some point). return dev->getFramesPerPacket() * dev->getNumChannels() * 4; } unsigned int RmeTransmitStreamProcessor::getNominalFramesPerPacket() { return static_cast(&m_Parent)->getFramesPerPacket(); } bool RmeTransmitStreamProcessor::resetForStreaming() { streaming_has_run = 0; streaming_has_dryrun = 0; streaming_start_count = 0; return true; } enum StreamProcessor::eChildReturnValue RmeTransmitStreamProcessor::generatePacketHeader ( unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr ) { unsigned int cycle = CYCLE_TIMER_GET_CYCLES(pkt_ctr); signed n_events = getNominalFramesPerPacket(); // Called once per packet. Need to work out whether data should be sent // in this cycle or not and then act accordingly. Need to deal with // the condition where the buffers don't contain sufficient data to fill // the packet. FIXME: this function is incomplete. // Do housekeeping expected for all packets, irrespective of whether // they will contain data. *sy = 0x00; *length = 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; // now we calculate the time when we have to transmit the sample block transmit_at_time = substractTicks ( presentation_time, RME_TRANSMIT_TRANSFER_DELAY ); // calculate the cycle this block should be presented in // (this is just a virtual calculation since at that time it should // already be in the device's buffer) presentation_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( presentation_time ) ); // calculate the cycle this block should be transmitted in transmit_at_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( transmit_at_time ) ); // we can check whether this cycle is within the 'window' we have // to send this packet. // first calculate the number of cycles left before presentation time cycles_until_presentation = diffCycles ( presentation_cycle, cycle ); // we can check whether this cycle is within the 'window' we have // to send this packet. // first calculate the number of cycles left before presentation time cycles_until_transmit = diffCycles ( transmit_at_cycle, cycle ); // two different options: // 1) there are not enough frames for one packet // => determine wether this is a problem, since we might still // have some time to send it // 2) there are enough packets // => determine whether we have to send them in this packet if ( fc < ( signed int ) getNominalFramesPerPacket() ) { // not enough frames in the buffer, // we can still postpone the queueing of the packets // if we are far enough ahead of the presentation time if ( cycles_until_presentation <= RME_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=%011" PRIu64 " (%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 >= RME_MIN_CYCLES_BEFORE_PRESENTATION) { // we are not that late and can still try to transmit the // packet *length = n_events*m_event_size; m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, presentation_time); m_last_timestamp = presentation_time; if (m_tx_dbc > 0xff) m_tx_dbc -= 0x100; return eCRV_Packet; } else // definitely too late { return eCRV_XRun; } } else if(cycles_until_transmit <= RME_MAX_CYCLES_TO_TRANSMIT_EARLY) { // it's time send the packet *length = n_events*m_event_size; m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, presentation_time); m_last_timestamp = presentation_time; if (m_tx_dbc > 0xff) m_tx_dbc -= 0x100; return eCRV_Packet; } else { debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "Too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011" PRIu64 " (%04u), TSP=%011" PRIu64 " (%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 > RME_MAX_CYCLES_TO_TRANSMIT_EARLY + 1 ) { debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "Way too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011" PRIu64 " (%04u), TSP=%011" PRIu64 " (%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 RmeTransmitStreamProcessor::generatePacketData ( unsigned char *data, unsigned int *length) { // Flag the successful start of streaming so generateEmptyPacketHeader() // knows that true empty packets are now required. streaming_has_run=1; // The number of events per packet expected by the RME 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))) { // 1 kHz tone into ch7 (phones L) for testing, but only if a debug // level is set. #if TESTTONE if (getDebugLevel() > 0) { float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getTicksPerFrame(); signed int int_tpf = lrintf(ticks_per_frame); quadlet_t *sample = (quadlet_t *)data + 6; signed int i; for (i=0; i= 24576000) { a_cx -= 24576000; } } } #endif return eCRV_OK; } else { // FIXME: debugOutput() for initial testing only debugOutput(DEBUG_LEVEL_VERBOSE, "readFrames() failure\n"); // If there's an xrun, ensure the data doesn't contain junk just // in case it gets sent to the interface. memset(data, 0, *length); return eCRV_XRun; } } enum StreamProcessor::eChildReturnValue RmeTransmitStreamProcessor::generateEmptyPacketHeader ( unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr ) { debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "XMIT EMPTY: CY=%04lu, TSP=%011" PRIu64 " (%04u)\n", CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp, ( unsigned int ) TICKS_TO_CYCLES ( m_last_timestamp ) ); // Do housekeeping expected for all packets, even for packets containing // no audio data. *sy = 0x00; *length = 0; *tag = 0; // During the dryRunning state used during startup FFADO will request // "empty" packets. However, the fireface will only "start" (ie: // sending data to the DACs and sending data from the ADCs) // packets once it has received a certain amount of data from the PC. // Since the receive stream processor won't register as dry running // until data is received, we therefore need to send some data while in // the initial states during startup in order to kick the process into // gear. // // Non-empty "empty" packets are not desired during the dry-running // state encountered during any other stage (eg: during streaming or // closedown), however, so take steps to ensure they are only sent // during the startup sequence. // Experimentation showed that it was necessary to send "silent" packets // for the duration of the pre-streaming setup. Otherwise the FF800 // especially would give a burst of digital noise out of some or all // outputs during the first FFADO streaming startup following the // powering up of the interface (the duration of the burst was estimated // to be of the order of 250 ms at 48 kHz sampling rate). Therefore if // streaming has not yet run assume we're in the pre-streaming stages // and send a silent data packet. // // While a single packet was sufficient to get data flowing from the // fireface, the noise bursts would almost always occur in this case. // // Sending during the ePS_DryRunning seemed to reduce the probability // of getting the bursts slightly. // // Sending during the ePS_DryRunning and ePS_WaitingForStreamEnable // states was almost enough to prevent the noise burst: the bursts were // far less frequent and when they did happen they were very short. // // Further notes about the noise burst: // * it was not due to bad data from readFrames(). // * it seemed to be aligned to the time around the transition to // the ePS_DryRunning state, although this was never explicitly // measured. // * once streaming had been run once the bursts were very unlikely // to occur on second and subsequent runs until the Fireface was // powercycled. Bursts after the initial run were observed on very // isolated occasions, but they could have been a side effect of // testing at the time. if (streaming_has_run == 0) { signed n_events = getNominalFramesPerPacket(); streaming_has_dryrun = 1; streaming_start_count += n_events; *length = n_events * m_event_size; } return eCRV_OK; } enum StreamProcessor::eChildReturnValue RmeTransmitStreamProcessor::generateEmptyPacketData ( unsigned char *data, unsigned int *length) { /* If dry-running data is being sent, zero the data */ if (*length > 0) { memset(data, 0, *length); } return eCRV_OK; // no need to do anything } enum StreamProcessor::eChildReturnValue RmeTransmitStreamProcessor::generateSilentPacketHeader ( unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr ) { unsigned int cycle = CYCLE_TIMER_GET_CYCLES(pkt_ctr); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "XMIT SILENT: CY=%04u, TSP=%011" PRIu64 " (%04u)\n", cycle, m_last_timestamp, ( unsigned int ) TICKS_TO_CYCLES ( m_last_timestamp ) ); // A "silent" packet is identical to a regular data packet except all // audio data is set to zero. The requirements of the silent packet // for RME devices is still being confirmed. // The number of events per packet expected by the RME 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, even for packets containing // no audio data. *sy = 0x00; /* Assume the packet will be empty unless proven otherwise */ *length = 0; uint64_t presentation_time; uint64_t transmit_at_time; unsigned int transmit_at_cycle; int cycles_until_transmit; /* The sample buffer is not necessarily running when silent packets are * needed, so use m_last_timestamp (the timestamp of the previously sent * data packet) as the basis for the presentation time of the next * packet. Since we're only writing zeros we don't have to deal with * buffer xruns. */ float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getTicksPerFrame(); presentation_time = addTicks(m_last_timestamp, (unsigned int)lrintf(n_events * ticks_per_frame)); transmit_at_time = substractTicks(presentation_time, RME_TRANSMIT_TRANSFER_DELAY); transmit_at_cycle = (unsigned int)(TICKS_TO_CYCLES(transmit_at_time)); // Not currently used for silent packets: // unsigned int presentation_cycle = (unsigned int)(TICKS_TO_CYCLES(presentation_time)); // int cycles_until_presentation = diffCycles(presentation_cycle, cycle); cycles_until_transmit = diffCycles(transmit_at_cycle, cycle); if (cycles_until_transmit < 0) { // At this point we've theoretically missed the cycle at which // the data needs to be transmitted. Technically, if // cycles_until_presentation is greater than or equal to // RME_MIN_CYCLES_BEFORE_PRESENTATION we have an xrun. However, // since this is a silent packet there's no real harm in indicating // that a silent packet can still be sent; it's not as if a silent // packet consumes data. Furthermore, due to the fact that // presentation time is estimated from m_last_timestamp it's possible // that any xrun indication here is a false trigger. // // In any case, when silent packets are utilised during shutdown // an eCRV_XRun return is "invalid" (see StreamProcessor, function // getPacket(). Since silent packets are usually used during startup // and/or shutdown there's little to be gained by flagging xruns; all // they seem to do is prevent an otherwise clean shutdown. m_last_timestamp = presentation_time; m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp); if (m_tx_dbc > 0xff) m_tx_dbc -= 0x100; return eCRV_Packet; } else if (cycles_until_transmit <= RME_MAX_CYCLES_TO_TRANSMIT_EARLY) { m_last_timestamp = presentation_time; m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp); if (m_tx_dbc > 0xff) m_tx_dbc -= 0x100; return eCRV_Packet; } else { return eCRV_EmptyPacket; } return eCRV_Invalid; } enum StreamProcessor::eChildReturnValue RmeTransmitStreamProcessor::generateSilentPacketData ( unsigned char *data, unsigned int *length ) { // Simply set all audio data to zero since that's what's meant by // a "silent" packet. memset(data, 0, *length); return eCRV_OK; } unsigned int RmeTransmitStreamProcessor::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 RME 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(); *length = n_events*m_event_size; return n_events; } unsigned int RmeTransmitStreamProcessor::fillNoDataPacketHeader ( quadlet_t *data, unsigned int* length ) { *length = 0; return 0; } bool RmeTransmitStreamProcessor::prepareChild() { debugOutput ( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this ); m_max_fs_diff_norm = 10.0; m_max_diff_ticks = 30720; // Unsure whether this helps yet. Testing continues. m_dll_bandwidth_hz = 1.0; // 0.1; return true; } /* * compose the event streams for the packets from the port buffers */ bool RmeTransmitStreamProcessor::processWriteBlock(char *data, unsigned int nevents, unsigned int offset) { bool no_problem=true; for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { // If this port is disabled, unconditionally send it silence. if((*it)->isDisabled()) { if (encodeSilencePortToRmeEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode silence for disabled port %s to Rme events\n",(*it)->getName().c_str()); // Don't treat this as a fatal error at this point } continue; } Port *port=(*it); switch(port->getPortType()) { case Port::E_Audio: if (encodePortToRmeEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode port %s to Rme events\n",(*it)->getName().c_str()); no_problem=false; } break; case Port::E_Midi: if (encodePortToRmeMidiEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode port %s to Midi events\n",(*it)->getName().c_str()); no_problem=false; } break; default: // ignore break; } } return no_problem; } bool RmeTransmitStreamProcessor::transmitSilenceBlock(char *data, unsigned int nevents, unsigned int offset) { // This is the same as the non-silence version, except that is // doesn't read from the port buffers. bool no_problem = true; for ( PortVectorIterator it = m_Ports.begin(); it != m_Ports.end(); ++it ) { Port *port=(*it); switch(port->getPortType()) { case Port::E_Audio: if (encodeSilencePortToRmeEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode port %s to MBLA events\n",(*it)->getName().c_str()); no_problem = false; } break; case Port::E_Midi: if (encodeSilencePortToRmeMidiEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { debugWarning("Could not encode port %s to Midi events\n",(*it)->getName().c_str()); no_problem = false; } break; default: // ignore break; } } return no_problem; } int RmeTransmitStreamProcessor::encodePortToRmeEvents(RmeAudioPort *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 RME. // 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 RME // 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; quadlet_t *target; target = data + p->getPosition()/4; switch(m_StreamProcessorManager.getAudioDataType()) { default: case StreamProcessorManager::eADT_Int24: { quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); // Offset is in frames, but each port is only a single // channel, so the number of frames is the same as the // number of quadlets to offset (assuming the port buffer // uses one quadlet per sample, which is the case currently). buffer+=offset; for(j = 0; j < nevents; j += 1) { // Decode nsamples *target = Rme::ByteSwapToDevice32((*buffer & 0x00ffffff) << 8); buffer++; target+=m_event_size/4; } } break; case StreamProcessorManager::eADT_Float: { const float multiplier = (float)(0x7FFFFF); float *buffer=(float *)(p->getBufferAddress()); assert(nevents + offset <= p->getBufferSize()); buffer+=offset; for(j = 0; j < nevents; j += 1) { // decode max nsamples float in = *buffer; #if RME_CLIP_FLOATS if (unlikely(in > 1.0)) in = 1.0; if (unlikely(in < -1.0)) in = -1.0; #endif unsigned int v = lrintf(in * multiplier); *target = Rme::ByteSwapToDevice32(v << 8); buffer++; target+=m_event_size/4; } } break; } return 0; } int RmeTransmitStreamProcessor::encodeSilencePortToRmeEvents(RmeAudioPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j=0; quadlet_t *target = data + p->getPosition()/4; switch (m_StreamProcessorManager.getAudioDataType()) { default: case StreamProcessorManager::eADT_Int24: case StreamProcessorManager::eADT_Float: for (j = 0; j < nevents; j++) { *target = 0; target += m_event_size/4; } break; } return 0; } int RmeTransmitStreamProcessor::encodePortToRmeMidiEvents( RmeMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents) { unsigned int j; quadlet_t *src = (quadlet_t *)p->getBufferAddress(); src += offset; unsigned char *target = (unsigned char *)data + p->getPosition(); // Send a MIDI byte if there is one to send. RME MIDI data is sent using // a 3-byte sequence within a frame starting at the port's position. // A non-zero MSB indicates there is MIDI data to send. for (j=0; jgetPosition(); // For now, a "silent" MIDI event contains nothing but zeroes. This // may have to change if we find this isn't for some reason appropriate. for (j=0; j. * */ #ifndef __FFADO_RMETRANSMITSTREAMPROCESSOR__ #define __FFADO_RMETRANSMITSTREAMPROCESSOR__ /** * This class implements RME based streaming */ #include "debugmodule/debugmodule.h" #include "../generic/StreamProcessor.h" #include "../util/cip.h" namespace Streaming { class Port; class RmeAudioPort; class RmeMidiPort; /*! \brief The Base Class for an RME transmit stream processor This class implements a TransmitStreamProcessor that multiplexes Ports into RME streams. */ class RmeTransmitStreamProcessor : public StreamProcessor { public: /** * Create a RME transmit StreamProcessor */ RmeTransmitStreamProcessor(FFADODevice &parent, unsigned int model, unsigned int event_size); virtual ~RmeTransmitStreamProcessor() {}; enum eChildReturnValue generatePacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr); enum eChildReturnValue generatePacketData(unsigned char *data, unsigned int *length); enum eChildReturnValue generateEmptyPacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr); enum eChildReturnValue generateEmptyPacketData(unsigned char *data, unsigned int *length); enum eChildReturnValue generateSilentPacketHeader(unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr); enum eChildReturnValue generateSilentPacketData(unsigned char *data, unsigned int *length); virtual bool prepareChild(); bool resetForStreaming(); public: virtual unsigned int getEventSize() {return m_event_size;}; virtual unsigned int getMaxPacketSize(); virtual unsigned int getEventsPerFrame() { return 1; }; 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 encodePortToRmeEvents(RmeAudioPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); int encodeSilencePortToRmeEvents(RmeAudioPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); int encodePortToRmeMidiEvents( RmeMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents); int encodeSilencePortToRmeMidiEvents( RmeMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents); unsigned int m_rme_model; /* * An iso packet mostly consists of multiple events. m_event_size * is the size of a single 'event' in bytes. */ unsigned int m_event_size; // Keep track of transmission data block count unsigned int m_tx_dbc; // A simple circular buffer for outgoing MIDI data to allow // a rate control to be implemented on the data to suit the RME // devices. Note that this buffer's size is forced to be a power // of 2 to allow for buffer manipulation optimisations. // FIXME: it is yet to be determined whether this is necessary for // the RME device. #define MIDIBUFFER_SIZE_EXP 10 #define MIDIBUFFER_SIZE (1<. * */ #ifndef _IEC61883_CIP_PRIVATE_H #define _IEC61883_CIP_PRIVATE_H #include #include #include #define IEC61883_FMT_DV 0x00 #define IEC61883_FMT_AMDTP 0x10 #define IEC61883_FMT_MPEG2 0x20 #define CIP_TRANSFER_DELAY 9000 #ifdef __cplusplus extern "C" { #endif #if __BYTE_ORDER == __BIG_ENDIAN struct iec61883_packet { /* First quadlet */ uint8_t eoh0 : 2; uint8_t sid : 6; uint8_t dbs : 8; uint8_t fn : 2; uint8_t qpc : 3; uint8_t sph : 1; uint8_t reserved : 2; uint8_t dbc : 8; /* Second quadlet */ uint8_t eoh1 : 2; uint8_t fmt : 6; uint8_t fdf : 8; uint16_t syt : 16; unsigned char data[0]; }; #elif __BYTE_ORDER == __LITTLE_ENDIAN struct iec61883_packet { /* First quadlet */ uint8_t sid : 6; uint8_t eoh0 : 2; uint8_t dbs : 8; uint8_t reserved : 2; uint8_t sph : 1; uint8_t qpc : 3; uint8_t fn : 2; uint8_t dbc : 8; /* Second quadlet */ uint8_t fmt : 6; uint8_t eoh1 : 2; uint8_t fdf : 8; uint16_t syt : 16; unsigned char data[0]; }; #else #error Unknown bitfield type #endif /* * The TAG value is present in the isochronous header (first quadlet). It * provides a high level label for the format of data carried by the * isochronous packet. */ #define IEC61883_TAG_WITHOUT_CIP 0 /* CIP header NOT included */ #define IEC61883_TAG_WITH_CIP 1 /* CIP header included. */ #define IEC61883_TAG_RESERVED1 2 /* Reserved */ #define IEC61883_TAG_RESERVED2 3 /* Reserved */ #define IEC61883_FDF_NODATA 0xFF /* AM824 format definitions. */ #define IEC61883_FDF_AM824 0x00 #define IEC61883_FDF_AM824_CONTROLLED 0x04 #define IEC61883_FDF_SFC_MASK 0x03 #define IEC61883_AM824_LABEL 0x40 #define IEC61883_AM824_LABEL_RAW_24BITS 0x40 #define IEC61883_AM824_LABEL_RAW_20BITS 0x41 #define IEC61883_AM824_LABEL_RAW_16BITS 0x42 #define IEC61883_AM824_LABEL_RAW_RESERVED 0x43 #define IEC61883_AM824_VBL_24BITS 0x0 #define IEC61883_AM824_VBL_20BITS 0x1 #define IEC61883_AM824_VBL_16BITS 0x2 #define IEC61883_AM824_VBL_RESERVED 0x3 /* IEC-60958 format definitions. */ #define IEC60958_LABEL 0x0 #define IEC60958_PAC_B 0x3 /* Preamble Code 'B': Start of channel 1, at * the start of a data block. */ #define IEC60958_PAC_RSV 0x2 /* Preamble Code 'RESERVED' */ #define IEC60958_PAC_M 0x1 /* Preamble Code 'M': Start of channel 1 that * is not at the start of a data block. */ #define IEC60958_PAC_W 0x0 /* Preamble Code 'W': start of channel 2. */ #define IEC60958_DATA_VALID 0 /* When cleared means data is valid. */ #define IEC60958_DATA_INVALID 1 /* When set means data is not suitable for an ADC. */ struct iec61883_fraction { int integer; int numerator; int denominator; }; struct iec61883_cip { struct iec61883_fraction cycle_offset; struct iec61883_fraction ticks_per_syt_offset; struct iec61883_fraction ready_samples; struct iec61883_fraction samples_per_cycle; int dbc, dbs; int cycle_count; int cycle_count2; int mode; int syt_interval; int dimension; int rate; int fdf; int format; }; void iec61883_cip_init(struct iec61883_cip *cip, int format, int fdf, int rate, int dbs, int syt_interval); void iec61883_cip_set_transmission_mode(struct iec61883_cip *ptz, int mode); int iec61883_cip_get_max_packet_size(struct iec61883_cip *ptz); int iec61883_cip_fill_header(int node_id, struct iec61883_cip *cip, struct iec61883_packet *packet); int iec61883_cip_fill_header_nodata(int node_id, struct iec61883_cip *cip, struct iec61883_packet *packet); #ifdef __cplusplus } #endif #endif libffado-2.4.5/src/libstreaming/util/cip.c0000644000175000001440000001521710744621175020040 0ustar jwoitheusers/* * libiec61883 - Linux IEEE 1394 streaming media library. * Copyright (C) 2004 Kristian Hogsberg, Dan Dennedy, and Dan Maas. * This file written by Kristian Hogsberg. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "cip.h" #include #include /* Integer fractional math. When we transmit a 44k1Hz signal we must * send 5 41/80 samples per isochronous cycle, as these occur 8000 * times a second. Of course, we must send an integral number of * samples in a packet, so we use the integer math to alternate * between sending 5 and 6 samples per packet. */ static void fraction_init(struct iec61883_fraction *f, int numerator, int denominator) { f->integer = numerator / denominator; f->numerator = numerator % denominator; f->denominator = denominator; } static __inline__ void fraction_add(struct iec61883_fraction *dst, struct iec61883_fraction *src1, struct iec61883_fraction *src2) { /* assert: src1->denominator == src2->denominator */ int sum, denom; /* We use these two local variables to allow gcc to optimize * the division and the modulo into only one division. */ sum = src1->numerator + src2->numerator; denom = src1->denominator; dst->integer = src1->integer + src2->integer + sum / denom; dst->numerator = sum % denom; dst->denominator = denom; } static __inline__ void fraction_sub_int(struct iec61883_fraction *dst, struct iec61883_fraction *src, int integer) { dst->integer = src->integer - integer; dst->numerator = src->numerator; dst->denominator = src->denominator; } static __inline__ int fraction_floor(struct iec61883_fraction *frac) { return frac->integer; } static __inline__ int fraction_ceil(struct iec61883_fraction *frac) { return frac->integer + (frac->numerator > 0 ? 1 : 0); } void iec61883_cip_init(struct iec61883_cip *ptz, int format, int fdf, int rate, int dbs, int syt_interval) { const int transfer_delay = CIP_TRANSFER_DELAY; ptz->rate = rate; ptz->cycle_count = transfer_delay / 3072; ptz->cycle_count2 = 0; ptz->format = format; ptz->fdf = fdf; ptz->mode = IEC61883_MODE_BLOCKING_EMPTY; ptz->dbs = dbs; ptz->dbc = 0; ptz->syt_interval = syt_interval; fraction_init(&ptz->samples_per_cycle, ptz->rate, 8000); fraction_init(&ptz->ready_samples, 0, 8000); /* The ticks_per_syt_offset is initialized to the number of * ticks between syt_interval events. The number of ticks per * second is 24.576e6, so the number of ticks between * syt_interval events is 24.576e6 * syt_interval / rate. */ fraction_init(&ptz->ticks_per_syt_offset, 24576000 * ptz->syt_interval, ptz->rate); fraction_init(&ptz->cycle_offset, (transfer_delay % 3072) * ptz->rate, ptz->rate); } void iec61883_cip_set_transmission_mode(struct iec61883_cip *ptz, int mode) { ptz->mode = mode; } int iec61883_cip_get_max_packet_size(struct iec61883_cip *ptz) { int max_nevents; if (ptz->mode == IEC61883_MODE_BLOCKING_EMPTY || ptz->mode == IEC61883_MODE_BLOCKING_NODATA) max_nevents = ptz->syt_interval; else max_nevents = fraction_ceil(&ptz->samples_per_cycle); return max_nevents * ptz->dbs * 4 + 8; } int iec61883_cip_fill_header(int node_id, struct iec61883_cip *ptz, struct iec61883_packet *packet) { struct iec61883_fraction next; int nevents, nevents_dbc, syt_index, syt; fraction_add(&next, &ptz->ready_samples, &ptz->samples_per_cycle); if (ptz->mode == IEC61883_MODE_BLOCKING_EMPTY || ptz->mode == IEC61883_MODE_BLOCKING_NODATA) { if (fraction_floor(&next) >= ptz->syt_interval) nevents = ptz->syt_interval; else nevents = 0; } else nevents = fraction_floor(&next); if (ptz->mode == IEC61883_MODE_BLOCKING_NODATA) { /* The DBC is incremented even with NO_DATA packets. */ nevents_dbc = ptz->syt_interval; } else { nevents_dbc = nevents; } /* Now that we know how many events to put in the packet, update the * fraction ready_samples. */ fraction_sub_int(&ptz->ready_samples, &next, nevents); /* Calculate synchronization timestamp (syt). First we * determine syt_index, that is, the index in the packet of * the sample for which the timestamp is valid. */ syt_index = (ptz->syt_interval - ptz->dbc) & (ptz->syt_interval - 1); if (syt_index < nevents) { syt = ((ptz->cycle_count << 12) | fraction_floor(&ptz->cycle_offset)) & 0xffff; fraction_add(&ptz->cycle_offset, &ptz->cycle_offset, &ptz->ticks_per_syt_offset); /* This next addition should be modulo 8000 (0x1f40), * but we only use the lower 4 bits of cycle_count, so * we don't need the modulo. */ ptz->cycle_count += ptz->cycle_offset.integer / 3072; ptz->cycle_offset.integer %= 3072; } else syt = 0xffff; packet->eoh0 = 0; /* Our node ID can change after a bus reset, so it is best to fetch * our node ID for each packet. */ packet->sid = node_id & 0x3f; packet->dbs = ptz->dbs; packet->fn = 0; packet->qpc = 0; packet->sph = 0; packet->reserved = 0; packet->dbc = ptz->dbc; packet->eoh1 = 2; packet->fmt = ptz->format; if ( nevents == 0 && ptz->mode == IEC61883_MODE_BLOCKING_NODATA ) { /* FDF code for packets containing dummy data. */ packet->fdf = IEC61883_FDF_NODATA; } else { /* FDF code for non-blocking mode and for blocking mode with empty packets. */ packet->fdf = ptz->fdf; } packet->syt = htons(syt); ptz->dbc += nevents_dbc; return nevents; } // note that we don't implement timestamp increase for nodata // FIXME: check if this is standards compliant!! int iec61883_cip_fill_header_nodata(int node_id, struct iec61883_cip *ptz, struct iec61883_packet *packet) { packet->eoh0 = 0; /* Our node ID can change after a bus reset, so it is best to fetch * our node ID for each packet. */ packet->sid = node_id & 0x3f; packet->dbs = ptz->dbs; packet->fn = 0; packet->qpc = 0; packet->sph = 0; packet->reserved = 0; packet->dbc = ptz->dbc; packet->eoh1 = 2; packet->fmt = ptz->format; packet->fdf = IEC61883_FDF_NODATA; packet->syt = 0xffff; ptz->dbc += ptz->syt_interval; return 0; } libffado-2.4.5/src/libutil/0000755000175000001440000000000014206145613015115 5ustar jwoitheuserslibffado-2.4.5/src/libutil/Atomic.h0000644000175000001440000000557014206145246016513 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Copied from the jackd/jackdmp sources * function names changed in order to avoid naming problems when using this in * a jackd backend. */ /* Original license: * * Copyright (C) 2004-2006 Grame * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef __FFADOATOMIC__ #define __FFADOATOMIC__ #include static inline char CAS(volatile uint32_t value, uint32_t newvalue, volatile int32_t* addr) { return __sync_bool_compare_and_swap (addr, value, newvalue); } static inline long INC_ATOMIC(volatile int32_t* val) { int32_t actual; do { actual = *val; } while (!CAS(actual, actual + 1, val)); return actual; } static inline long DEC_ATOMIC(volatile int32_t* val) { int32_t actual; do { actual = *val; } while (!CAS(actual, actual - 1, val)); return actual; } static inline long ADD_ATOMIC(volatile int32_t* val, int32_t addval) { int32_t actual; do { actual = *val; } while (!CAS(actual, actual + addval, val)); return actual; } static inline long SUBSTRACT_ATOMIC(volatile int32_t* val, int32_t addval) { int32_t actual; do { actual = *val; } while (!CAS(actual, actual - addval, val)); return actual; } static inline long ZERO_ATOMIC(volatile int32_t* val) { int32_t actual; do { actual = *val; } while (!CAS(actual, 0, val)); return actual; } #endif // __FFADO_ATOMIC__ libffado-2.4.5/src/libutil/ByteSwap.h0000644000175000001440000001357314206145246017037 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_BYTESWAP__ #define __FFADO_BYTESWAP__ #include #include #include #include // to check for SSE etc... #include #define BYTESWAP32_CONST(x) ((((x) & 0x000000FF) << 24) | \ (((x) & 0x0000FF00) << 8) | \ (((x) & 0x00FF0000) >> 8) | \ (((x) & 0xFF000000) >> 24)) static inline uint64_t ByteSwap64(uint64_t d) { return bswap_64(d); } static inline uint32_t ByteSwap32(uint32_t d) { return bswap_32(d); } static inline uint16_t ByteSwap16(uint16_t d) { return bswap_16(d); } static inline void byteSwapBlock(quadlet_t *data, unsigned int nb_elements) { unsigned int i=0; for(; i static inline void byteSwapToBus(quadlet_t *data, unsigned int nb_elements) { // Work input until data reaches 16 byte alignment while ((((unsigned long)data) & 0xF) && nb_elements > 0) { *data = ByteSwap32(*data); data++; nb_elements--; } if(nb_elements == 0) { return; } assert((((unsigned long)data) & 0xF) == 0); // now do the SSE based conversion // we have to go from [A B C D] to [D C B A] // where A, B, C, D are bytes // // the algorithm is: // 1) [A B C D] => [B A D C] // 2) [B A D C] => [D C B A] // // i.e. first do a 2x(2x8bit) swap // then a 2x16bit swap __m128i v; while(nb_elements >= 4) { // prefetch the data for the next round __builtin_prefetch(data+128, 0, 0); // load the data into the vector unit v = _mm_load_si128((__m128i*)data); // do first swap v = _mm_or_si128( _mm_slli_epi16( v, 8 ), _mm_srli_epi16( v, 8 ) ); //swap it // do second swap v = _mm_or_si128( _mm_slli_epi32( v, 16 ), _mm_srli_epi32( v, 16 ) ); //swap it // store result _mm_store_si128 ((__m128i*)data, v); data += 4; nb_elements -= 4; } // and do the remaining ones while (nb_elements > 0) { *data = ByteSwap32(*data); data++; nb_elements--; } } static inline void byteSwapFromBus(quadlet_t *data, unsigned int nb_elements) { // Work input until data reaches 16 byte alignment while ((((unsigned long)data) & 0xF) && nb_elements > 0) { *data = ByteSwap32(*data); data++; nb_elements--; } if(nb_elements == 0) { return; } assert((((unsigned long)data) & 0xF) == 0); // now do the SSE based conversion // we have to go from [A B C D] to [D C B A] // where A, B, C, D are bytes // // the algorithm is: // 1) [A B C D] => [B A D C] // 2) [B A D C] => [D C B A] // // i.e. first do a 2x(2x8bit) swap // then a 2x16bit swap __m128i v; while(nb_elements >= 4) { // load the data into the vector unit v = _mm_load_si128((__m128i*)data); // do first swap v = _mm_or_si128( _mm_slli_epi16( v, 8 ), _mm_srli_epi16( v, 8 ) ); //swap it // do second swap v = _mm_or_si128( _mm_slli_epi32( v, 16 ), _mm_srli_epi32( v, 16 ) ); //swap it // store result _mm_store_si128 ((__m128i*)data, v); data += 4; nb_elements -= 4; } // and do the remaining ones while (nb_elements > 0) { *data = ByteSwap32(*data); data++; nb_elements--; } } #else static inline void byteSwapToBus(quadlet_t *data, unsigned int nb_elements) { byteSwapBlock(data, nb_elements); } static inline void byteSwapFromBus(quadlet_t *data, unsigned int nb_elements) { byteSwapBlock(data, nb_elements); } #endif // sse2 #endif // byte order #endif // h libffado-2.4.5/src/libutil/Configuration.cpp0000644000175000001440000004257114206145246020443 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "Configuration.h" #include #include using namespace libconfig; namespace Util { IMPL_DEBUG_MODULE( Configuration, Configuration, DEBUG_LEVEL_NORMAL ); Configuration::Configuration() { } Configuration::~Configuration() { while(m_ConfigFiles.size()) { delete m_ConfigFiles.back(); m_ConfigFiles.pop_back(); } } bool Configuration::openFile(std::string filename, enum eFileMode mode) { // check if not already open if(findFileName(filename) >= 0) { debugError("file already open\n"); return false; } ConfigFile *c = new ConfigFile(*this, filename, mode); switch(mode) { case eFM_ReadOnly: case eFM_ReadWrite: try { c->readFile(); } catch (FileIOException& e) { debugOutput(DEBUG_LEVEL_VERBOSE, "Could not open file: %s\n", filename.c_str()); delete c; return false; } catch (ParseException& e) { debugWarning("Could not parse file: %s\nError: %s, Line: %i, What: %s", filename.c_str(), e.getError(), e.getLine(), e.what()); delete c; return false; } catch (...) { debugWarning("Unknown exception when opening file: %s\n", filename.c_str()); delete c; return false; } break; default: break; } m_ConfigFiles.push_back(c); return true; } bool Configuration::closeFile(std::string filename) { int idx = findFileName(filename); if(idx >= 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "Closing config file: %s\n", filename.c_str()); ConfigFile *c = m_ConfigFiles.at(idx); m_ConfigFiles.erase(m_ConfigFiles.begin()+idx); delete c; return true; } else { debugError("file not open\n"); return false; } } bool Configuration::saveFile(std::string name) { int idx = findFileName(name); if(idx >= 0) { ConfigFile *c = m_ConfigFiles.at(idx); switch(c->getMode()) { case eFM_ReadOnly: debugOutput(DEBUG_LEVEL_VERBOSE, "Not saving readonly config file: %s\n", c->getName().c_str()); break; case eFM_Temporary: debugOutput(DEBUG_LEVEL_VERBOSE, "Not saving temporary config file: %s\n", c->getName().c_str()); break; case eFM_ReadWrite: debugOutput(DEBUG_LEVEL_VERBOSE, "Saving config file: %s\n", c->getName().c_str()); try { c->writeFile(); } catch (...) { debugError("Could not write file: %s\n", c->getName().c_str()); return false; } default: debugOutput(DEBUG_LEVEL_VERBOSE, "bad mode for file: %s\n", c->getName().c_str()); } } return true; } bool Configuration::save() { bool retval = true; for (unsigned int idx = 0; idx < m_ConfigFiles.size(); idx++) { ConfigFile *c = m_ConfigFiles.at(idx); switch(c->getMode()) { case eFM_ReadOnly: debugOutput(DEBUG_LEVEL_VERBOSE, "Not saving readonly config file: %s\n", c->getName().c_str()); break; case eFM_Temporary: debugOutput(DEBUG_LEVEL_VERBOSE, "Not saving temporary config file: %s\n", c->getName().c_str()); break; case eFM_ReadWrite: debugOutput(DEBUG_LEVEL_VERBOSE, "Saving config file: %s\n", c->getName().c_str()); try { c->writeFile(); } catch (...) { debugError("Could not write file: %s\n", c->getName().c_str()); retval = false; } default: debugOutput(DEBUG_LEVEL_VERBOSE, "bad mode for file: %s\n", c->getName().c_str()); } } return retval; } void Configuration::ConfigFile::showSetting(libconfig::Setting &s, std::string prefix) { unsigned int children = s.getLength(); Setting::Type t = s.getType(); switch(t) { case Setting::TypeGroup: debugOutput(DEBUG_LEVEL_NORMAL, " %sGroup: %s\n", prefix.c_str(), s.getName()); for(unsigned int i = 0; i < children; i++) { showSetting(s[i], prefix + " "); } break; case Setting::TypeList: debugOutput(DEBUG_LEVEL_NORMAL, " %sList: %s\n", prefix.c_str(), s.getName()); for(unsigned int i = 0; i < children; i++) { showSetting(s[i], prefix + " "); } break; case Setting::TypeArray: debugOutput(DEBUG_LEVEL_NORMAL, " %sArray: %s\n", prefix.c_str(), s.getName()); for(unsigned int i = 0; i < children; i++) { showSetting(s[i], prefix + " "); } break; case Setting::TypeInt: { int32_t i = s; debugOutput(DEBUG_LEVEL_NORMAL, " %s%s = %d (0x%08X)\n", prefix.c_str(), s.getName(), i, i); } break; case Setting::TypeInt64: { int64_t i = s; debugOutput(DEBUG_LEVEL_NORMAL, " %s%s = %" PRId64 " (0x%016" PRIX64 ")\n", prefix.c_str(), s.getName(), i, i); } break; case Setting::TypeFloat: { float f = s; debugOutput(DEBUG_LEVEL_NORMAL, " %s%s = %f\n", prefix.c_str(), s.getName(), f); } break; case Setting::TypeString: { std::string str = s; debugOutput(DEBUG_LEVEL_NORMAL, " %s%s = %s\n", prefix.c_str(), s.getName(), str.c_str()); } break; case Setting::TypeBoolean: { bool b = s; std::string str = (b?"true":"false"); debugOutput(DEBUG_LEVEL_NORMAL, " %s%s = %s\n", prefix.c_str(), s.getName(), str.c_str()); } break; default: { debugOutput(DEBUG_LEVEL_NORMAL, " %s%s = Unsupported Type\n", prefix.c_str(), s.getName()); } break; } } bool Configuration::getValueForSetting(std::string path, int32_t &ref) { libconfig::Setting *s = getSetting( path ); if(s) { // FIXME: this can be done using the libconfig methods Setting::Type t = s->getType(); if(t == Setting::TypeInt) { ref = *s; debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "path '%s' has value %d\n", path.c_str(), ref); return true; } else { debugWarning("path '%s' has wrong type\n", path.c_str()); return false; } } else { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "path '%s' not found\n", path.c_str()); return false; } } bool Configuration::getValueForSetting(std::string path, int64_t &ref) { libconfig::Setting *s = getSetting( path ); if(s) { // FIXME: this can be done using the libconfig methods Setting::Type t = s->getType(); if(t == Setting::TypeInt64) { ref = *s; debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "path '%s' has value %" PRId64 "\n", path.c_str(), ref); return true; } else { debugWarning("path '%s' has wrong type\n", path.c_str()); return false; } } else { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "path '%s' not found\n", path.c_str()); return false; } } bool Configuration::getValueForSetting(std::string path, float &ref) { libconfig::Setting *s = getSetting( path ); if(s) { // FIXME: this can be done using the libconfig methods Setting::Type t = s->getType(); if(t == Setting::TypeFloat) { ref = *s; debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "path '%s' has value %f\n", path.c_str(), ref); return true; } else { debugWarning("path '%s' has wrong type\n", path.c_str()); return false; } } else { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "path '%s' not found\n", path.c_str()); return false; } } libconfig::Setting * Configuration::getSetting( std::string path ) { for ( std::vector::iterator it = m_ConfigFiles.begin(); it != m_ConfigFiles.end(); ++it ) { ConfigFile *c = *it; try { Setting &s = c->lookup(path); return &s; } catch (...) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " %s has no setting %s\n", c->getName().c_str(), path.c_str()); } } return NULL; } bool Configuration::getValueForDeviceSetting(unsigned int vendor_id, unsigned model_id, std::string setting, int32_t &ref) { libconfig::Setting *s = getDeviceSetting( vendor_id, model_id ); if(s) { try { return s->lookupValue(setting, ref); } catch (...) { debugOutput(DEBUG_LEVEL_VERBOSE, "Setting %s not found\n", setting.c_str()); return false; } } else { debugOutput(DEBUG_LEVEL_VERBOSE, "device %X/%X not found\n", vendor_id, model_id); return false; } } bool Configuration::getValueForDeviceSetting(unsigned int vendor_id, unsigned model_id, std::string setting, int64_t &ref) { libconfig::Setting *s = getDeviceSetting( vendor_id, model_id ); if(s) { try { long long int refverylong = ref; return s->lookupValue(setting, refverylong); } catch (...) { debugOutput(DEBUG_LEVEL_VERBOSE, "Setting %s not found\n", setting.c_str()); return false; } } else { debugOutput(DEBUG_LEVEL_VERBOSE, "device %X/%X not found\n", vendor_id, model_id); return false; } } bool Configuration::getValueForDeviceSetting(unsigned int vendor_id, unsigned model_id, std::string setting, float &ref) { libconfig::Setting *s = getDeviceSetting( vendor_id, model_id ); if(s) { try { return s->lookupValue(setting, ref); } catch (...) { debugOutput(DEBUG_LEVEL_VERBOSE, "Setting %s not found\n", setting.c_str()); return false; } } else { debugOutput(DEBUG_LEVEL_VERBOSE, "device %X/%X not found\n", vendor_id, model_id); return false; } } libconfig::Setting * Configuration::getDeviceSetting( unsigned int vendor_id, unsigned model_id ) { for ( std::vector::iterator it = m_ConfigFiles.begin(); it != m_ConfigFiles.end(); ++it ) { ConfigFile *c = *it; try { Setting &list = c->lookup("device_definitions"); unsigned int children = list.getLength(); for(unsigned int i = 0; i < children; i++) { Setting &s = list[i]; try { Setting &vendorid = s["vendorid"]; Setting &modelid = s["modelid"]; uint32_t vid = vendorid; uint32_t mid = modelid; if (vendor_id == vid && model_id == mid) { debugOutput(DEBUG_LEVEL_VERBOSE, " device VME for %X:%x found in %s\n", vendor_id, model_id, c->getName().c_str()); c->showSetting(s); return &s; } } catch (...) { debugWarning("Bogus format\n"); } } } catch (...) { debugOutput(DEBUG_LEVEL_VERBOSE, " %s has no device definitions\n", c->getName().c_str()); } } return NULL; } Configuration::VendorModelEntry Configuration::findDeviceVME( unsigned int vendor_id, unsigned model_id ) { // FIXME: clean this pointer/reference mess please Setting *ps = getDeviceSetting(vendor_id, model_id); if(ps) { Setting &s = *ps; try { Setting &vendorid = s["vendorid"]; Setting &modelid = s["modelid"]; uint32_t vid = vendorid; uint32_t mid = modelid; if (vendor_id == vid && model_id == mid) { struct VendorModelEntry vme; vme.vendor_id = vendorid; vme.model_id = modelid; const char *tmp = s["vendorname"]; vme.vendor_name = tmp; tmp = s["modelname"]; vme.model_name = tmp; if (!s.lookupValue("driver", vme.driver)) { std::string driver = s["driver"]; vme.driver = convertDriver(driver); } return vme; } else { debugError("BUG: vendor/model found but not found?\n"); } } catch (...) { debugWarning("Bogus format\n"); } } struct VendorModelEntry invalid; return invalid; } bool Configuration::isDeviceVMEPresent( unsigned int vendor_id, unsigned model_id ) { return isValid(findDeviceVME( vendor_id, model_id )); } bool Configuration::isValid( const Configuration::VendorModelEntry& vme ) { struct VendorModelEntry invalid; return !(vme==invalid); } void Configuration::ConfigFile::show() { debugOutput(DEBUG_LEVEL_NORMAL, " config file: %s\n", getName().c_str()); Setting &root = getRoot(); if(root.getLength()) { showSetting(root, ""); } else { debugOutput(DEBUG_LEVEL_NORMAL, " Empty\n"); } } void Configuration::show() { debugOutput(DEBUG_LEVEL_NORMAL, "Configuration:\n"); for (unsigned int idx = 0; idx < m_ConfigFiles.size(); idx++) { ConfigFile *c = m_ConfigFiles.at(idx); c->show(); } } int Configuration::findFileName(std::string s) { int i=0; for ( std::vector::iterator it = m_ConfigFiles.begin(); it != m_ConfigFiles.end(); ++it ) { if((*it)->getName() == s) { return i; } i++; } return -1; } unsigned int Configuration::convertDriver(const std::string& driver) const { if(driver == "BEBOB") return eD_BeBoB; if(driver == "FIREWORKS") return eD_FireWorks; if(driver == "GENERICAVC") return eD_GenericAVC; if(driver == "OXFORD") return eD_Oxford; if(driver == "MOTU") return eD_MOTU; if(driver == "DICE") return eD_DICE; if(driver == "METRICHALO") return eD_MetricHalo; if(driver == "RME") return eD_RME; if(driver == "BOUNCE") return eD_Bounce; if(driver == "DIGIDESIGN") return eD_Digidesign; return eD_Unknown; // Unknown } void Configuration::ConfigFile::readFile() { std::string filename = m_name; // fix up the '~' as homedir std::string::size_type pos = filename.find_first_of("~"); if(pos != std::string::npos) { char *homedir = getenv("HOME"); if(homedir) { std::string home = homedir; filename.replace( pos, 1, home, 0, home.length()); } } Config::readFile(filename.c_str()); } void Configuration::ConfigFile::writeFile() { std::string filename = m_name; // fix up the '~' as homedir std::string::size_type pos = filename.find_first_of("~"); if(pos != std::string::npos) { char *homedir = getenv("HOME"); if(homedir) { std::string home = homedir; filename.replace( pos, 1, home, 0, home.length()); } } Config::writeFile(filename.c_str()); } Configuration::VendorModelEntry::VendorModelEntry() : vendor_id( 0 ) , model_id( 0 ) , driver( 0 ) { } Configuration::VendorModelEntry::VendorModelEntry( const VendorModelEntry& rhs ) : vendor_id( rhs.vendor_id ) , model_id( rhs.model_id ) , vendor_name( rhs.vendor_name ) , model_name( rhs.model_name ) , driver( rhs.driver ) { } Configuration::VendorModelEntry& Configuration::VendorModelEntry::operator = ( const VendorModelEntry& rhs ) { // check for assignment to self if ( this == &rhs ) return *this; vendor_id = rhs.vendor_id; model_id = rhs.model_id; vendor_name = rhs.vendor_name; model_name = rhs.model_name; driver = rhs.driver; return *this; } bool Configuration::VendorModelEntry::operator == ( const VendorModelEntry& rhs ) const { bool equal=true; equal &= (vendor_id == rhs.vendor_id); equal &= (model_id == rhs.model_id); equal &= (vendor_name == rhs.vendor_name); equal &= (model_name == rhs.model_name); equal &= (driver == rhs.driver); return equal; } Configuration::VendorModelEntry::~VendorModelEntry() { } } // namespace Util libffado-2.4.5/src/libutil/Configuration.h0000644000175000001440000001266514206145246020111 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef _FFADO_UTIL_CONFIGURATION_ #define _FFADO_UTIL_CONFIGURATION_ #include "debugmodule/debugmodule.h" #include "libconfig.h++" #include namespace Util { /** * A class that manages several configuration files * the idea is that you can have a system config file * and then a user-defined config file * * note: not thread safe! */ class Configuration { public: // driver ID's to be used in the config file // Note: When changing this enum, please update convertDriver code as well! enum eDrivers { eD_Unknown = 0, eD_BeBoB = 1, eD_FireWorks = 2, eD_GenericAVC = 3, eD_Oxford = 4, eD_MOTU = 10, eD_DICE = 20, eD_MetricHalo = 30, eD_RME = 40, eD_Bounce = 50, eD_Digidesign = 60, }; // the modes a config file can have enum eFileMode { eFM_ReadOnly, eFM_ReadWrite, eFM_Temporary, // this won't be saved to dist }; // struct to define the supported devices struct VendorModelEntry { VendorModelEntry(); VendorModelEntry(const VendorModelEntry& rhs); VendorModelEntry& operator = (const VendorModelEntry& rhs); bool operator == (const VendorModelEntry& rhs) const; virtual ~VendorModelEntry(); unsigned int vendor_id; unsigned int model_id; std::string vendor_name; std::string model_name; unsigned int driver; }; typedef std::vector VendorModelEntryVector; private: class ConfigFile : public libconfig::Config { public: ConfigFile(Configuration &c, std::string n, enum eFileMode mode = eFM_ReadOnly) : Config() , m_parent(c) , m_name( n ) , m_mode( mode ) , m_debugModule(c.m_debugModule) {}; ~ConfigFile() {}; void readFile(); void writeFile(); void show(); void showSetting(libconfig::Setting &, std::string prefix = ""); std::string getName() {return m_name;}; enum eFileMode getMode() {return m_mode;}; private: Configuration &m_parent; std::string m_name; enum eFileMode m_mode; private: DECLARE_DEBUG_MODULE_REFERENCE; }; public: Configuration(); virtual ~Configuration(); virtual bool openFile(std::string filename, enum eFileMode = eFM_ReadOnly); virtual bool closeFile(std::string filename); virtual bool saveFile(std::string filename); virtual bool save(); VendorModelEntry findDeviceVME( unsigned int vendor_id, unsigned model_id ); bool isDeviceVMEPresent( unsigned int vendor_id, unsigned model_id ); static bool isValid( const VendorModelEntry& vme ); // access functions /** * @brief retrieves a setting for a given path * * the value in the ref parameter is not changed if * the function returns false. * * @param path path to the setting * @param ref reference to the integer that will hold the value. * @return true if successful, false if not */ bool getValueForSetting(std::string path, int32_t &ref); bool getValueForSetting(std::string path, int64_t &ref); bool getValueForSetting(std::string path, float &ref); /** * @brief retrieves a setting for a given device * * the value in the ref parameter is not changed if * the function returns false. * * @param vendor_id vendor id for the device * @param model_id model id for the device * @param setting name of the setting * @param ref reference to the integer that will hold the value. * @return true if successful, false if not */ bool getValueForDeviceSetting(unsigned int vendor_id, unsigned model_id, std::string setting, int32_t &ref); bool getValueForDeviceSetting(unsigned int vendor_id, unsigned model_id, std::string setting, int64_t &ref); bool getValueForDeviceSetting(unsigned int vendor_id, unsigned model_id, std::string setting, float &ref); virtual void setVerboseLevel(int l) {setDebugLevel(l);}; virtual void show(); private: libconfig::Setting *getSetting( std::string path ); libconfig::Setting *getDeviceSetting( unsigned int vendor_id, unsigned model_id ); int findFileName(std::string s); unsigned int convertDriver(const std::string & driver) const; // important: keep 1-1 mapping for these two! // cannot use map since we need the vector order to // provide priorities std::vector m_ConfigFiles; DECLARE_DEBUG_MODULE; }; } // namespace Util #endif // _FFADO_UTIL_CONFIGURATION_ libffado-2.4.5/src/libutil/DelayLockedLoop.cpp0000644000175000001440000001362514206145246020644 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "DelayLockedLoop.h" namespace Util { /** * Creates a new Delay Locked Loop with a predefined order and * a predefined set of coefficients. * * Make sure coeffs is a float array containing order+1 coefficients. * * @pre order > 0 * @param order order of the DLL * @param coeffs coefficients to use */ DelayLockedLoop::DelayLockedLoop(unsigned int order, float *coeffs) { unsigned int i; m_order=order; if (m_order==0) m_order=1; m_coeffs=new float[order]; m_nodes=new float[order]; for (i=0;i 0 * @param order order of the DLL */ DelayLockedLoop::DelayLockedLoop(unsigned int order) { unsigned int i; m_order=order; if (m_order==0) m_order=1; m_coeffs=new float[order]; m_nodes=new float[order]; for (i=0;i 0 * @param order new order for the DLL * @param coeffs coefficients to use */ void DelayLockedLoop::setOrder(unsigned int order, float* coeffs) { unsigned int i; reset(); m_order=order; if (m_order==0) m_order=1; if(m_coeffs) delete[] m_coeffs; m_coeffs=new float[order]; if(m_nodes) delete[] m_nodes; m_nodes=new float[order]; for (i=0;i 0 * @param order new order for the DLL */ void DelayLockedLoop::setOrder(unsigned int order) { unsigned int i; reset(); m_order=order; if (m_order==0) m_order=1; if(m_coeffs) delete[] m_coeffs; m_coeffs=new float[order]; if(m_nodes) delete[] m_nodes; m_nodes=new float[order]; for (i=0;i. * */ #ifndef __FFADO_DELAYLOCKEDLOOP__ #define __FFADO_DELAYLOCKEDLOOP__ namespace Util { class DelayLockedLoop { public: DelayLockedLoop(unsigned int order, float *coeffs); DelayLockedLoop(unsigned int order); DelayLockedLoop(); virtual ~DelayLockedLoop(); float getCoefficient(unsigned int i); void setCoefficient(unsigned int i, float c); void setIntegrator(unsigned int i, float c); void reset(); unsigned int getOrder(); void setOrder(unsigned int i); void setOrder(unsigned int order, float* coeffs); void put(float v); float get(); float getError(); protected: unsigned int m_order; float *m_coeffs; float *m_nodes; float m_error; }; } // end of namespace FFADOUtil #endif /* __FFADO_DELAYLOCKEDLOOP__ */ libffado-2.4.5/src/libutil/Functors.h0000644000175000001440000001072514206145246017100 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_FUNCTORS__ #define __FFADO_FUNCTORS__ #include #include namespace Util { class Functor { public: Functor() {} virtual ~Functor() {} virtual void operator() () = 0; virtual bool matchCallee(void *) = 0; }; typedef std::vector FunctorVector; typedef std::vector::iterator FunctorVectorIterator; //////////////////////////////////////////////////////////////////////// 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; } } virtual bool matchCallee(void *p) { return p == (void *)m_pCallee; } 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; } } virtual bool matchCallee(void *p) { return p == (void *)m_pCallee; } private: CalleePtr m_pCallee; MemFunPtr m_pMemFun; Parm0 m_parm0; sem_t* m_pSem; bool m_bDelete; }; template< typename FunPtr > class CallbackFunctor0 : public Functor { public: CallbackFunctor0( FunPtr pMemFun, bool bDelete = true ) : m_pMemFun( pMemFun ) , m_pSem( 0 ) , m_bDelete( bDelete ) {} CallbackFunctor0( FunPtr pMemFun, sem_t* pSem, bool bDelete = true ) : m_pMemFun( pMemFun ) , m_pSem( pSem ) , m_bDelete( bDelete ) {} virtual ~CallbackFunctor0() {} virtual void operator() () { ( *m_pMemFun )(); if ( m_pSem ) { sem_post( m_pSem); } if (m_bDelete) { delete this; } } virtual bool matchCallee(void *p) { return false; } private: FunPtr m_pMemFun; sem_t* m_pSem; bool m_bDelete; }; }; // end of namespace Util #endif libffado-2.4.5/src/libutil/IpcRingBuffer.cpp0000644000175000001440000005302414206145246020314 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "IpcRingBuffer.h" #include "PosixMessageQueue.h" #include "PosixSharedMemory.h" #include "Mutex.h" #include "PosixMutex.h" #include "Functors.h" #include // FIXME: if we restrict the nb_blocks to a power of two, the overflows // can be implemented using masks namespace Util { IMPL_DEBUG_MODULE( IpcRingBuffer, IpcRingBuffer, DEBUG_LEVEL_VERBOSE ); IpcRingBuffer::IpcRingBuffer(std::string name, enum eBufferType type, enum eDirection dir, enum eBlocking blocking, unsigned int blocks, unsigned int block_size) : m_name(name) , m_blocks(blocks) , m_blocksize(block_size) , m_type( type ) , m_direction( dir ) , m_blocking( blocking ) , m_initialized( false ) , m_next_block( 1 ) , m_last_block_ack( 0 ) , m_idx( 1 ) , m_last_idx_ack( 0 ) , m_ping_queue( *(new PosixMessageQueue(name+":ping")) ) , m_pong_queue( *(new PosixMessageQueue(name+":pong")) ) , m_memblock( *(new PosixSharedMemory(name+":mem", blocks*block_size)) ) , m_access_lock( *(new PosixMutex()) ) , m_notify_functor( *(new MemberFunctor0< IpcRingBuffer*, void (IpcRingBuffer::*)() > ( this, &IpcRingBuffer::notificationHandler, false )) ) , m_block_requested_for_read( *(new PosixMutex()) ) , m_block_requested_for_write( *(new PosixMutex()) ) { m_ping_queue.setVerboseLevel(getDebugLevel()); m_pong_queue.setVerboseLevel(getDebugLevel()); m_memblock.setVerboseLevel(getDebugLevel()); m_access_lock.setVerboseLevel(getDebugLevel()); sem_init(&m_activity, 0, 0); } IpcRingBuffer::~IpcRingBuffer() { // make sure everyone is done with this // should not be necessary AFAIK m_access_lock.Lock(); m_initialized=false; delete &m_memblock; delete &m_ping_queue; delete &m_pong_queue; m_access_lock.Unlock(); delete &m_access_lock; delete &m_notify_functor; sem_destroy(&m_activity); } bool IpcRingBuffer::init() { if(m_initialized) { debugError("(%p, %s) Already initialized\n", this, m_name.c_str()); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) init %s\n", this, m_name.c_str()); debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) direction %d, %d blocks of %d bytes\n", this, m_direction, m_blocks, m_blocksize); switch(m_type) { case eBT_Master: // a master creates and owns all of the shared memory structures // for outward connections we write the data, else we read if(!m_memblock.Create( PosixSharedMemory::eD_ReadWrite )) { debugError("(%p, %s) Could not create memblock\n", this, m_name.c_str()); return false; } m_memblock.LockInMemory(true); // for outward connections we do the pinging, else // we do the pong-ing. note that for writing, we open read-write // in order to be able to dequeue when the queue is full if(!m_ping_queue.Create( (m_direction == eD_Outward ? PosixMessageQueue::eD_ReadWrite : PosixMessageQueue::eD_ReadOnly), (m_blocking==eB_Blocking ? PosixMessageQueue::eB_Blocking : PosixMessageQueue::eB_NonBlocking) )) { debugError("(%p, %s) Could not create ping queue\n", this, m_name.c_str()); return false; } if(!m_pong_queue.Create( (m_direction == eD_Outward ? PosixMessageQueue::eD_ReadOnly : PosixMessageQueue::eD_ReadWrite), (m_blocking==eB_Blocking ? PosixMessageQueue::eB_Blocking : PosixMessageQueue::eB_NonBlocking) )) { debugError("(%p, %s) Could not create pong queue\n", this, m_name.c_str()); return false; } break; case eBT_Slave: // a slave only opens the shared memory structures // for outward connections we write the data, else we read if(!m_memblock.Open( (m_direction == eD_Outward ? PosixSharedMemory::eD_ReadWrite : PosixSharedMemory::eD_ReadOnly) )) { debugError("(%p, %s) Could not open memblock\n", this, m_name.c_str()); return false; } m_memblock.LockInMemory(true); // for outward connections we do the pinging, else // we do the pong-ing. note that for writing, we open read-write // in order to be able to dequeue when the queue is full if(!m_ping_queue.Open( (m_direction == eD_Outward ? PosixMessageQueue::eD_ReadWrite : PosixMessageQueue::eD_ReadOnly), (m_blocking==eB_Blocking ? PosixMessageQueue::eB_Blocking : PosixMessageQueue::eB_NonBlocking) )) { debugError("(%p, %s) Could not open ping queue\n", this, m_name.c_str()); return false; } if(!m_pong_queue.Open( (m_direction == eD_Outward ? PosixMessageQueue::eD_ReadOnly : PosixMessageQueue::eD_ReadWrite), (m_blocking==eB_Blocking ? PosixMessageQueue::eB_Blocking : PosixMessageQueue::eB_NonBlocking) )) { debugError("(%p, %s) Could not open pong queue\n", this, m_name.c_str()); return false; } break; } // if we are on the sending end of the buffer, we need a notifier // on the pong queue // the receiving end is driven by the messages in the ping queue if(m_direction == eD_Outward) { if(!m_pong_queue.setNotificationHandler(&m_notify_functor)) { debugError("Could not set Notification Handler\n"); return false; } // enable the handler if(!m_pong_queue.enableNotification()) { debugError("Could not enable notification\n"); } // now clear the queue to eliminate messages that might be there // from earlier runs m_pong_queue.Clear(); } else { // if we are on the receiving end, clear any waiting messages in the ping // queue m_ping_queue.Clear(); } m_initialized = true; return true; } void IpcRingBuffer::notificationHandler() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) IpcRingBuffer %s\n", this, m_name.c_str()); // prevent multiple access MutexLockHelper lock(m_access_lock); // The first thing we do is re-enable the handler // it is not going to be called since there are messages in the queue // first enabling the handler, then reading all received messages will // ensure that we either read or get notified of any message that arrives // while this handler is running if(!m_pong_queue.enableNotification()) { debugError("Could not re-enable notification\n"); } // no need for a lock protecting the pong queue as long as we are the only ones reading it // while we have messages to read, read them while(m_pong_queue.canReceive()) { // message placeholder IpcMessage m_ack = IpcMessage(); // FIXME: stack allocation not strictly RT safe // read ping message (blocks) enum PosixMessageQueue::eResult msg_res; msg_res = m_pong_queue.Receive(m_ack); switch(msg_res) { case PosixMessageQueue::eR_OK: break; default: // we were just notified, anything except OK is an error debugError("Could not read from ping queue\n"); } IpcMessage::eMessageType type = m_ack.getType(); if(type == IpcMessage::eMT_DataAck) { // get a view on the data struct DataWrittenMessage* data = reinterpret_cast(m_ack.getDataPtr()); debugOutput(DEBUG_LEVEL_VERBOSE, "Received ack idx %d at id %d\n", data->idx, data->id); // check counters unsigned int expected_block_ack = m_last_block_ack+1; if(expected_block_ack == m_blocks) expected_block_ack = 0; if(data->id != expected_block_ack) { debugWarning("unexpected block id: %d (expected %d)\n", data->id, expected_block_ack); } unsigned int expected_block_idx = m_last_idx_ack+1; //will auto-overflow if(data->idx != expected_block_idx) { debugWarning("unexpected block idx: %d (expected %d)\n", data->idx, expected_block_idx); } // prepare the next expected values // this is the only value used (and written in case of error) in the other thread m_last_block_ack = data->id; // this is not used m_last_idx_ack = data->idx; // signal activity if(m_blocking == eB_Blocking) { sem_post(&m_activity); } } else { debugError("Invalid message received (type %d)\n", type); } } } unsigned int IpcRingBuffer::getBufferFill() { // the next pointer is last_written+1 // so the bufferfill = last_written - last_ack // = last_written+1 - last_ack - 1 // last_ack is always <= last_written. if not, // last_written has wrapped // => wrap if: last_written < last_ack // or last_written+1 < last_ack+1 // or m_next_block < last_ack+1 // unwrap if this happens int bufferfill = m_next_block - m_last_block_ack - 1; if(m_next_block <= m_last_block_ack) { bufferfill += m_blocks; } assert(bufferfill>=0); debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) fill: %d\n", this, m_name.c_str(), bufferfill); return (unsigned int)bufferfill; } enum IpcRingBuffer::eResult IpcRingBuffer::requestBlockForWrite(void **block) { if(!m_block_requested_for_write.TryLock()) { debugError("Already a block requested for write\n"); return eR_Error; } // check if we can write a message // we can send when: // - we are not overwriting // AND - we are in blocking mode and // - OR (we are in non-blocking mode and there is space) if(m_blocking == eB_Blocking) { if(getBufferFill() >= m_blocks) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) full\n", this, m_name.c_str()); // make it wait sem_wait(&m_activity); } } else { // there are no free data blocks, or there is no message space if(getBufferFill() >= m_blocks || !m_ping_queue.canSend()) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) full\n", this, m_name.c_str()); m_block_requested_for_write.Unlock(); return eR_Again; } } // check for overflow if(m_next_block == m_last_block_ack) { debugWarning("Overwriting not yet read block %u\n", m_next_block); // we have to increment the block_read pointer // in order to keep consistency m_last_block_ack++; if(m_last_block_ack == m_blocks) { m_last_block_ack = 0; } } int offset = m_next_block * m_blocksize; *block = m_memblock.requestBlock(offset, m_blocksize); if(*block) { // keep the lock, to be released by releaseBlockForWrite return eR_OK; } else { m_block_requested_for_write.Unlock(); return eR_Error; } } enum IpcRingBuffer::eResult IpcRingBuffer::releaseBlockForWrite() { if(!m_block_requested_for_write.isLocked()) { debugError("No block requested for write\n"); return eR_Error; } IpcMessage &m = m_LastDataMessageSent; // prepare ping message m.setType(IpcMessage::eMT_DataWritten); m.setDataSize(sizeof(struct DataWrittenMessage)); // get a view on the data struct DataWrittenMessage* data = reinterpret_cast(m.getDataPtr()); // set the data contents data->id = m_next_block; data->idx = m_idx; debugOutput(DEBUG_LEVEL_VERBOSE, "Releasing block idx %d at id %d\n", data->idx, data->id); // send ping message enum PosixMessageQueue::eResult msg_res; msg_res = m_ping_queue.Send(m); switch(msg_res) { case PosixMessageQueue::eR_OK: break; case PosixMessageQueue::eR_Again: // this is a bug since we checked whether it was empty or not debugError("Bad response value\n"); m_block_requested_for_write.Unlock(); return eR_Error; case PosixMessageQueue::eR_Timeout: debugOutput(DEBUG_LEVEL_VERBOSE, "Timeout\n"); m_block_requested_for_write.Unlock(); return eR_Timeout; // blocking and no space on time default: debugError("Could not send to ping queue\n"); m_block_requested_for_write.Unlock(); return eR_Error; } // increment and wrap m_next_block++; if(m_next_block == m_blocks) { m_next_block = 0; } m_idx++; m_block_requested_for_write.Unlock(); return eR_OK; } enum IpcRingBuffer::eResult IpcRingBuffer::Write(char *block) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) IpcRingBuffer\n", this, m_name.c_str()); if(m_direction == eD_Inward) { debugError("Cannot write to inbound buffer\n"); return eR_Error; } enum IpcRingBuffer::eResult msg_res; void *xmit_block; // request a block for reading msg_res = requestBlockForWrite(&xmit_block); if(msg_res == eR_OK) { // if we receive a eR_OK, we should always be able to write to the shared memory memcpy(xmit_block, block, m_blocksize); releaseBlockForWrite(); } return msg_res; } // wait for a block of space to be available enum IpcRingBuffer::eResult IpcRingBuffer::waitForWrite() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) IpcRingBuffer\n", this, m_name.c_str()); while(getBufferFill() >= m_blocks-1) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) full\n", this, m_name.c_str()); // wait sem_wait(&m_activity); } return eR_OK; } enum IpcRingBuffer::eResult IpcRingBuffer::requestBlockForRead(void **block) { if(!m_block_requested_for_read.TryLock()) { debugError("Already a block requested for read\n"); return eR_Error; } // message placeholder IpcMessage &m = m_LastDataMessageReceived; // read ping message (blocks) enum PosixMessageQueue::eResult msg_res; msg_res = m_ping_queue.Receive(m); switch(msg_res) { case PosixMessageQueue::eR_OK: break; case PosixMessageQueue::eR_Again: m_block_requested_for_read.Unlock(); return eR_Again; // non-blocking and no message case PosixMessageQueue::eR_Timeout: debugOutput(DEBUG_LEVEL_VERBOSE, "Timeout\n"); m_block_requested_for_read.Unlock(); return eR_Timeout; // blocking and no message on time default: debugError("Could not read from ping queue\n"); m_block_requested_for_read.Unlock(); return eR_Error; } IpcMessage::eMessageType type = m.getType(); if(type == IpcMessage::eMT_DataWritten) { // get a view on the data struct DataWrittenMessage* data = reinterpret_cast(m.getDataPtr()); debugOutput(DEBUG_LEVEL_VERBOSE, "Requested block idx %d at id %d\n", data->idx, data->id); // check counters if(data->id != m_next_block) { debugWarning("unexpected block id: %d (expected %d)\n", data->id, m_next_block); } if(data->idx != m_idx) { debugWarning("unexpected block idx: %d (expected %d)\n", data->idx, m_idx); } int offset = data->id * m_blocksize; *block = m_memblock.requestBlock(offset, m_blocksize); if(*block) { // keep the mutex locked, we expect the thread that grabbed the block to also return it return eR_OK; } else { m_block_requested_for_read.Unlock(); return eR_Error; } } else { debugError("Invalid message received (type %d)\n", type); m_block_requested_for_read.Unlock(); return eR_Error; } } enum IpcRingBuffer::eResult IpcRingBuffer::releaseBlockForRead() { if(!m_block_requested_for_read.isLocked()) { debugError("No block requested for read\n"); return eR_Error; } IpcMessage &m = m_LastDataMessageReceived; // get a view on the data struct DataWrittenMessage* data = reinterpret_cast(m.getDataPtr()); debugOutput(DEBUG_LEVEL_VERBOSE, "Releasing block idx %d at id %d\n", data->idx, data->id); // write a response to the pong queue // reuse the message m.setType(IpcMessage::eMT_DataAck); enum PosixMessageQueue::eResult msg_res; msg_res = m_pong_queue.Send(m); switch(msg_res) { case PosixMessageQueue::eR_OK: break; case PosixMessageQueue::eR_Again: m_block_requested_for_read.Unlock(); // FIXME: this is not very correct debugOutput(DEBUG_LEVEL_VERBOSE, "Again on ACK\n"); return eR_Again; // non-blocking and no message case PosixMessageQueue::eR_Timeout: m_block_requested_for_read.Unlock(); debugOutput(DEBUG_LEVEL_VERBOSE, "Timeout on ACK\n"); return eR_Timeout; // blocking and no message on time default: debugError("Could not write to pong queue\n"); m_block_requested_for_read.Unlock(); return eR_Error; } // prepare the next expected values m_next_block = data->id + 1; if(m_next_block == m_blocks) { m_next_block = 0; } m_idx = data->idx + 1; m_block_requested_for_read.Unlock(); return eR_OK; } enum IpcRingBuffer::eResult IpcRingBuffer::Read(char *block) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) IpcRingBuffer %s\n", this, m_name.c_str()); if(m_direction == eD_Outward) { debugError("Cannot read from outward buffer\n"); return eR_Error; } enum IpcRingBuffer::eResult msg_res; void *rcv_block; // request a block for reading msg_res = requestBlockForRead(&rcv_block); if(msg_res == eR_OK) { // if we receive a eR_OK, we should always be able to read the shared memory memcpy(block, rcv_block, m_blocksize); releaseBlockForRead(); } return msg_res; } // wait for a block of data to be available enum IpcRingBuffer::eResult IpcRingBuffer::waitForRead() { while(getBufferFill() == 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) empty\n", this, m_name.c_str()); // FIXME: this will work only when no non-data messages are sent m_ping_queue.Wait(); } return eR_OK; } void IpcRingBuffer::show() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) IpcRingBuffer %s\n", this, m_name.c_str()); } void IpcRingBuffer::setVerboseLevel(int i) { setDebugLevel(i); debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) verbose: %d\n", this, m_name.c_str(), i); m_ping_queue.setVerboseLevel(i); m_pong_queue.setVerboseLevel(i); m_memblock.setVerboseLevel(i); m_access_lock.setVerboseLevel(i); } bool IpcRingBuffer::IpcMessage::serialize(char *buff) { memcpy(buff, &m_header, sizeof(m_header)); buff += sizeof(m_header); memcpy(buff, m_data, m_data_len); return true; } bool IpcRingBuffer::IpcMessage::deserialize(const char *buff, unsigned int length, unsigned prio) { assert(length >= sizeof(m_header)); memcpy(&m_header, buff, sizeof(m_header)); if(m_header.magic != FFADO_IPC_RINGBUFFER_MAGIC) { return false; // invalid magic } if(m_header.version != FFADO_IPC_RINGBUFFER_VERSION) { return false; // invalid version } m_data_len = length - sizeof(m_header); buff += sizeof(m_header); assert(m_data_len <= FFADO_IPC_MAX_MESSAGE_SIZE); memcpy(m_data, buff, m_data_len); m_priority = prio; return true; } } // Util libffado-2.4.5/src/libutil/IpcRingBuffer.h0000644000175000001440000001412314206145246017756 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __UTIL_IPC_RINGBUFFER__ #define __UTIL_IPC_RINGBUFFER__ #include "debugmodule/debugmodule.h" #include #define FFADO_IPC_RINGBUFFER_MAGIC 0x57439812 #define FFADO_IPC_RINGBUFFER_VERSION 0 #define FFADO_IPC_MAX_MESSAGE_SIZE 16 #include "PosixMessageQueue.h" #include namespace Util { /** * @brief A Ringbuffer for IPC use. */ class PosixMessageQueue; class PosixSharedMemory; class Mutex; class Functor; class IpcRingBuffer { // the message types protected: enum eMessagePriorities { eMP_Realtime = 10, }; // these are the views on the message contents struct DataWrittenMessage { unsigned int idx; unsigned int id; }; class IpcMessage : public PosixMessageQueue::Message { public: enum eMessageType { eMT_Unknown = 0, eMT_DataWritten = 1, eMT_DataAck = 2, }; IpcMessage() : Message() , m_priority( 0 ) , m_data_len( 0 ) { m_header.magic = FFADO_IPC_RINGBUFFER_MAGIC; m_header.version = FFADO_IPC_RINGBUFFER_VERSION; m_header.type = eMT_Unknown; }; IpcMessage(enum eMessageType t) : Message() , m_priority( 0 ) , m_data_len( 0 ) { m_header.magic = FFADO_IPC_RINGBUFFER_MAGIC; m_header.version = FFADO_IPC_RINGBUFFER_VERSION; m_header.type = t; }; virtual ~IpcMessage() {}; virtual unsigned getPriority() {return m_priority;}; virtual unsigned int getLength() {return sizeof(m_header) + sizeof(m_data);}; virtual bool serialize(char *buff); virtual bool deserialize(const char *buff, unsigned int length, unsigned prio); void setType(enum eMessageType t) {m_header.type=t;}; enum eMessageType getType() {return m_header.type;}; char *getDataPtr() {return m_data;}; void setDataSize(unsigned int i) { assert(i. * */ #ifndef __MUTEX__ #define __MUTEX__ namespace Util { /** * @brief The mutex interface. */ class Mutex { public: Mutex() {}; virtual ~Mutex() {}; virtual void Lock() = 0; virtual bool TryLock() = 0; virtual void Unlock() = 0; virtual bool isLocked() = 0; virtual void show() = 0; virtual void setVerboseLevel(int) = 0; }; /** * @brief A class to implement monitors * Locks a mutex when an instance is created, * unlocks it as soon as the instance is destroyed. * when this class is created on the stack at function * entry, this implements a monitor */ class MutexLockHelper { public: MutexLockHelper(Mutex &m) : m_mutex( m ) , m_early_unlocked(false) {m.Lock();}; virtual ~MutexLockHelper() {if(!m_early_unlocked) m_mutex.Unlock();}; /** * Allows to unlock the mutex before the object is * destroyed */ void earlyUnlock() {m_early_unlocked=true; m_mutex.Unlock();}; private: Mutex &m_mutex; bool m_early_unlocked; }; } // end of namespace #endif libffado-2.4.5/src/libutil/OptionContainer.cpp0000644000175000001440000003037214206145246020743 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "OptionContainer.h" #include #include namespace Util { IMPL_DEBUG_MODULE( OptionContainer, OptionContainer, DEBUG_LEVEL_NORMAL ); OptionContainer::Option::Option() : m_Name(""), m_stringValue(""), m_boolValue(false), m_doubleValue(0.0), m_intValue(0), m_uintValue(0), m_Type(EInvalid) {} OptionContainer::Option::Option(std::string n) : m_Name(n), m_stringValue(""), m_boolValue(false), m_doubleValue(0.0), m_intValue(0), m_uintValue(0), m_Type(EInvalid) {} OptionContainer::Option::Option(std::string n, std::string v) : m_Name(n), m_stringValue(v), m_boolValue(false), m_doubleValue(0.0), m_intValue(0), m_uintValue(0), m_Type(EString) {} OptionContainer::Option::Option(std::string n, bool v) : m_Name(n), m_stringValue(""), m_boolValue(v), m_doubleValue(0.0), m_intValue(0), m_uintValue(0), m_Type(EBool) {} OptionContainer::Option::Option(std::string n, double v) : m_Name(n), m_stringValue(""), m_boolValue(false), m_doubleValue(v), m_intValue(0), m_uintValue(0), m_Type(EDouble) {} OptionContainer::Option::Option(std::string n, int64_t v) : m_Name(n), m_stringValue(""), m_boolValue(false), m_doubleValue(0.0), m_intValue(v), m_uintValue(0), m_Type(EInt) {} OptionContainer::Option::Option(std::string n, uint64_t v) : m_Name(n), m_stringValue(""), m_boolValue(false), m_doubleValue(0.0), m_intValue(0), m_uintValue(v), m_Type(EUInt) {} void OptionContainer::Option::set(std::string v) { m_stringValue = v; m_Type=EString;} void OptionContainer::Option::set(bool v) { m_boolValue = v; m_Type=EBool;} void OptionContainer::Option::set(double v) { m_doubleValue = v; m_Type=EDouble;} void OptionContainer::Option::set(int64_t v) { m_intValue = v; m_Type=EInt;} void OptionContainer::Option::set(uint64_t v) { m_uintValue = v; m_Type=EUInt;} bool OptionContainer::Option::serialize( std::string basePath, Util::IOSerialize& ser ) const { bool result; result = ser.write( basePath + "m_Name", std::string(m_Name) ); result &= ser.write( basePath + "m_stringValue", std::string(m_stringValue) ); result &= ser.write( basePath + "m_boolValue", m_boolValue ); result &= ser.write( basePath + "m_doubleValue", m_doubleValue ); result &= ser.write( basePath + "m_intValue", m_intValue ); result &= ser.write( basePath + "m_uintValue", m_uintValue ); result &= ser.write( basePath + "m_Type", m_Type ); return result; } OptionContainer::Option OptionContainer::Option::deserialize( std::string basePath, Util::IODeserialize& deser ) { bool result; Option op=Option(); std::string tmpstr; result = deser.read( basePath + "m_Name", tmpstr ); op.m_Name = tmpstr; result &= deser.read( basePath + "m_stringValue", tmpstr ); op.m_stringValue = tmpstr; result &= deser.read( basePath + "m_boolValue", op.m_boolValue ); result &= deser.read( basePath + "m_doubleValue", op.m_doubleValue ); result &= deser.read( basePath + "m_intValue", op.m_intValue ); result &= deser.read( basePath + "m_uintValue", op.m_uintValue ); result &= deser.read( basePath + "m_Type", op.m_Type ); if(result) { return op; } else { return Option(); } } // ------------------------ OptionContainer::OptionContainer() { } OptionContainer::~OptionContainer() { } // -------------- SETTERS -------------------- bool OptionContainer::setOption(std::string name, std::string v) { Option o=getOption(name); if (o.getType()==OptionContainer::Option::EInvalid) return false; o.set(v); return setOption(o); } bool OptionContainer::setOption(std::string name, bool v) { Option o=getOption(name); if (o.getType()==OptionContainer::Option::EInvalid) return false; o.set(v); return setOption(o); } bool OptionContainer::setOption(std::string name, double v) { Option o=getOption(name); if (o.getType()==OptionContainer::Option::EInvalid) return false; o.set(v); return setOption(o); } bool OptionContainer::setOption(std::string name, int64_t v) { Option o=getOption(name); if (o.getType()==OptionContainer::Option::EInvalid) return false; o.set((int64_t)v); return setOption(o); } bool OptionContainer::setOption(std::string name, uint64_t v) { Option o=getOption(name); if (o.getType()==OptionContainer::Option::EInvalid) return false; o.set((uint64_t)v); return setOption(o); } bool OptionContainer::setOption(std::string name, int32_t v) { Option o=getOption(name); if (o.getType()==OptionContainer::Option::EInvalid) return false; o.set((int64_t)v); return setOption(o); } bool OptionContainer::setOption(std::string name, uint32_t v) { Option o=getOption(name); if (o.getType()==OptionContainer::Option::EInvalid) return false; o.set((uint64_t)v); return setOption(o); } bool OptionContainer::setOption(std::string name, int16_t v) { Option o=getOption(name); if (o.getType()==OptionContainer::Option::EInvalid) return false; o.set((int64_t)v); return setOption(o); } bool OptionContainer::setOption(std::string name, uint16_t v) { Option o=getOption(name); if (o.getType()==OptionContainer::Option::EInvalid) return false; o.set((uint64_t)v); return setOption(o); } bool OptionContainer::setOption(std::string name, int8_t v) { Option o=getOption(name); if (o.getType()==OptionContainer::Option::EInvalid) return false; o.set((int64_t)v); return setOption(o); } bool OptionContainer::setOption(std::string name, uint8_t v) { Option o=getOption(name); if (o.getType()==OptionContainer::Option::EInvalid) return false; o.set((uint64_t)v); return setOption(o); } // -------------- GETTERS -------------------- bool OptionContainer::getOption(std::string name, std::string &v) { Option o=getOption(name); if (o.getType()!=OptionContainer::Option::EString) return false; v=o.getString(); return true; } bool OptionContainer::getOption(std::string name, bool &v) { Option o=getOption(name); if (o.getType()!=OptionContainer::Option::EBool) return false; v=o.getBool(); return true; } bool OptionContainer::getOption(std::string name, double &v) { Option o=getOption(name); if (o.getType()!=OptionContainer::Option::EDouble) return false; v=o.getDouble(); return true; } bool OptionContainer::getOption(std::string name, float &v) { Option o=getOption(name); if (o.getType()!=OptionContainer::Option::EDouble) return false; v=o.getDouble(); return true; } bool OptionContainer::getOption(std::string name, int64_t &v) { Option o=getOption(name); if (o.getType()!=OptionContainer::Option::EInt) return false; v=o.getInt(); return true; } bool OptionContainer::getOption(std::string name, int32_t &v) { Option o=getOption(name); if (o.getType()!=OptionContainer::Option::EInt) return false; v=o.getInt(); return true; } bool OptionContainer::getOption(std::string name, int16_t &v) { Option o=getOption(name); if (o.getType()!=OptionContainer::Option::EInt) return false; v=o.getInt(); return true; } bool OptionContainer::getOption(std::string name, int8_t &v) { Option o=getOption(name); if (o.getType()!=OptionContainer::Option::EInt) return false; v=o.getInt(); return true; } bool OptionContainer::getOption(std::string name, uint64_t &v) { Option o=getOption(name); if (o.getType()!=OptionContainer::Option::EUInt) return false; v=o.getUInt(); return true; } bool OptionContainer::getOption(std::string name, uint32_t &v) { Option o=getOption(name); if (o.getType()!=OptionContainer::Option::EUInt) return false; v=o.getUInt(); return true; } bool OptionContainer::getOption(std::string name, uint16_t &v) { Option o=getOption(name); if (o.getType()!=OptionContainer::Option::EUInt) return false; v=o.getUInt(); return true; } bool OptionContainer::getOption(std::string name, uint8_t &v) { Option o=getOption(name); if (o.getType()!=OptionContainer::Option::EUInt) return false; v=o.getUInt(); return true; } OptionContainer::Option::EType OptionContainer::getOptionType(std::string name) { Option o=getOption(name); return o.getType(); } OptionContainer::Option OptionContainer::getOption(std::string name) { int i=findOption(name); if (i<0) { return Option(); } else { return m_Options.at(i); } } bool OptionContainer::addOption(Option o) { if (o.getType()==OptionContainer::Option::EInvalid) { return false; } if (hasOption(o)){ return false; } m_Options.push_back(o); return true; } bool OptionContainer::setOption(Option o) { int i=findOption(o); if (i<0) { return false; } else { m_Options.erase(m_Options.begin()+i); m_Options.push_back(o); return true; } } bool OptionContainer::removeOption(Option o) { int i=findOption(o); if (i<0) { return false; } else { m_Options.erase(m_Options.begin()+i); return true; } } bool OptionContainer::removeOption(std::string name) { int i=findOption(name); if (i<0) { return false; } else { m_Options.erase(m_Options.begin()+i); return true; } } bool OptionContainer::hasOption(std::string name) { return (findOption(name) >= 0); } bool OptionContainer::hasOption(Option o) { return (findOption(o) >= 0); } int OptionContainer::findOption(Option o) { int i=0; for ( OptionVectorIterator it = m_Options.begin(); it != m_Options.end(); ++it ) { if((*it).getName() == o.getName()) { return i; } i++; } return -1; } int OptionContainer::findOption(std::string name) { int i=0; for ( OptionVectorIterator it = m_Options.begin(); it != m_Options.end(); ++it ) { if((*it).getName() == name) { return i; } i++; } return -1; } // serialization support bool OptionContainer::serializeOptions( std::string basePath, Util::IOSerialize& ser) const { bool result = true; int i = 0; for ( OptionVector::const_iterator it = m_Options.begin(); it != m_Options.end(); ++it ) { const Option& pOption = *it; std::ostringstream strstrm; strstrm << basePath << "/" << "Option" << i; result &= pOption.serialize( strstrm.str() + "/", ser ); i++; } return result; } bool OptionContainer::deserializeOptions( std::string basePath, Util::IODeserialize& deser, OptionContainer& container) { int i = 0; bool bFinished = false; bool result=true; do { std::ostringstream strstrm; strstrm << basePath << "/" << "Option" << i; if ( deser.isExisting( strstrm.str() ) ) { Option pOption = Option::deserialize( strstrm.str() + "/", deser ); if ( pOption.getType() != Option::EInvalid ) { result &= container.addOption(pOption); i++; } else { bFinished = true; } } else { bFinished = true; } } while ( !bFinished ); return result; } } // end of namespace Util libffado-2.4.5/src/libutil/OptionContainer.h0000644000175000001440000001170414206145246020406 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_OPTIONCONTAINER__ #define __FFADO_OPTIONCONTAINER__ #include "../debugmodule/debugmodule.h" #include "libutil/serialize.h" #include #include #include namespace Util { class OptionContainer { protected: class Option { public: enum EType { EInvalid = 0, EString = 1, EBool = 2, EDouble = 3, EInt = 4, EUInt = 5, }; public: Option(); Option(std::string); Option(std::string, std::string); Option(std::string, bool); Option(std::string, double); Option(std::string, int64_t); Option(std::string, uint64_t); ~Option() {}; std::string getName() {return m_Name;}; enum EType getType() {return m_Type;}; void set(std::string v); void set(bool v); void set(double v); void set(int64_t v); void set(uint64_t v); std::string getString() {return m_stringValue;}; bool getBool() {return m_boolValue;}; double getDouble() {return m_doubleValue;}; int64_t getInt() {return m_intValue;}; uint64_t getUInt() {return m_uintValue;}; public: // serialization support bool serialize( std::string basePath, Util::IOSerialize& ser ) const; static Option deserialize( std::string basePath, Util::IODeserialize& deser); private: std::string m_Name; std::string m_stringValue; bool m_boolValue; double m_doubleValue; int64_t m_intValue; uint64_t m_uintValue; enum EType m_Type; }; public: OptionContainer(); virtual ~OptionContainer(); bool setOption(std::string name, std::string v); bool setOption(std::string name, bool v); bool setOption(std::string name, double v); bool setOption(std::string name, int64_t v); bool setOption(std::string name, uint64_t v); bool setOption(std::string name, int32_t v); bool setOption(std::string name, uint32_t v); bool setOption(std::string name, int16_t v); bool setOption(std::string name, uint16_t v); bool setOption(std::string name, int8_t v); bool setOption(std::string name, uint8_t v); bool getOption(std::string name, std::string &v); bool getOption(std::string name, bool &v); bool getOption(std::string name, double &v); bool getOption(std::string name, float &v); bool getOption(std::string name, int64_t &v); bool getOption(std::string name, uint64_t &v); bool getOption(std::string name, int32_t &v); bool getOption(std::string name, uint32_t &v); bool getOption(std::string name, int16_t &v); bool getOption(std::string name, uint16_t &v); bool getOption(std::string name, int8_t &v); bool getOption(std::string name, uint8_t &v); Option::EType getOptionType(std::string name); bool hasOption(std::string name); int countOptions() {return m_Options.size();}; protected: bool setOption(Option o); Option getOption(std::string name); bool hasOption(Option o); bool addOption(Option o); bool removeOption(Option o); bool removeOption(std::string name); void clearOptions() {m_Options.clear();}; public: // provide an iterator interface typedef std::vector< Option >::iterator iterator; iterator begin() { return(m_Options.begin()); } iterator end() { return(m_Options.end()); } protected: // serialization support bool serializeOptions( std::string basePath, Util::IOSerialize& ser) const; static bool deserializeOptions( std::string basePath, Util::IODeserialize& deser, OptionContainer& container); private: int findOption(Option o); int findOption(std::string name); typedef std::vector< Option > OptionVector; typedef std::vector< Option >::iterator OptionVectorIterator; OptionVector m_Options; protected: DECLARE_DEBUG_MODULE; }; } // end of namespace Util #endif /* __FFADO_OPTIONCONTAINER__ */ libffado-2.4.5/src/libutil/PacketBuffer.cpp0000644000175000001440000001010714206145246020163 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "PacketBuffer.h" #include namespace Streaming { IMPL_DEBUG_MODULE( PacketBuffer, PacketBuffer, DEBUG_LEVEL_VERBOSE ); PacketBuffer::~PacketBuffer() { if(payload_buffer) ffado_ringbuffer_free(payload_buffer); if(header_buffer) ffado_ringbuffer_free(header_buffer); if(len_buffer) ffado_ringbuffer_free(len_buffer); } int PacketBuffer::initialize() { debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n"); if(payload_buffer) ffado_ringbuffer_free(payload_buffer); if(header_buffer) ffado_ringbuffer_free(header_buffer); if(len_buffer) ffado_ringbuffer_free(len_buffer); payload_buffer=ffado_ringbuffer_create(m_buffersize * m_max_packetsize * sizeof(quadlet_t)); if(!payload_buffer) { debugFatal("Could not allocate payload buffer\n"); return -1; } header_buffer=ffado_ringbuffer_create(m_buffersize * (m_headersize) * sizeof(quadlet_t)); if(!header_buffer) { debugFatal("Could not allocate header buffer\n"); return -1; } len_buffer=ffado_ringbuffer_create(m_buffersize * sizeof(unsigned int)); if(!len_buffer) { debugFatal("Could not allocate len buffer\n"); return -1; } debugOutput( DEBUG_LEVEL_VERBOSE, "exit...\n"); return 0; } void PacketBuffer::flush() { if(header_buffer) { ffado_ringbuffer_reset(header_buffer); } if(payload_buffer) { ffado_ringbuffer_reset(payload_buffer); } if(len_buffer) { ffado_ringbuffer_reset(len_buffer); } } int PacketBuffer::addPacket(quadlet_t *packet, int packet_len) { unsigned int payload_bytes=sizeof(quadlet_t)*(packet_len-m_headersize); unsigned int header_bytes=sizeof(quadlet_t)*(m_headersize); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "add packet: length=%d\n", packet_len); if ((ffado_ringbuffer_write_space(payload_buffer) > payload_bytes) && (ffado_ringbuffer_write_space(header_buffer) > header_bytes)) { ffado_ringbuffer_write(payload_buffer,(char *)(packet)+header_bytes, payload_bytes); ffado_ringbuffer_write(len_buffer,(char *)(&payload_bytes),sizeof(unsigned int)); ffado_ringbuffer_write(header_buffer,(char *)(packet), header_bytes); } else return -1; return 0; } int PacketBuffer::getNextPacket(quadlet_t *packet, int max_packet_len) { unsigned int bytes=sizeof(quadlet_t)*(m_headersize); quadlet_t *ptr=packet+m_headersize; debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "getNextPacket\n"); if(max_packet_len(max_packet_len-m_headersize)*sizeof(quadlet_t)) return -2; if(ffado_ringbuffer_read(payload_buffer,(char *)(ptr),bytes) < bytes) { return -3; } return bytes/sizeof(quadlet_t)+m_headersize; } int PacketBuffer::getBufferFillPackets() { return ffado_ringbuffer_read_space(len_buffer)/sizeof(unsigned int); } // in quadlets int PacketBuffer::getBufferFillPayload() { return ffado_ringbuffer_read_space(payload_buffer)/sizeof(quadlet_t); } } libffado-2.4.5/src/libutil/PacketBuffer.h0000644000175000001440000000355114206145246017635 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_PACKETBUFFER__ #define __FFADO_PACKETBUFFER__ #include "../debugmodule/debugmodule.h" #include #include "libutil/ringbuffer.h" namespace Streaming { class PacketBuffer { // note: all sizes in quadlets public: PacketBuffer(int headersize, int buffersize, int max_packetsize) : m_headersize(headersize), m_buffersize(buffersize), m_max_packetsize(max_packetsize), payload_buffer(0), header_buffer(0), len_buffer(0) {}; virtual ~PacketBuffer(); void setVerboseLevel(int l) { setDebugLevel( l ); }; int initialize(); void flush(); int addPacket(quadlet_t *packet, int packet_len); int getNextPacket(quadlet_t *packet, int packet_len); int getBufferFillPackets(); int getBufferFillPayload(); protected: int m_headersize; int m_buffersize; int m_max_packetsize; ffado_ringbuffer_t *payload_buffer; ffado_ringbuffer_t *header_buffer; ffado_ringbuffer_t *len_buffer; DECLARE_DEBUG_MODULE; }; } #endif /* __FFADO_PACKETBUFFER__ */ libffado-2.4.5/src/libutil/PosixMessageQueue.cpp0000644000175000001440000003243414206145246021245 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "PosixMessageQueue.h" #include "Functors.h" #include "PosixMutex.h" #include "Time.h" #include #include #include #include #define MQ_INVALID_ID ((mqd_t) -1) // one second #define POSIX_MESSAGEQUEUE_DEFAULT_TIMEOUT_SEC 10 #define POSIX_MESSAGEQUEUE_DEFAULT_TIMEOUT_NSEC 0 #define POSIX_MESSAGEQUEUE_MAX_MESSAGE_SIZE 1024 // note 10 is the default hard limit #define POSIX_MESSAGEQUEUE_MAX_NB_MESSAGES 10 namespace Util { IMPL_DEBUG_MODULE( PosixMessageQueue, PosixMessageQueue, DEBUG_LEVEL_NORMAL ); PosixMessageQueue::PosixMessageQueue(std::string name) : m_name( "/" + name ) , m_blocking( eB_Blocking ) , m_direction( eD_None ) , m_owner( false ) , m_handle( MQ_INVALID_ID ) , m_tmp_buffer( NULL ) , m_notifyHandler( NULL ) , m_notifyHandlerLock( *(new PosixMutex()) ) { m_timeout.tv_sec = POSIX_MESSAGEQUEUE_DEFAULT_TIMEOUT_SEC; m_timeout.tv_nsec = POSIX_MESSAGEQUEUE_DEFAULT_TIMEOUT_NSEC; memset(&m_attr, 0, sizeof(m_attr)); m_attr.mq_maxmsg = POSIX_MESSAGEQUEUE_MAX_NB_MESSAGES; m_attr.mq_msgsize = POSIX_MESSAGEQUEUE_MAX_MESSAGE_SIZE; m_tmp_buffer = new char[m_attr.mq_msgsize]; } PosixMessageQueue::~PosixMessageQueue() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) PosixMessageQueue destroy\n", this, m_name.c_str()); Close(); if(m_owner) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) unlink\n", this, m_name.c_str()); if(mq_unlink(m_name.c_str()) == MQ_INVALID_ID) { debugError("(%p, %s) could not unlink message queue: %s\n", this, m_name.c_str(), strerror(errno)); } } delete[] m_tmp_buffer; } bool PosixMessageQueue::doOpen(enum eDirection t, int flags, enum eBlocking b) { if(m_handle != MQ_INVALID_ID) { debugError("(%p, %s) already open\n", this, m_name.c_str()); return false; } switch(t) { case eD_ReadOnly: flags |= O_RDONLY; break; case eD_WriteOnly: flags |= O_WRONLY; break; case eD_ReadWrite: flags |= O_RDWR; break; default: debugError("bad direction\n"); return false; } if(b == eB_NonBlocking) { flags |= O_NONBLOCK; } if(flags & O_CREAT) { // only user has permissions m_handle = mq_open(m_name.c_str(), flags, S_IRWXU, &m_attr); } else { m_handle = mq_open(m_name.c_str(), flags); } if(m_handle == MQ_INVALID_ID) { debugError("(%p, %s) could not open: %s\n", this, m_name.c_str(), strerror(errno)); return false; } if(flags & O_CREAT) { m_owner = true; } if(mq_getattr(m_handle, &m_attr) == MQ_INVALID_ID) { debugError("(%p, %s) could get attr: %s\n", this, m_name.c_str(), strerror(errno)); return false; } m_blocking = b; return true; } bool PosixMessageQueue::Open(enum eDirection t, enum eBlocking b) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) open\n", this, m_name.c_str()); if(m_handle != MQ_INVALID_ID) { debugError("(%p, %s) already open\n", this, m_name.c_str()); return false; } return doOpen(t, 0, b); } bool PosixMessageQueue::Create(enum eDirection t, enum eBlocking b) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) create\n", this, m_name.c_str()); if(m_handle != MQ_INVALID_ID) { debugError("(%p, %s) already open\n", this, m_name.c_str()); return false; } return doOpen(t, O_CREAT | O_EXCL, b); } bool PosixMessageQueue::Close() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) close\n", this, m_name.c_str()); if(m_handle == MQ_INVALID_ID) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) not open\n", this, m_name.c_str()); return true; } if(mq_close(m_handle)) { debugError("(%p, %s) could not close: %s\n", this, m_name.c_str(), strerror(errno)); return false; } m_handle = MQ_INVALID_ID; return true; } enum PosixMessageQueue::eResult PosixMessageQueue::Clear() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) clear\n", this, m_name.c_str()); if(m_direction == eD_WriteOnly) { debugError("Cannot clear write-only queue\n"); return eR_Error; } // ensure that we don't interfere with the notification handler MutexLockHelper lock(m_notifyHandlerLock); while(countMessages()) { struct timespec timeout; Util::SystemTimeSource::clockGettime(&timeout); timeout.tv_sec += m_timeout.tv_sec; timeout.tv_nsec += m_timeout.tv_nsec; if(timeout.tv_nsec >= 1000000000LL) { timeout.tv_sec++; timeout.tv_nsec -= 1000000000LL; } signed int len; unsigned prio; if((len = mq_timedreceive(m_handle, m_tmp_buffer, m_attr.mq_msgsize, &prio, &timeout)) < 0) { switch(errno) { case EAGAIN: debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) empty\n", this, m_name.c_str()); return eR_OK; case ETIMEDOUT: debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) read timed out\n", this, m_name.c_str()); return eR_Timeout; default: debugError("(%p, %s) could not receive: %s\n", this, m_name.c_str(), strerror(errno)); return eR_Error; } } } return eR_OK; } enum PosixMessageQueue::eResult PosixMessageQueue::Send(PosixMessageQueue::Message &m) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) send\n", this, m_name.c_str()); if(m_direction == eD_ReadOnly) { debugError("Cannot write to read-only queue\n"); return eR_Error; } int len = m.getLength(); if (len > m_attr.mq_msgsize) { debugError("Message too long\n"); return eR_Error; } struct timespec timeout; Util::SystemTimeSource::clockGettime(&timeout); timeout.tv_sec += m_timeout.tv_sec; timeout.tv_nsec += m_timeout.tv_nsec; if(timeout.tv_nsec >= 1000000000LL) { timeout.tv_sec++; timeout.tv_nsec -= 1000000000LL; } if(!m.serialize(m_tmp_buffer)) { debugError("Could not serialize\n"); return eR_Error; } if(mq_timedsend(m_handle, m_tmp_buffer, len, m.getPriority(), &timeout) == MQ_INVALID_ID) { switch(errno) { case EAGAIN: debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) full\n", this, m_name.c_str()); return eR_Again; case ETIMEDOUT: debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) read timed out\n", this, m_name.c_str()); return eR_Timeout; default: debugError("(%p, %s) could not send: %s\n", this, m_name.c_str(), strerror(errno)); return eR_Error; } } return eR_OK; } enum PosixMessageQueue::eResult PosixMessageQueue::Receive(PosixMessageQueue::Message &m) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) receive\n", this, m_name.c_str()); if(m_direction == eD_WriteOnly) { debugError("Cannot read from write-only queue\n"); return eR_Error; } struct timespec timeout; Util::SystemTimeSource::clockGettime(&timeout); timeout.tv_sec += m_timeout.tv_sec; timeout.tv_nsec += m_timeout.tv_nsec; if(timeout.tv_nsec >= 1000000000LL) { timeout.tv_sec++; timeout.tv_nsec -= 1000000000LL; } signed int len; unsigned prio; if((len = mq_timedreceive(m_handle, m_tmp_buffer, m_attr.mq_msgsize, &prio, &timeout)) < 0) { switch(errno) { case EAGAIN: debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) empty\n", this, m_name.c_str()); return eR_Again; case ETIMEDOUT: debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) read timed out\n", this, m_name.c_str()); return eR_Timeout; default: debugError("(%p, %s) could not receive: %s\n", this, m_name.c_str(), strerror(errno)); return eR_Error; } } if(!m.deserialize(m_tmp_buffer, len, prio)) { debugError("Could not parse message\n"); return eR_Error; } return eR_OK; } bool PosixMessageQueue::Wait() { int err; struct pollfd poll_fds[1]; poll_fds[0].fd = m_handle; // NOTE: not portable, Linux only poll_fds[0].events = POLLIN; err = poll (poll_fds, 1, -1); if (err < 0) { if (errno == EINTR) { debugOutput(DEBUG_LEVEL_VERBOSE, "Ignoring poll return due to signal\n"); return true; } debugFatal("poll error: %s\n", strerror (errno)); return false; } return true; } int PosixMessageQueue::countMessages() { if(m_handle == MQ_INVALID_ID) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) invalid handle\n", this, m_name.c_str()); return -1; } struct mq_attr attr; if(mq_getattr(m_handle, &attr) == MQ_INVALID_ID) { debugError("(%p, %s) could get attr: %s\n", this, m_name.c_str(), strerror(errno)); return -1; } return attr.mq_curmsgs; } bool PosixMessageQueue::canSend() { return countMessages() < m_attr.mq_maxmsg; } bool PosixMessageQueue::canReceive() { return countMessages() > 0; } bool PosixMessageQueue::setNotificationHandler(Util::Functor *f) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) setting handler to %p\n", this, m_name.c_str(), f); // ensure we don't change the notifier while // it's used MutexLockHelper lock(m_notifyHandlerLock); if(m_notifyHandler == NULL) { m_notifyHandler = f; return true; } else { debugError("handler already present\n"); return false; } } bool PosixMessageQueue::unsetNotificationHandler() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) unsetting handler\n", this, m_name.c_str()); // ensure we don't change the notifier while // it's used MutexLockHelper lock(m_notifyHandlerLock); if(m_notifyHandler != NULL) { m_notifyHandler = NULL; return true; } else { debugWarning("no handler present\n"); return true; // not considered an error } } void PosixMessageQueue::notifyCallback() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) Notified\n", this, m_name.c_str()); // make sure the handler is not changed MutexLockHelper lock(m_notifyHandlerLock); if(m_notifyHandler) { (*m_notifyHandler)(); } } bool PosixMessageQueue::enableNotification() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) set\n", this, m_name.c_str()); sigevent evp; memset (&evp, 0, sizeof(evp)); evp.sigev_notify = SIGEV_THREAD; evp.sigev_value.sival_ptr = (void *)this; evp.sigev_notify_function = ¬ifyCallbackStatic; if(mq_notify(m_handle, &evp) == MQ_INVALID_ID) { debugError("(%p, %s) could set notifier: %s\n", this, m_name.c_str(), strerror(errno)); return false; } return true; } bool PosixMessageQueue::disableNotification() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) unset\n", this, m_name.c_str()); if(mq_notify(m_handle, NULL) == MQ_INVALID_ID) { debugError("(%p, %s) could unset notifier: %s\n", this, m_name.c_str(), strerror(errno)); return false; } return true; } void PosixMessageQueue::show() { debugOutput(DEBUG_LEVEL_NORMAL, "(%p) MessageQueue %s\n", this, m_name.c_str()); } void PosixMessageQueue::setVerboseLevel(int i) { setDebugLevel(i); m_notifyHandlerLock.setVerboseLevel(i); } } // end of namespace libffado-2.4.5/src/libutil/PosixMessageQueue.h0000644000175000001440000000665414206145246020717 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __UTIL_POSIX_MESSAGE_QUEUE__ #define __UTIL_POSIX_MESSAGE_QUEUE__ #ifdef _XOPEN_SOURCE #undef _XOPEN_SOURCE #endif #define _XOPEN_SOURCE 600 #include "debugmodule/debugmodule.h" #include #include #include #include namespace Util { class Functor; class Mutex; /** * @brief A POSIX Message Queue Wrapper. */ class PosixMessageQueue { public: class Message { public: Message() {}; virtual ~Message() {}; virtual unsigned getPriority() = 0; virtual unsigned int getLength() = 0; virtual bool serialize(char *buff) = 0; virtual bool deserialize(const char *buff, unsigned int length, unsigned prio) = 0; }; public: enum eDirection { eD_None, eD_ReadOnly, eD_WriteOnly, eD_ReadWrite }; enum eResult { eR_OK, eR_Again, eR_Error, eR_Timeout, }; // blocking mode enum eBlocking { eB_Blocking, eB_NonBlocking, }; public: PosixMessageQueue(std::string name); virtual ~PosixMessageQueue(); virtual bool Open(enum eDirection, enum eBlocking blocking = eB_Blocking); virtual bool Create(enum eDirection, enum eBlocking blocking = eB_Blocking); virtual bool Close(); virtual enum eResult Send(Message &m); virtual enum eResult Receive(Message &m); virtual enum eResult Clear(); virtual int countMessages(); virtual bool canSend(); virtual bool canReceive(); virtual bool setNotificationHandler(Util::Functor *f); virtual bool unsetNotificationHandler(); virtual bool enableNotification(); virtual bool disableNotification(); virtual bool Wait(); virtual unsigned int getMaxMessageLength() {return m_attr.mq_msgsize;}; virtual void show(); virtual void setVerboseLevel(int l); private: bool doOpen(enum eDirection t, int, enum eBlocking); static void notifyCallbackStatic(sigval_t t) { PosixMessageQueue *obj; obj = static_cast(t.sival_ptr); obj->notifyCallback(); } void notifyCallback(); protected: DECLARE_DEBUG_MODULE; private: std::string m_name; enum eBlocking m_blocking; enum eDirection m_direction; bool m_owner; struct timespec m_timeout; mqd_t m_handle; struct mq_attr m_attr; char * m_tmp_buffer; Util::Functor * m_notifyHandler; Util::Mutex& m_notifyHandlerLock; }; } // end of namespace #endif // __UTIL_POSIX_MESSAGE_QUEUE__ libffado-2.4.5/src/libutil/PosixMutex.cpp0000644000175000001440000001454614206145246017762 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "PosixMutex.h" #include // disable collision tracing for non-debug builds #ifndef DEBUG #undef DEBUG_LOCK_COLLISION_TRACING #define DEBUG_LOCK_COLLISION_TRACING 0 #endif // check whether backtracing is enabled #if DEBUG_LOCK_COLLISION_TRACING #define DEBUG_LOCK_COLLISION_TRACING_INDEX 2 #define DEBUG_LOCK_COLLISION_TRACING_NAME_MAXLEN 64 #if DEBUG_BACKTRACE_SUPPORT // ok #else #error cannot enable lock tracing without backtrace support #endif #endif namespace Util { IMPL_DEBUG_MODULE( PosixMutex, PosixMutex, DEBUG_LEVEL_NORMAL ); PosixMutex::PosixMutex() { m_id = "?"; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); #ifdef DEBUG pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); #else pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT); #endif pthread_mutex_init(&m_mutex, &attr); pthread_mutexattr_destroy(&attr); #if DEBUG_LOCK_COLLISION_TRACING m_locked_by = NULL; #endif } PosixMutex::PosixMutex(std::string id) { m_id = id; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); #ifdef DEBUG pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); #else pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT); #endif pthread_mutex_init(&m_mutex, &attr); pthread_mutexattr_destroy(&attr); #if DEBUG_LOCK_COLLISION_TRACING m_locked_by = NULL; #endif } PosixMutex::~PosixMutex() { pthread_mutex_destroy(&m_mutex); } void PosixMutex::Lock() { int err; debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%s, %p) lock\n", m_id.c_str(), this); #if DEBUG_LOCK_COLLISION_TRACING if(TryLock()) { // locking succeeded m_locked_by = debugBacktraceGet( DEBUG_LOCK_COLLISION_TRACING_INDEX ); char name[ DEBUG_LOCK_COLLISION_TRACING_NAME_MAXLEN ]; name[0] = 0; debugGetFunctionNameFromAddr(m_locked_by, name, DEBUG_LOCK_COLLISION_TRACING_NAME_MAXLEN); debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%s, %p) %s obtained lock\n", m_id.c_str(), this, name); return; } else { void *lock_try_by = debugBacktraceGet( DEBUG_LOCK_COLLISION_TRACING_INDEX ); char name1[ DEBUG_LOCK_COLLISION_TRACING_NAME_MAXLEN ]; name1[0] = 0; debugGetFunctionNameFromAddr(lock_try_by, name1, DEBUG_LOCK_COLLISION_TRACING_NAME_MAXLEN); char name2[ DEBUG_LOCK_COLLISION_TRACING_NAME_MAXLEN ]; name2[0] = 0; debugGetFunctionNameFromAddr(m_locked_by, name2, DEBUG_LOCK_COLLISION_TRACING_NAME_MAXLEN); debugWarning("(%s, %p) lock collision: %s wants lock, %s has lock\n", m_id.c_str(), this, name1, name2); if((err = pthread_mutex_lock(&m_mutex))) { if (err == EDEADLK) { debugError("(%s, %p) Resource deadlock detected\n", m_id.c_str(), this); debugPrintBacktrace(10); } else { debugError("(%s, %p) Error locking the mutex: %d\n", m_id.c_str(), this, err); } } else { debugWarning("(%s, %p) lock collision: %s got lock (from %s?)\n", m_id.c_str(), this, name1, name2); } } #else #ifdef DEBUG if((err = pthread_mutex_lock(&m_mutex))) { if (err == EDEADLK) { debugError("(%s, %p) Resource deadlock detected\n", m_id.c_str(), this); debugPrintBacktrace(10); } else { debugError("(%s, %p) Error locking the mutex: %d\n", m_id.c_str(), this, err); } } #else pthread_mutex_lock(&m_mutex); #endif #endif } bool PosixMutex::TryLock() { debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%s, %p) trying to lock\n", m_id.c_str(), this); return pthread_mutex_trylock(&m_mutex) == 0; } bool PosixMutex::isLocked() { debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%s, %p) checking lock\n", m_id.c_str(), this); int res = pthread_mutex_trylock(&m_mutex); if(res == 0) { pthread_mutex_unlock(&m_mutex); return false; } else { if (res == EDEADLK) { // this means that the current thread already has the lock, // iow it's locked. debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%s, %p) lock taken by current thread\n", m_id.c_str(), this); } else if(res == EBUSY) { debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%s, %p) lock taken\n", m_id.c_str(), this); } else { debugError("(%s, %p) Bogus error code: %d\n", m_id.c_str(), this, res); } return true; } } void PosixMutex::Unlock() { debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%s, %p) unlock\n", m_id.c_str(), this); #if DEBUG_LOCK_COLLISION_TRACING // unlocking m_locked_by = NULL; void *unlocker = debugBacktraceGet( DEBUG_LOCK_COLLISION_TRACING_INDEX ); char name[ DEBUG_LOCK_COLLISION_TRACING_NAME_MAXLEN ]; name[0] = 0; debugGetFunctionNameFromAddr(unlocker, name, DEBUG_LOCK_COLLISION_TRACING_NAME_MAXLEN); debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%s, %p) %s releases lock\n", m_id.c_str(), this, name); #endif #ifdef DEBUG int err; if((err = pthread_mutex_unlock(&m_mutex))) { debugError("(%s, %p) Error unlocking the mutex: %d\n", m_id.c_str(), this, err); } #else pthread_mutex_unlock(&m_mutex); #endif } void PosixMutex::show() { debugOutput(DEBUG_LEVEL_NORMAL, "(%s, %p) mutex (%s)\n", m_id.c_str(), this, (isLocked() ? "Locked" : "Unlocked")); } } // end of namespace libffado-2.4.5/src/libutil/PosixMutex.h0000644000175000001440000000304614206145246017420 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __POSIX_MUTEX__ #define __POSIX_MUTEX__ #include "Mutex.h" #include #include #include "debugmodule/debugmodule.h" namespace Util { /** * @brief The POSIX mutex implementation. */ class PosixMutex : public Mutex { public: PosixMutex(); PosixMutex(std::string id); virtual ~PosixMutex(); virtual void Lock(); virtual bool TryLock(); virtual void Unlock(); virtual bool isLocked(); virtual void show(); virtual void setVerboseLevel(int l) {setDebugLevel(l);}; protected: DECLARE_DEBUG_MODULE; private: pthread_mutex_t m_mutex; std::string m_id; #if DEBUG_LOCK_COLLISION_TRACING void *m_locked_by; #endif }; } // end of namespace #endif libffado-2.4.5/src/libutil/PosixSharedMemory.cpp0000644000175000001440000002012214206145246021242 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "PosixSharedMemory.h" #include #include #include #include #include namespace Util { IMPL_DEBUG_MODULE( PosixSharedMemory, PosixSharedMemory, DEBUG_LEVEL_NORMAL ); PosixSharedMemory::PosixSharedMemory(std::string name, unsigned int size) : m_name( "/" + name ) , m_size( size ) , m_owner( false ) , m_access( NULL ) { } PosixSharedMemory::~PosixSharedMemory() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) destroy\n", this, m_name.c_str()); Close(); if(m_owner) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) unlink\n", this, m_name.c_str()); shm_unlink(m_name.c_str()); } } bool PosixSharedMemory::Open(enum eDirection d) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) open\n", this, m_name.c_str()); if(m_access != NULL) { debugError("(%p, %s) already attached to segment\n", this, m_name.c_str()); } int flags=0; switch(d) { case eD_ReadOnly: flags |= O_RDONLY; break; case eD_WriteOnly: flags |= O_WRONLY; break; case eD_ReadWrite: flags |= O_RDWR; break; default: debugError("bad direction\n"); return false; } // open the shared memory segment int fd = shm_open(m_name.c_str(), flags, S_IRWXU); if (fd < 0) { if (errno != ENOENT) { debugError("(%p, %s) Cannot open shared memory: %s\n", this, m_name.c_str(), strerror (errno)); } else { debugError("(%p, %s) shared memory segment does not exist: %s\n", this, m_name.c_str(), strerror (errno)); } close(fd); return false; } // mmap the memory flags=0; switch(d) { case eD_ReadOnly: flags |= PROT_READ; break; case eD_WriteOnly: flags |= PROT_WRITE; break; case eD_ReadWrite: flags |= PROT_READ|PROT_WRITE; break; default: debugError("bad direction\n"); shm_unlink(m_name.c_str()); return false; } m_access = (char*)mmap(0, m_size, flags, MAP_SHARED, fd, 0); if (m_access == MAP_FAILED) { debugError("(%p, %s) Cannot mmap shared memory: %s\n", this, m_name.c_str(), strerror (errno)); close(fd); m_access = NULL; shm_unlink(m_name.c_str()); return false; } // close the fd close(fd); return true; } bool PosixSharedMemory::Create(enum eDirection d) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) create dir: %d, size: %u \n", this, m_name.c_str(), d, m_size); if(m_access != NULL) { debugError("(%p, %s) already attached to segment\n", this, m_name.c_str()); } // open the shared memory segment // always create it readwrite, if not, the other side can't map // it correctly, nor can we truncate it to the right length. int fd = shm_open(m_name.c_str(), O_RDWR|O_CREAT, S_IRWXU); if (fd < 0) { debugError("(%p, %s) Cannot open shared memory: %s\n", this, m_name.c_str(), strerror (errno)); close(fd); return false; } // set size if (ftruncate (fd, m_size) < 0) { debugError("(%p, %s) Cannot set shared memory size: %s\n", this, m_name.c_str(), strerror (errno)); close(fd); return false; } // mmap the memory int flags=0; switch(d) { case eD_ReadOnly: flags |= PROT_READ; break; case eD_WriteOnly: flags |= PROT_WRITE; break; case eD_ReadWrite: flags |= PROT_READ|PROT_WRITE; break; default: debugError("bad direction\n"); return false; } m_access = (char*)mmap(0, m_size, flags, MAP_SHARED, fd, 0); if (m_access == MAP_FAILED) { debugError("(%p, %s) Cannot mmap shared memory: %s\n", this, m_name.c_str(), strerror (errno)); close(fd); m_access = NULL; return false; } // close the fd close(fd); m_owner = true; return true; } bool PosixSharedMemory::Close() { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) close\n", this, m_name.c_str()); if(m_access) { if(munmap(m_access, m_size)) { debugError("(%p, %s) Cannot munmap shared memory: %s\n", this, m_name.c_str(), strerror (errno)); return false; } m_access = NULL; } else { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) not open\n", this, m_name.c_str()); } return true; } enum PosixSharedMemory::eResult PosixSharedMemory::Write(unsigned int offset, void * buff, unsigned int len) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) write\n", this, m_name.c_str()); if(offset+len <= m_size) { char * addr = m_access + offset; memcpy(addr, buff, len); // if(msync(addr, len, MS_SYNC | MS_INVALIDATE)) { // debugError("Could not sync written block\n"); // return eR_Error; // } return eR_OK; } else { debugError("Requested block (%u) out of range (%u)\n", offset+len, m_size); return eR_Error; } } enum PosixSharedMemory::eResult PosixSharedMemory::Read(unsigned int offset, void * buff, unsigned int len) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) read\n", this, m_name.c_str()); if(offset+len <= m_size) { char * addr = m_access + offset; memcpy(buff, addr, len); return eR_OK; } else { debugError("Requested block (%u) out of range (%u)\n", offset+len, m_size); return eR_Error; } } void* PosixSharedMemory::requestBlock(unsigned int offset, unsigned int len) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) getBlock\n", this, m_name.c_str()); if(offset+len <= m_size) { return (void*)(m_access + offset); } else { debugError("Requested block (%u) out of range (%u)\n", offset+len, m_size); return NULL; } } void PosixSharedMemory::commitBlock(unsigned int offset, unsigned int len) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) commitBlock\n", this, m_name.c_str()); if(offset+len >= m_size) { debugError("Committed block (%u) out of range (%u)\n", offset+len, m_size); } } bool PosixSharedMemory::LockInMemory(bool lock) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) LockInMemory\n", this, m_name.c_str()); if(lock) { if(mlock(m_access, m_size)) { debugError("(%p, %s) Cannot mlock shared memory: %s\n", this, m_name.c_str(), strerror (errno)); return false; } else return true; } else { if(munlock(m_access, m_size)) { debugError("(%p, %s) Cannot munlock shared memory: %s\n", this, m_name.c_str(), strerror (errno)); return false; } else return true; } return false; } void PosixSharedMemory::show() { debugOutput(DEBUG_LEVEL_NORMAL, "(%p) PosixSharedMemory %s\n", this, m_name.c_str()); } } // namespace Util libffado-2.4.5/src/libutil/PosixSharedMemory.h0000644000175000001440000000436214206145246020717 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __UTIL_POSIX_SHARED_MEMORY__ #define __UTIL_POSIX_SHARED_MEMORY__ #include "debugmodule/debugmodule.h" #include namespace Util { /** * @brief A POSIX Shared Memory Wrapper. */ class PosixSharedMemory { public: enum eDirection { eD_None, eD_ReadOnly, eD_WriteOnly, eD_ReadWrite }; enum eResult { eR_OK, eR_Again, eR_Error, }; public: PosixSharedMemory(std::string name, unsigned int len); virtual ~PosixSharedMemory(); // mlock interface /** * Tries to (un)lock the segment to stay in memory * @param lock * @return */ bool LockInMemory(bool lock); virtual bool Create(enum eDirection d=eD_ReadWrite); virtual bool Open(enum eDirection d=eD_ReadWrite); virtual bool Close(); virtual enum eResult Write(unsigned int offset, void * buff, unsigned int len); virtual enum eResult Read(unsigned int offset, void * buff, unsigned int len); virtual void* requestBlock(unsigned int offset, unsigned int len); virtual void commitBlock(unsigned int offset, unsigned int len); virtual void show(); virtual void setVerboseLevel(int l) {setDebugLevel(l);}; protected: DECLARE_DEBUG_MODULE; private: std::string m_name; unsigned int m_size; bool m_owner; char * m_access; }; } // namespace Util #endif // __UTIL__POSIX_SHARED_MEMORY__ libffado-2.4.5/src/libutil/PosixThread.cpp0000644000175000001440000002174414206145246020065 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Copied from the jackd/jackdmp sources * function names changed in order to avoid naming problems when using this in * a jackd backend. */ /* Original license: * Copyright (C) 2001 Paul Davis * Copyright (C) 2004-2006 Grame * * this program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "PosixThread.h" #include // for memset #include #include #include namespace Util { IMPL_DEBUG_MODULE( Thread, Thread, DEBUG_LEVEL_NORMAL ); void* PosixThread::ThreadHandler(void* arg) { PosixThread* obj = (PosixThread*)arg; RunnableInterface* runnable = obj->fRunnable; int err; obj->m_lock.Lock(); // Signal that ThreadHandler has acquired its initial lock pthread_mutex_lock(&obj->handler_active_lock); obj->handler_active = 1; pthread_cond_signal(&obj->handler_active_cond); pthread_mutex_unlock(&obj->handler_active_lock); if ((err = pthread_setcanceltype(obj->fCancellation, NULL)) != 0) { debugError("pthread_setcanceltype err = %s\n", strerror(err)); } // Call Init method if (!runnable->Init()) { debugError("Thread init fails: thread quits\n"); obj->m_lock.Unlock(); return 0; } std::string threadname = std::string("FW_") + obj->m_id; prctl(PR_SET_NAME, threadname.c_str()); debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) ThreadHandler: start %p\n", obj->m_id.c_str(), obj); // If Init succeed start the thread loop bool res = true; obj->m_lock.Unlock(); while (obj->fRunning && res) { debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "(%s) ThreadHandler: run %p\n", obj->m_id.c_str(), obj); res = runnable->Execute(); pthread_testcancel(); } debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) ThreadHandler: exit %p\n", obj->m_id.c_str(), obj); return 0; } int PosixThread::Start() { int res; fRunning = true; if (fRealTime) { debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Create RT thread %p with priority %d\n", m_id.c_str(), this, fPriority); /* Get the client thread to run as an RT-FIFO scheduled thread of appropriate priority. */ pthread_attr_t attributes; struct sched_param rt_param; pthread_attr_init(&attributes); if ((res = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED))) { debugError("Cannot request explicit scheduling for RT thread %d %s\n", res, strerror(res)); return -1; } if ((res = pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_JOINABLE))) { debugError("Cannot request joinable thread creation for RT thread %d %s\n", res, strerror(res)); return -1; } if ((res = pthread_attr_setscope(&attributes, PTHREAD_SCOPE_SYSTEM))) { debugError("Cannot set scheduling scope for RT thread %d %s\n", res, strerror(res)); return -1; } if ((res = pthread_attr_setschedpolicy(&attributes, SCHED_FIFO))) { //if ((res = pthread_attr_setschedpolicy(&attributes, SCHED_RR))) { debugError("Cannot set FIFO scheduling class for RT thread %d %s\n", res, strerror(res)); return -1; } memset(&rt_param, 0, sizeof(rt_param)); if(fPriority <= 0) { debugWarning("Clipping to minimum priority (%d -> 1)\n", fPriority); rt_param.sched_priority = 1; } else if(fPriority >= 99) { debugWarning("Clipping to maximum priority (%d -> 98)\n", fPriority); rt_param.sched_priority = 98; } else { rt_param.sched_priority = fPriority; } if ((res = pthread_attr_setschedparam(&attributes, &rt_param))) { debugError("Cannot set scheduling priority for RT thread %d %s\n", res, strerror(res)); return -1; } m_lock.Lock(); res = pthread_create(&fThread, &attributes, ThreadHandler, this); m_lock.Unlock(); if (res) { debugError("Cannot create realtime thread (%d: %s)\n", res, strerror(res)); debugError(" priority: %d\n", fPriority); return -1; } } else { debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Create non RT thread %p\n", m_id.c_str(), this); m_lock.Lock(); res = pthread_create(&fThread, 0, ThreadHandler, this); m_lock.Unlock(); if (res) { debugError("Cannot create thread %d %s\n", res, strerror(res)); return -1; } } // Wait for ThreadHandler() to acquire the thread lock (m_lock) before // continuing, thereby ensuring that ThreadHandler() acquires a lock on // m_lock before anything else tries. pthread_mutex_lock(&handler_active_lock); while (handler_active == 0) pthread_cond_wait(&handler_active_cond, &handler_active_lock); pthread_mutex_unlock(&handler_active_lock); return 0; } int PosixThread::Kill() { if (fThread) { // If thread has been started debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Kill %p (thread: %p)\n", m_id.c_str(), this, (void *)fThread); void* status; pthread_cancel(fThread); m_lock.Lock(); pthread_join(fThread, &status); m_lock.Unlock(); debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Killed %p (thread: %p)\n", m_id.c_str(), this, (void *)fThread); return 0; } else { return -1; } } int PosixThread::Stop() { if (fThread) { // If thread has been started debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Stop %p (thread: %p)\n", m_id.c_str(), this, (void *)fThread); void* status; fRunning = false; // Request for the thread to stop m_lock.Lock(); pthread_join(fThread, &status); fThread = 0; m_lock.Unlock(); debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Stopped %p (thread: %p)\n", m_id.c_str(), this, (void *)fThread); return 0; } else { return -1; } } int PosixThread::AcquireRealTime() { struct sched_param rtparam; int res; debugOutput( DEBUG_LEVEL_VERBOSE, "(%s, %p) Acquire realtime, prio %d\n", m_id.c_str(), this, fPriority); if (!fThread) return -1; memset(&rtparam, 0, sizeof(rtparam)); if(fPriority <= 0) { debugWarning("Clipping to minimum priority (%d -> 1)\n", fPriority); rtparam.sched_priority = 1; } else if(fPriority >= 99) { debugWarning("Clipping to maximum priority (%d -> 98)\n", fPriority); rtparam.sched_priority = 98; } else { rtparam.sched_priority = fPriority; } //if ((res = pthread_setschedparam(fThread, SCHED_FIFO, &rtparam)) != 0) { if ((res = pthread_setschedparam(fThread, SCHED_FIFO, &rtparam)) != 0) { debugError("Cannot use real-time scheduling (FIFO/%d) " "(%d: %s)", rtparam.sched_priority, res, strerror(res)); return -1; } return 0; } int PosixThread::AcquireRealTime(int priority) { fPriority = priority; return AcquireRealTime(); } int PosixThread::DropRealTime() { struct sched_param rtparam; int res; debugOutput( DEBUG_LEVEL_VERBOSE, "(%s, %p) Drop realtime\n", m_id.c_str(), this); if (!fThread) return -1; memset(&rtparam, 0, sizeof(rtparam)); rtparam.sched_priority = 0; if ((res = pthread_setschedparam(fThread, SCHED_OTHER, &rtparam)) != 0) { debugError("Cannot switch to normal scheduling priority(%s)\n", strerror(res)); return -1; } return 0; } pthread_t PosixThread::GetThreadID() { return fThread; } } // end of namespace libffado-2.4.5/src/libutil/PosixThread.h0000644000175000001440000001267514206145246017535 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Copied from the jackd/jackdmp sources * function names changed in order to avoid naming problems when using this in * a jackd backend. */ /* Original license: * * Copyright (C) 2001 Paul Davis * Copyright (C) 2004-2006 Grame * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef __FFADO_POSIXTHREAD__ #define __FFADO_POSIXTHREAD__ #include "Thread.h" #include #include "PosixMutex.h" namespace Util { /*! \brief The POSIX thread base class. */ class PosixThread : public Thread { protected: pthread_t fThread; int fPriority; bool fRealTime; volatile bool fRunning; int fCancellation; pthread_mutex_t handler_active_lock; pthread_cond_t handler_active_cond; int handler_active; static void* ThreadHandler(void* arg); Util::Mutex &m_lock; public: PosixThread(RunnableInterface* runnable, bool real_time, int priority, int cancellation) : Thread(runnable), fThread((pthread_t)NULL), fPriority(priority), fRealTime(real_time), fRunning(false), fCancellation(cancellation) , handler_active(0) , m_lock(*(new Util::PosixMutex("THREAD"))) { pthread_mutex_init(&handler_active_lock, NULL); pthread_cond_init(&handler_active_cond, NULL); } PosixThread(RunnableInterface* runnable) : Thread(runnable), fThread((pthread_t)NULL), fPriority(0), fRealTime(false), fRunning(false), fCancellation(PTHREAD_CANCEL_DEFERRED) , handler_active(0) , m_lock(*(new Util::PosixMutex("THREAD"))) { pthread_mutex_init(&handler_active_lock, NULL); pthread_cond_init(&handler_active_cond, NULL); } PosixThread(RunnableInterface* runnable, int cancellation) : Thread(runnable), fThread((pthread_t)NULL), fPriority(0), fRealTime(false), fRunning(false), fCancellation(cancellation) , handler_active(0) , m_lock(*(new Util::PosixMutex("THREAD"))) { pthread_mutex_init(&handler_active_lock, NULL); pthread_cond_init(&handler_active_cond, NULL); } PosixThread(RunnableInterface* runnable, std::string id, bool real_time, int priority, int cancellation) : Thread(runnable, id), fThread((pthread_t)NULL), fPriority(priority), fRealTime(real_time), fRunning(false), fCancellation(cancellation) , handler_active(0) , m_lock(*(new Util::PosixMutex(id))) { pthread_mutex_init(&handler_active_lock, NULL); pthread_cond_init(&handler_active_cond, NULL); } PosixThread(RunnableInterface* runnable, std::string id) : Thread(runnable, id), fThread((pthread_t)NULL), fPriority(0), fRealTime(false), fRunning(false), fCancellation(PTHREAD_CANCEL_DEFERRED) , handler_active(0) , m_lock(*(new Util::PosixMutex(id))) { pthread_mutex_init(&handler_active_lock, NULL); pthread_cond_init(&handler_active_cond, NULL); } PosixThread(RunnableInterface* runnable, std::string id, int cancellation) : Thread(runnable, id), fThread((pthread_t)NULL), fPriority(0), fRealTime(false), fRunning(false), fCancellation(cancellation) , handler_active(0) , m_lock(*(new Util::PosixMutex(id))) { pthread_mutex_init(&handler_active_lock, NULL); pthread_cond_init(&handler_active_cond, NULL); } virtual ~PosixThread() { delete &m_lock; pthread_mutex_destroy(&handler_active_lock); pthread_cond_destroy(&handler_active_cond); } virtual int Start(); virtual int Kill(); virtual int Stop(); virtual int AcquireRealTime(); virtual int AcquireRealTime(int priority); virtual int DropRealTime(); pthread_t GetThreadID(); protected: }; } // end of namespace #endif // __FFADO_POSIXTHREAD__ libffado-2.4.5/src/libutil/StreamStatistics.cpp0000644000175000001440000000425314206145246021135 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "StreamStatistics.h" #include namespace Streaming { IMPL_DEBUG_MODULE( StreamStatistics, StreamStatistics, DEBUG_LEVEL_VERBOSE ); StreamStatistics::StreamStatistics() : m_name("") , m_count(0) , m_average(0.0) , m_min(0x7FFFFFFF) , m_max(0) , m_sum(0) { reset(); } void StreamStatistics::mark(int value) { if(value>m_max) m_max=value; if(value. * */ #ifndef FFADOSTREAMINGSTREAMSTATISTICS_H #define FFADOSTREAMINGSTREAMSTATISTICS_H #include #include "debugmodule/debugmodule.h" #define MAX_SIGNAL_VALUE 7 namespace Streaming { class StreamStatistics { public: StreamStatistics(); ~StreamStatistics() {}; void setName(std::string n) {m_name=n;}; void mark(int value); void dumpInfo(); void reset(); std::string m_name; int64_t m_count; float m_average; int64_t m_min; int64_t m_max; int64_t m_sum; // some tools to do run statistics // will keep a histogram of the number of times a certain value // is added. void signal(unsigned int val); unsigned int m_signalled[MAX_SIGNAL_VALUE+1]; private: DECLARE_DEBUG_MODULE; }; } #endif libffado-2.4.5/src/libutil/SystemTimeSource.cpp0000644000175000001440000001004514206145246021107 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copytight (C) 2012 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "SystemTimeSource.h" #include "Time.h" // needed for clock_nanosleep #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include DECLARE_GLOBAL_DEBUG_MODULE; namespace Util { static clockid_t clock_id = CLOCK_REALTIME; bool SystemTimeSource::setSource(clockid_t id) { struct timespec tp; // Determine at runtime whether the kernel has support for the // requested clock source. if (clock_gettime(id, &tp) == 0) { clock_id = id; return true; } return false; } clockid_t SystemTimeSource::getSource(void) { return clock_id; } int SystemTimeSource::clockGettime(struct timespec *tp) { return clock_gettime(clock_id, tp); } void SystemTimeSource::SleepUsecRelative(ffado_microsecs_t usecs) { //usleep(usecs); struct timespec ts; ts.tv_sec = usecs / (1000000LL); ts.tv_nsec = (usecs % (1000000LL)) * 1000LL; // clock_nanosleep() is not implemented for CLOCK_MONOTONIC_RAW. // If the clock source is CLOCK_MONOTONIC_RAW, use CLOCK_MONOTONIC // as an approximation for the purposes of sleep timing. clockid_t clk = (clock_id==CLOCK_MONOTONIC_RAW)?CLOCK_MONOTONIC:clock_id; clock_nanosleep(clk, 0, &ts, NULL); } void SystemTimeSource::SleepUsecAbsolute(ffado_microsecs_t wake_at_usec) { #if USE_ABSOLUTE_NANOSLEEP // If the system time is based on CLOCK_MONOTONIC_RAW we can't use // TIMER_ABSTIME even if it is available because wake_at_usec will be in // terms of CLOCK_MONOTONIC_RAW while clock_nanosleep() can at best use // only CLOCK_MONOTONIC. There is, AFAIK, no guarantee that the two are // even remotely related. For now, resolve this problem by only using // TIMER_ABSTIME when CLOCK_MONOTONIC_RAW is not in use. if (clock_id != CLOCK_MONOTONIC_RAW) { struct timespec ts; ts.tv_sec = wake_at_usec / (1000000LL); ts.tv_nsec = (wake_at_usec % (1000000LL)) * 1000LL; debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "clock_nanosleep until %" PRId64 " sec, %" PRId64 " nanosec\n", (int64_t)ts.tv_sec, (int64_t)ts.tv_nsec); int err = clock_nanosleep(clock_id, TIMER_ABSTIME, &ts, NULL); if(err) { // maybe signal occurred, but we're going to ignore that } debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "back with err=%d\n", err); } else #endif { // only sleep if needed ffado_microsecs_t now = getCurrentTime(); if(wake_at_usec >= now) { ffado_microsecs_t to_sleep = wake_at_usec - now; SleepUsecRelative(to_sleep); } } } ffado_microsecs_t SystemTimeSource::SleepUsecRandom(ffado_microsecs_t max_usec) { long int rnd = random(); long long int tmp = (rnd*max_usec); tmp /= RAND_MAX; ffado_microsecs_t usec = tmp; SleepUsecRelative(usec); return usec; } ffado_microsecs_t SystemTimeSource::getCurrentTime() { return getCurrentTimeAsUsecs(); } ffado_microsecs_t SystemTimeSource::getCurrentTimeAsUsecs() { struct timespec ts; clock_gettime(clock_id, &ts); return (ffado_microsecs_t)(ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL); } } // end of namespace Util libffado-2.4.5/src/libutil/SystemTimeSource.h0000644000175000001440000000400514206145246020553 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2012 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_SYSTEMTIMESOURCE__ #define __FFADO_SYSTEMTIMESOURCE__ #include "../debugmodule/debugmodule.h" #include typedef uint64_t ffado_microsecs_t; // Ensure this is defined even for kernels/glib versions which don't include // it. This allows runtime-time testing for the feature. #ifndef CLOCK_MONOTONIC_RAW #define CLOCK_MONOTONIC_RAW 4 #endif namespace Util { class SystemTimeSource { private: // don't allow objects to be created SystemTimeSource() {}; virtual ~SystemTimeSource() {}; public: static bool setSource(clockid_t id); static clockid_t getSource(void); static int clockGettime(struct timespec *tp); static ffado_microsecs_t getCurrentTime(); static ffado_microsecs_t getCurrentTimeAsUsecs(); static void SleepUsecRelative(ffado_microsecs_t usecs); static void SleepUsecAbsolute(ffado_microsecs_t wake_time); /** * @brief sleeps for a random amount of usecs * @param max_usec max usecs * @return number of usecs slept */ static ffado_microsecs_t SleepUsecRandom(ffado_microsecs_t max_usec); }; } // end of namespace Util #endif /* __FFADO_SYSTEMTIMESOURCE__ */ libffado-2.4.5/src/libutil/Thread.h0000644000175000001440000000700514206145246016501 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Copied from the jackd/jackdmp sources * function names changed in order to avoid naming problems when using this in * a jackd backend. */ /* Original license: * * Copyright (C) 2001 Paul Davis * Copyright (C) 2004-2006 Grame * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef __THREAD__ #define __THREAD__ #include "../debugmodule/debugmodule.h" #include "Atomic.h" #include #include namespace Util { /*! \brief The base class for runnable objects, that have an Init and Execute method to be called in a thread. */ class RunnableInterface { public: RunnableInterface() {} virtual ~RunnableInterface() {} virtual bool Init() /*! Called once when the thread is started */ { return true; } virtual bool Execute() = 0; /*! Must be implemented by subclasses */ }; /*! \brief The thread base class. */ class Thread { protected: RunnableInterface* fRunnable; public: Thread(RunnableInterface* runnable) : fRunnable(runnable) , m_id( "UNKNOWN" ) {} Thread(RunnableInterface* runnable, std::string id) : fRunnable(runnable) , m_id( id ) {} virtual ~Thread() {} virtual int Start() = 0; virtual int Kill() = 0; virtual int Stop() = 0; virtual int AcquireRealTime() = 0; virtual int AcquireRealTime(int priority) = 0; virtual int DropRealTime() = 0; virtual void SetParams(uint64_t period, uint64_t computation, uint64_t constraint) // Empty implementation, will only make sense on OSX... {} virtual pthread_t GetThreadID() = 0; virtual void setVerboseLevel(int l) { setDebugLevel(l); debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Setting verbose level to %d...\n", m_id.c_str(), l ); }; protected: std::string m_id; DECLARE_DEBUG_MODULE; }; } // end of namespace #endif libffado-2.4.5/src/libutil/Time.h0000644000175000001440000000242214206145246016166 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __Time__ #define __Time__ #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include #include "SystemTimeSource.h" /** * Type used to represent the value of free running * monotonic clock with units of microseconds. */ typedef uint64_t ffado_microsecs_t; #define PRI_FFADO_MICROSECS_T PRIu64 static inline void SleepRelativeUsec(ffado_microsecs_t usec) { Util::SystemTimeSource::SleepUsecRelative(usec); } #endif libffado-2.4.5/src/libutil/TimestampedBuffer.cpp0000644000175000001440000011336414206145246021241 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "libutil/Atomic.h" #include "libieee1394/cycletimer.h" #include "TimestampedBuffer.h" #include "assert.h" #include "errno.h" #include #include #include #define DLL_PI (3.141592653589793238) #define DLL_SQRT2 (1.414213562373095049) #define DLL_2PI (2.0 * DLL_PI) // these are the defaults #define DLL_OMEGA (DLL_2PI * 0.01) #define DLL_COEFF_B (DLL_SQRT2 * DLL_OMEGA) #define DLL_COEFF_C (DLL_OMEGA * DLL_OMEGA) #define FRAMES_PER_PROCESS_BLOCK 8 /* #define ENTER_CRITICAL_SECTION { \ if (pthread_mutex_trylock(&m_framecounter_lock) == EBUSY) { \ debugWarning(" (%p) lock clash\n", this); \ pthread_mutex_lock(&m_framecounter_lock); \ } \ } */ #define ENTER_CRITICAL_SECTION { \ pthread_mutex_lock(&m_framecounter_lock); \ } #define EXIT_CRITICAL_SECTION { \ pthread_mutex_unlock(&m_framecounter_lock); \ } namespace Util { IMPL_DEBUG_MODULE( TimestampedBuffer, TimestampedBuffer, DEBUG_LEVEL_VERBOSE ); TimestampedBuffer::TimestampedBuffer(TimestampedBufferClient *c) : m_event_buffer(NULL), m_process_buffer(NULL), m_cluster_size( 0 ), m_process_block_size( 0 ), m_event_size(0), m_events_per_frame(0), m_buffer_size(0), m_bytes_per_frame(0), m_bytes_per_buffer(0), m_enabled( false ), m_transparent ( true ), m_wrap_at(0xFFFFFFFFFFFFFFFFLLU), m_Client(c), m_framecounter(0), m_buffer_tail_timestamp(TIMESTAMP_MAX + 1.0), m_buffer_next_tail_timestamp(TIMESTAMP_MAX + 1.0), m_dll_e2(0.0), m_dll_b(DLL_COEFF_B), m_dll_c(DLL_COEFF_C), m_nominal_rate(0.0), m_current_rate(0.0), m_update_period(0), // half a cycle is what we consider 'normal' m_max_abs_diff(3072/2) { pthread_mutex_init(&m_framecounter_lock, NULL); } TimestampedBuffer::~TimestampedBuffer() { pthread_mutex_destroy(&m_framecounter_lock); if(m_event_buffer) ffado_ringbuffer_free(m_event_buffer); if(m_process_buffer) free(m_process_buffer); } /** * \brief Set the bandwidth of the DLL * * Sets the bandwith of the DLL in absolute frequency * * @param bw bandwidth in absolute frequency * @return true if successful */ bool TimestampedBuffer::setBandwidth(double bw) { double curr_bw = getBandwidth(); debugOutput(DEBUG_LEVEL_VERBOSE," bandwidth %e => %e\n", curr_bw, bw); double tupdate = m_nominal_rate * (float)m_update_period; double bw_rel = bw * tupdate; if(bw_rel >= 0.5) { debugError("Requested bandwidth out of range: %f > %f\n", bw, 0.5 / tupdate); return false; } ENTER_CRITICAL_SECTION; m_dll_b = bw_rel * (DLL_SQRT2 * DLL_2PI); m_dll_c = bw_rel * bw_rel * DLL_2PI * DLL_2PI; EXIT_CRITICAL_SECTION; return true; } /** * \brief Returns the current bandwidth of the DLL * * Returns the current bandwith of the DLL in absolute frequency * * @return bandwidth in absolute frequency */ double TimestampedBuffer::getBandwidth() { double tupdate = m_nominal_rate * (float)m_update_period; double curr_bw = m_dll_b / (DLL_SQRT2 * DLL_2PI * tupdate); return curr_bw; } /** * \brief Set the nominal rate in timeunits/frame * * Sets the nominal rate in time units per frame. This rate is used * to initialize the DLL that will extract the effective rate based * upon the timestamps it gets fed. * * @param r rate * @return true if successful */ bool TimestampedBuffer::setNominalRate(float r) { debugOutput(DEBUG_LEVEL_VERBOSE," nominal rate %e => %e\n", m_nominal_rate, r); m_nominal_rate=r; return true; } /** * \brief Set the nominal update period (in frames) * * Sets the nominal update period. This period is the number of frames * between two timestamp updates (hence buffer writes) * * @param n period in frames * @return true if successful */ bool TimestampedBuffer::setUpdatePeriod(unsigned int n) { m_update_period=n; return true; } /** * \brief Get the nominal update period (in frames) * * Gets the nominal update period. This period is the number of frames * between two timestamp updates (hence buffer writes) * * @return period in frames */ unsigned int TimestampedBuffer::getUpdatePeriod() { return m_update_period; } /** * \brief set the value at which timestamps should wrap around * @param w value to wrap at * @return true if successful */ bool TimestampedBuffer::setWrapValue(ffado_timestamp_t w) { m_wrap_at=w; return true; } /** * \brief return the effective rate * * Returns the effective rate calculated by the DLL. * * @return rate (in timeunits/frame) */ float TimestampedBuffer::getRate() { return m_current_rate; } /** * \brief presets the effective rate * * Presets the DLL such that the effective rate is as given * @param rate rate (in timeunits/frame) */ void TimestampedBuffer::setRate(float rate) { // we take the current tail timestamp and update the head timestamp // to ensure the rate is ok ENTER_CRITICAL_SECTION; m_current_rate = rate; m_dll_e2 = m_update_period * m_current_rate; m_buffer_next_tail_timestamp = (ffado_timestamp_t)((double)m_buffer_tail_timestamp + m_dll_e2); EXIT_CRITICAL_SECTION; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "for (%p) " "NTS=" TIMESTAMP_FORMAT_SPEC ", DLL2=%f, RATE=%f\n", this, m_buffer_next_tail_timestamp, m_dll_e2, getRate()); } /** * \brief calculate the effective rate * * Returns the effective rate calculated by the DLL. * @note should be called with the lock held * @return rate (in timeunits/frame) */ float TimestampedBuffer::calculateRate() { ffado_timestamp_t diff; diff=m_buffer_next_tail_timestamp - m_buffer_tail_timestamp; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "getRate: %f/%f=%f\n", (float)(diff), (float)m_update_period, ((float)(diff))/((float) m_update_period)); // the maximal difference we can allow (64secs) const ffado_timestamp_t max=m_wrap_at/((ffado_timestamp_t)2); if(diff > max) { diff -= m_wrap_at; } else if (diff < -max) { diff += m_wrap_at; } float rate=((float)diff)/((float) m_update_period); if (rate<0.0) debugError("rate < 0! (%f)\n",rate); if (fabsf(m_nominal_rate - rate)>(m_nominal_rate*0.1)) { debugWarning("(%p) rate (%10.5f) more that 10%% off nominal " "(rate=%10.5f, diff=" TIMESTAMP_FORMAT_SPEC ", update_period=%d)\n", this, rate,m_nominal_rate,diff, m_update_period); return m_nominal_rate; } else { return rate; } } /** * \brief Sets the size of the events * @param s event size in bytes * @return true if successful */ bool TimestampedBuffer::setEventSize(unsigned int s) { m_event_size=s; m_bytes_per_frame=m_event_size*m_events_per_frame; m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size; return true; } /** * \brief Sets the number of events per frame * @param n number of events per frame * @return true if successful */ bool TimestampedBuffer::setEventsPerFrame(unsigned int n) { m_events_per_frame=n; m_bytes_per_frame=m_event_size*m_events_per_frame; m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size; return true; } /** * \brief Sets the buffer size in frames * @param n number frames * @return true if successful */ bool TimestampedBuffer::setBufferSize(unsigned int n) { m_buffer_size=n; m_bytes_per_frame=m_event_size*m_events_per_frame; m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size; return true; } /** * \brief Returns the current fill of the buffer * * This returns the buffer fill of the internal ringbuffer. This * can only be used as an indication because it's state is not * guaranteed to be consistent at all times due to threading issues. * * In order to get the number of frames in the buffer, use the * getBufferHeadTimestamp, getBufferTailTimestamp * functions * * @return the internal buffer fill in frames */ unsigned int TimestampedBuffer::getBufferFill() { //return ffado_ringbuffer_read_space(m_event_buffer)/(m_bytes_per_frame); return m_framecounter; } /** * \brief Returns the current write space in the buffer * * This returns the buffer free space of the internal ringbuffer. This * can only be used as an indication because it's state is not * guaranteed to be consistent at all times due to threading issues. * * @return the internal buffer fill in frames */ unsigned int TimestampedBuffer::getBufferSpace() { //return ffado_ringbuffer_write_space(m_event_buffer)/(m_bytes_per_frame); assert(m_buffer_size-m_framecounter >= 0); return m_buffer_size-m_framecounter; } /** * \brief Resets the TimestampedBuffer * * Resets the TimestampedBuffer, clearing the buffers and counters. * [DEL Also resets the DLL to the nominal values. DEL] * * \note when this is called, you should make sure that the buffer * tail timestamp gets set before continuing * * @return true if successful */ bool TimestampedBuffer::clearBuffer() { debugOutput(DEBUG_LEVEL_VERBOSE, "Clearing buffer\n"); ffado_ringbuffer_reset(m_event_buffer); resetFrameCounter(); m_current_rate = m_nominal_rate; m_dll_e2 = m_current_rate * (float)m_update_period; return true; } /** * \brief Prepares the TimestampedBuffer * * Prepare the TimestampedBuffer. This allocates all internal buffers and * initializes all data structures. * * This should be called after parameters such as buffer size, event size etc.. are set, * and before any read/write operations are performed. * * @return true if successful */ bool TimestampedBuffer::prepare() { debugOutput(DEBUG_LEVEL_VERBOSE,"Preparing buffer (%p)\n",this); debugOutput(DEBUG_LEVEL_VERBOSE," Size=%u events, events/frame=%u, event size=%ubytes\n", m_buffer_size,m_events_per_frame,m_event_size); debugOutput(DEBUG_LEVEL_VERBOSE," update period %u\n", m_update_period); debugOutput(DEBUG_LEVEL_VERBOSE," nominal rate=%f\n", m_nominal_rate); debugOutput(DEBUG_LEVEL_VERBOSE," wrapping at " TIMESTAMP_FORMAT_SPEC "\n",m_wrap_at); assert(m_buffer_size); assert(m_events_per_frame); assert(m_event_size); assert(m_nominal_rate != 0.0L); assert(m_update_period != 0); m_current_rate = m_nominal_rate; if( !resizeBuffer(m_buffer_size) ) { debugError("Failed to allocate the event buffer\n"); return false; } // allocate the temporary cluster buffer // NOTE: has to be a multiple of 8 in order to // correctly decode midi bytes (since that // enforces packet alignment) m_cluster_size = m_events_per_frame * m_event_size; m_process_block_size = m_cluster_size * FRAMES_PER_PROCESS_BLOCK; if (m_process_buffer != NULL) free(m_process_buffer); if( !(m_process_buffer=(char *)calloc(m_process_block_size, 1))) { debugFatal("Could not allocate temporary cluster buffer\n"); ffado_ringbuffer_free(m_event_buffer); return false; } // init the DLL m_dll_e2 = m_nominal_rate * (float)m_update_period; // init the timestamps to a bogus value, as there is not // really something sane to say about them m_buffer_tail_timestamp = TIMESTAMP_MAX + 1.0; m_buffer_next_tail_timestamp = TIMESTAMP_MAX + 1.0; return true; } /** * Resizes the timestamped buffer * @return true if successful, false if not */ bool TimestampedBuffer::resizeBuffer(unsigned int new_size) { assert(new_size); assert(m_events_per_frame); assert(m_event_size); // if present, free the previous buffer if(m_event_buffer) { ffado_ringbuffer_free(m_event_buffer); } // allocate a new one if( !(m_event_buffer = ffado_ringbuffer_create( (m_events_per_frame * new_size) * m_event_size))) { debugFatal("Could not allocate memory event ringbuffer\n"); return false; } resetFrameCounter(); m_current_rate = m_nominal_rate; m_dll_e2 = m_current_rate * (float)m_update_period; m_buffer_size = new_size; return true; } /** * @brief Insert a dummy frame to the head buffer * * Writes one frame of dummy data to the head of the buffer. * This is to assist the phase sync of several buffers. * * Note: currently the dummy data is added to the tail of the * buffer, but without updating the timestamp. * * @return true if successful */ bool TimestampedBuffer::writeDummyFrame() { unsigned int write_size=m_event_size*m_events_per_frame; char dummy[write_size]; // one frame of garbage memset(dummy,0,write_size); // add the data payload to the ringbuffer if (ffado_ringbuffer_write(m_event_buffer,dummy,write_size) < write_size) { // debugWarning("writeFrames buffer overrun\n"); return false; } // incrementFrameCounter(nframes,ts); // increment without updating the DLL ENTER_CRITICAL_SECTION; m_framecounter++; EXIT_CRITICAL_SECTION; return true; } /** * @brief Write frames to the buffer * * Copies \ref nframes of frames from the buffer pointed to by \ref data to the * internal ringbuffer. The time of the last frame in the buffer is set to \ref ts. * * @param nframes number of frames to copy * @param data pointer to the frame buffer * @param ts timestamp of the last frame copied * @return true if successful */ bool TimestampedBuffer::writeFrames(unsigned int nframes, char *data, ffado_timestamp_t ts) { unsigned int write_size=nframes*m_event_size*m_events_per_frame; if (m_transparent) { // while disabled, we don't update the DLL, nor do we write frames // we just set the correct timestamp for the frames if (m_buffer_tail_timestamp < TIMESTAMP_MAX && m_buffer_next_tail_timestamp < TIMESTAMP_MAX) { incrementFrameCounter(nframes, ts); decrementFrameCounter(nframes); } setBufferTailTimestamp(ts); } else { // add the data payload to the ringbuffer size_t written = ffado_ringbuffer_write(m_event_buffer, data, write_size); if (written < write_size) { debugWarning("ringbuffer full, %u, %zd\n", write_size, written); return false; } incrementFrameCounter(nframes, ts); } return true; } /** * @brief Preload frames into the buffer * * Preload \ref nframes of frames from the buffer pointed to by \ref data to the * internal ringbuffer. Does not care about transparency. Keeps the buffer head or tail * timestamp constant. * * @note not thread safe * * @param nframes number of frames to copy * @param data pointer to the frame buffer * @param keep_head_ts if true, keep the head timestamp constant. If false, keep the * tail timestamp constant. * @return true if successful */ bool TimestampedBuffer::preloadFrames(unsigned int nframes, char *data, bool keep_head_ts) { unsigned int write_size = nframes * m_event_size * m_events_per_frame; // add the data payload to the ringbuffer size_t written = ffado_ringbuffer_write(m_event_buffer, data, write_size); if (written < write_size) { debugWarning("ringbuffer full, request: %u, actual: %zd\n", write_size, written); return false; } // make sure the head timestamp remains identical signed int fc; ffado_timestamp_t ts; if (keep_head_ts) { getBufferHeadTimestamp(&ts, &fc); } else { getBufferTailTimestamp(&ts, &fc); } // update frame counter m_framecounter += nframes; if (keep_head_ts) { setBufferHeadTimestamp(ts); } else { setBufferTailTimestamp(ts); } return true; } /** * @brief Drop frames from the head of the buffer * * drops \ref nframes of frames from the head of internal buffer * * @param nframes number of frames to drop * @return true if successful */ bool TimestampedBuffer::dropFrames(unsigned int nframes) { unsigned int read_size = nframes * m_event_size * m_events_per_frame; ffado_ringbuffer_read_advance(m_event_buffer, read_size); decrementFrameCounter(nframes); return true; } /** * @brief Read frames from the buffer * * Copies \ref nframes of frames from the internal buffer to the data buffer pointed * to by \ref data. * * @param nframes number of frames to copy * @param data pointer to the frame buffer * @return true if successful */ bool TimestampedBuffer::readFrames(unsigned int nframes, char *data) { unsigned int read_size=nframes*m_event_size*m_events_per_frame; if (m_transparent) { return true; // FIXME: the data still doesn't make sense! } else { // get the data payload to the ringbuffer if ((ffado_ringbuffer_read(m_event_buffer,data,read_size)) < read_size) { debugWarning("readFrames buffer underrun\n"); return false; } decrementFrameCounter(nframes); } return true; } /** * @brief Performs block processing write of frames * * This function allows for zero-copy writing into the ringbuffer. * It calls the client's processWriteBlock function to write frames * into the internal buffer's data area, in a thread safe fashion. * * It also updates the timestamp. * * @param nbframes number of frames to process * @param ts timestamp of the last frame written to the buffer * @return true if successful */ bool TimestampedBuffer::blockProcessWriteFrames(unsigned int nbframes, ffado_timestamp_t ts) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p) Writing %u frames for ts " TIMESTAMP_FORMAT_SPEC "\n", this, nbframes, ts); int xrun; unsigned int offset = 0; ffado_ringbuffer_data_t vec[2]; // we received one period of frames // this is period_size*dimension of events unsigned int events2write = nbframes * m_events_per_frame; unsigned int bytes2write = events2write * m_event_size; /* write events2write bytes to the ringbuffer * first see if it can be done in one read. * if so, ok. * otherwise write up to a multiple of clusters directly to the buffer * then do the buffer wrap around using ringbuffer_write * then write the remaining data directly to the buffer in a third pass * Make sure that we cannot end up on a non-cluster aligned position! */ while(bytes2write > 0) { int byteswritten = 0; unsigned int frameswritten = (nbframes * m_cluster_size - bytes2write) / m_cluster_size; offset = frameswritten; ffado_ringbuffer_get_write_vector(m_event_buffer, vec); if(vec[0].len + vec[1].len < m_process_block_size) { // this indicates a full event buffer debugError("Event buffer overrun in buffer %p, fill: %zd, bytes2write: %u \n", this, ffado_ringbuffer_read_space(m_event_buffer), bytes2write); debugShowBackLog(); return false; } /* if we don't take care we will get stuck in an infinite loop * because we align to a cluster boundary later * the remaining nb of bytes in one write operation can be * smaller than one cluster * this can happen because the ringbuffer size is always a power of 2 */ if(vec[0].len < m_process_block_size) { // encode to the temporary buffer // note that we always process 8 frames at once, in order to ensure that // we don't have to care about the DBC field xrun = m_Client->processWriteBlock(m_process_buffer, FRAMES_PER_PROCESS_BLOCK, offset); if(xrun < 0) { // xrun detected debugError("Frame buffer underrun in buffer %p\n",this); return false; } // use the ringbuffer function to write one cluster // the write function handles the wrap around. ffado_ringbuffer_write(m_event_buffer, m_process_buffer, m_process_block_size); // we advanced one cluster_size bytes2write -= m_process_block_size; } else { // if(bytes2write > vec[0].len) { // align to a cluster boundary byteswritten = vec[0].len - (vec[0].len % m_process_block_size); } else { byteswritten = bytes2write; } xrun = m_Client->processWriteBlock(vec[0].buf, byteswritten / m_cluster_size, offset); if(xrun < 0 ) { // xrun detected debugError("Frame buffer underrun in buffer %p\n",this); return false; // FIXME: return false ? } ffado_ringbuffer_write_advance(m_event_buffer, byteswritten); bytes2write -= byteswritten; } // the bytes2write should always be process block aligned assert(bytes2write % m_process_block_size == 0); } incrementFrameCounter(nbframes,ts); return true; } /** * @brief Performs block processing read of frames * * This function allows for zero-copy reading from the ringbuffer. * It calls the client's processReadBlock function to read frames * directly from the internal buffer's data area, in a thread safe * fashion. * * @param nbframes number of frames to process * @return true if successful */ bool TimestampedBuffer::blockProcessReadFrames(unsigned int nbframes) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p) Reading %u frames\n", this, nbframes); int xrun; unsigned int offset = 0; ffado_ringbuffer_data_t vec[2]; // we received one period of frames on each connection // this is period_size*dimension of events unsigned int events2read = nbframes * m_events_per_frame; unsigned int bytes2read = events2read * m_event_size; /* read events2read bytes from the ringbuffer * first see if it can be done in one read. * if so, ok. * otherwise read up to a multiple of clusters directly from the buffer * then do the buffer wrap around using ringbuffer_read * then read the remaining data directly from the buffer in a third pass * Make sure that we cannot end up on a non-cluster aligned position! */ while(bytes2read > 0) { unsigned int framesread = (nbframes * m_cluster_size - bytes2read) / m_cluster_size; offset = framesread; int bytesread = 0; ffado_ringbuffer_get_read_vector(m_event_buffer, vec); if(vec[0].len + vec[1].len < m_process_block_size) { // this indicates an empty event buffer debugError("Event buffer underrun in buffer %p\n",this); return false; } /* if we don't take care we will get stuck in an infinite loop * because we align to a cluster boundary later * the remaining nb of bytes in one read operation can be smaller than one cluster * this can happen because the ringbuffer size is always a power of 2 */ if(vec[0].len < m_process_block_size) { // use the ringbuffer function to read one cluster // the read function handles wrap around ffado_ringbuffer_read(m_event_buffer, m_process_buffer, m_process_block_size); assert(m_Client); // note that we always process 8 frames at once, in order to ensure that // we don't have to care about the DBC field xrun = m_Client->processReadBlock(m_process_buffer, FRAMES_PER_PROCESS_BLOCK, offset); if(xrun < 0) { // xrun detected debugError("Frame buffer overrun in buffer %p\n",this); return false; } // we advanced one cluster_size bytes2read -= m_process_block_size; } else { // if(bytes2read > vec[0].len) { // align to a cluster boundary bytesread = vec[0].len - (vec[0].len % m_process_block_size); } else { bytesread = bytes2read; } assert(m_Client); xrun = m_Client->processReadBlock(vec[0].buf, bytesread/m_cluster_size, offset); if(xrun < 0) { // xrun detected debugError("Frame buffer overrun in buffer %p\n",this); return false; } ffado_ringbuffer_read_advance(m_event_buffer, bytesread); bytes2read -= bytesread; } // the bytes2read should always be cluster aligned assert(bytes2read % m_process_block_size == 0); } decrementFrameCounter(nbframes); return true; } /** * @brief Sets the buffer tail timestamp. * * Set the buffer tail timestamp to \ref new_timestamp. This will recalculate * the internal state such that the buffer's timeframe starts at * \ref new_timestamp. * * This is thread safe. * * @note considers offsets * * @param new_timestamp */ void TimestampedBuffer::setBufferTailTimestamp(ffado_timestamp_t new_timestamp) { // add the offsets ffado_timestamp_t ts = new_timestamp; if (ts >= m_wrap_at) { ts -= m_wrap_at; } else if (ts < 0) { ts += m_wrap_at; } #ifdef DEBUG if (new_timestamp >= m_wrap_at) { debugWarning("timestamp not wrapped: " TIMESTAMP_FORMAT_SPEC "\n",new_timestamp); } if ((ts >= m_wrap_at) || (ts < 0 )) { debugWarning("ts not wrapped correctly: " TIMESTAMP_FORMAT_SPEC "\n",ts); } #endif ENTER_CRITICAL_SECTION; m_buffer_tail_timestamp = ts; m_dll_e2 = m_update_period * (double)m_current_rate; m_buffer_next_tail_timestamp = (ffado_timestamp_t)((double)m_buffer_tail_timestamp + m_dll_e2); EXIT_CRITICAL_SECTION; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "for (%p) to " TIMESTAMP_FORMAT_SPEC " => " TIMESTAMP_FORMAT_SPEC ", " "NTS=" TIMESTAMP_FORMAT_SPEC ", DLL2=%f, RATE=%f\n", this, new_timestamp, ts, m_buffer_next_tail_timestamp, m_dll_e2, getRate()); } /** * @brief Sets the buffer head timestamp. * * Set the buffer tail timestamp such that the buffer head timestamp becomes * \ref new_timestamp. This does not consider offsets, because it's use is to * make sure the following is true after setBufferHeadTimestamp(x): * x == getBufferHeadTimestamp() * * This is thread safe. * * @param new_timestamp */ void TimestampedBuffer::setBufferHeadTimestamp(ffado_timestamp_t new_timestamp) { #ifdef DEBUG if (new_timestamp >= m_wrap_at) { debugWarning("timestamp not wrapped: " TIMESTAMP_FORMAT_SPEC "\n", new_timestamp); } #endif ffado_timestamp_t ts = new_timestamp; ENTER_CRITICAL_SECTION; // add the time ts += (ffado_timestamp_t)(m_current_rate * (float)(m_framecounter)); if (ts >= m_wrap_at) { ts -= m_wrap_at; } else if (ts < 0) { ts += m_wrap_at; } m_buffer_tail_timestamp = ts; m_dll_e2 = m_update_period * (double)m_current_rate; m_buffer_next_tail_timestamp = (ffado_timestamp_t)((double)m_buffer_tail_timestamp + m_dll_e2); EXIT_CRITICAL_SECTION; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "for (%p) to " TIMESTAMP_FORMAT_SPEC " => " TIMESTAMP_FORMAT_SPEC ", " "NTS=" TIMESTAMP_FORMAT_SPEC ", DLL2=%f, RATE=%f\n", this, new_timestamp, ts, m_buffer_next_tail_timestamp, m_dll_e2, getRate()); } /** * \brief return the timestamp of the first frame in the buffer * * This function returns the timestamp of the very first sample in * the StreamProcessor's buffer. It also returns the framecounter value * for which this timestamp is valid. * * @param ts address to store the timestamp in * @param fc address to store the associated framecounter in */ void TimestampedBuffer::getBufferHeadTimestamp(ffado_timestamp_t *ts, signed int *fc) { ENTER_CRITICAL_SECTION; *fc = m_framecounter; *ts = getTimestampFromTail(*fc); EXIT_CRITICAL_SECTION; } /** * \brief return the timestamp of the last frame in the buffer * * This function returns the timestamp of the last frame in * the StreamProcessor's buffer. It also returns the framecounter * value for which this timestamp is valid. * * @param ts address to store the timestamp in * @param fc address to store the associated framecounter in */ void TimestampedBuffer::getBufferTailTimestamp(ffado_timestamp_t *ts, signed int *fc) { ENTER_CRITICAL_SECTION; *fc = m_framecounter; *ts = getTimestampFromTail(0); EXIT_CRITICAL_SECTION; } /** * @brief Get timestamp for a specific position from the buffer tail * * Returns the timestamp for a position that is nframes earlier than the * buffer tail * * @param nframes number of frames * @return timestamp value */ ffado_timestamp_t TimestampedBuffer::getTimestampFromTail(int nframes) { // ts(x) = m_buffer_tail_timestamp - // (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp)/(samples_between_updates)*(x) ffado_timestamp_t timestamp; timestamp = m_buffer_tail_timestamp; timestamp -= (ffado_timestamp_t)((nframes) * m_current_rate); if(timestamp >= m_wrap_at) { timestamp -= m_wrap_at; } else if(timestamp < 0) { timestamp += m_wrap_at; } return timestamp; } /** * @brief Get timestamp for a specific position from the buffer head * * Returns the timestamp for a position that is nframes later than the * buffer head * * @param nframes number of frames * @return timestamp value */ ffado_timestamp_t TimestampedBuffer::getTimestampFromHead(int nframes) { ffado_timestamp_t retval; ENTER_CRITICAL_SECTION; retval = getTimestampFromTail(m_framecounter - nframes); EXIT_CRITICAL_SECTION; return retval; } /** * Resets the frame counter, in a atomic way. This * is thread safe. */ void TimestampedBuffer::resetFrameCounter() { ENTER_CRITICAL_SECTION; m_framecounter = 0; EXIT_CRITICAL_SECTION; } /** * Decrements the frame counter in a thread safe way. * * @param nbframes number of frames to decrement */ void TimestampedBuffer::decrementFrameCounter(unsigned int nbframes) { ENTER_CRITICAL_SECTION; m_framecounter -= nbframes; EXIT_CRITICAL_SECTION; } /** * Increments the frame counter in a thread safe way. * Also updates the timestamp. * * @note the offsets defined by setTicksOffset and setFrameOffset * are added here. * * @param nbframes the number of frames to add * @param new_timestamp the new timestamp */ void TimestampedBuffer::incrementFrameCounter(unsigned int nbframes, ffado_timestamp_t new_timestamp) { // require the timestamps to be in the correct range assert(new_timestamp < m_wrap_at); assert(new_timestamp >= 0); // if this is not true the timestamps have to be corrected // to account for the non-uniform update period assert(nbframes == m_update_period); // the difference between the given TS and the one predicted for this time instant // this is the error for the DLL ffado_timestamp_t diff = new_timestamp - m_buffer_next_tail_timestamp; // correct for when new_timestamp doesn't wrap at the same time as // m_buffer_next_tail_timestamp if (diff > m_wrap_at/2) diff = m_wrap_at - diff; else if (diff < -m_wrap_at/2) diff = m_wrap_at + diff; #ifdef DEBUG // check whether the update is within the allowed bounds ffado_timestamp_t max_abs_diff = m_max_abs_diff; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " nbframes: %d, m_update_period: %d \n", nbframes, m_update_period); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " tail TS: " TIMESTAMP_FORMAT_SPEC ", next tail TS: " TIMESTAMP_FORMAT_SPEC "\n", m_buffer_tail_timestamp, m_buffer_next_tail_timestamp); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " new TS: " TIMESTAMP_FORMAT_SPEC ", diff: " TIMESTAMP_FORMAT_SPEC "\n", new_timestamp, diff); if (diff > max_abs_diff) { //debugShowBackLogLines(100); debugWarning("(%p) difference rather large (+): diff=" TIMESTAMP_FORMAT_SPEC ", max=" TIMESTAMP_FORMAT_SPEC ", " TIMESTAMP_FORMAT_SPEC ", " TIMESTAMP_FORMAT_SPEC "\n", this, diff, max_abs_diff, new_timestamp, m_buffer_next_tail_timestamp); } else if (diff < -max_abs_diff) { //debugShowBackLogLines(100); debugWarning("(%p) difference rather large (-): diff=" TIMESTAMP_FORMAT_SPEC ", max=" TIMESTAMP_FORMAT_SPEC ", " TIMESTAMP_FORMAT_SPEC ", " TIMESTAMP_FORMAT_SPEC "\n", this, diff, -max_abs_diff, new_timestamp, m_buffer_next_tail_timestamp); } debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p): diff=" TIMESTAMP_FORMAT_SPEC " ", this, diff); #endif double err = diff; debugOutputShortExtreme(DEBUG_LEVEL_VERY_VERBOSE, "diff2=" TIMESTAMP_FORMAT_SPEC " err=%f\n", diff, err); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "B: FC=%10u, TS=" TIMESTAMP_FORMAT_SPEC ", NTS=" TIMESTAMP_FORMAT_SPEC "\n", m_framecounter, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp); ENTER_CRITICAL_SECTION; m_framecounter += nbframes; m_buffer_tail_timestamp = m_buffer_next_tail_timestamp; m_buffer_next_tail_timestamp = m_buffer_next_tail_timestamp + (ffado_timestamp_t)(m_dll_b * err + m_dll_e2); m_dll_e2 += m_dll_c*err; if (m_buffer_next_tail_timestamp >= m_wrap_at) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "Unwrapping next tail timestamp: " TIMESTAMP_FORMAT_SPEC "", m_buffer_next_tail_timestamp); m_buffer_next_tail_timestamp -= m_wrap_at; debugOutputShortExtreme(DEBUG_LEVEL_VERY_VERBOSE, " => " TIMESTAMP_FORMAT_SPEC "\n", m_buffer_next_tail_timestamp); } m_current_rate = calculateRate(); EXIT_CRITICAL_SECTION; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "A: TS=" TIMESTAMP_FORMAT_SPEC ", NTS=" TIMESTAMP_FORMAT_SPEC ", DLLe2=%f, RATE=%f\n", m_buffer_tail_timestamp, m_buffer_next_tail_timestamp, m_dll_e2, m_current_rate); if(m_buffer_tail_timestamp>=m_wrap_at) { debugError("Wrapping failed for m_buffer_tail_timestamp! " TIMESTAMP_FORMAT_SPEC "\n",m_buffer_tail_timestamp); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " IN=" TIMESTAMP_FORMAT_SPEC ", TS=" TIMESTAMP_FORMAT_SPEC ", NTS=" TIMESTAMP_FORMAT_SPEC "\n", new_timestamp, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp); } if(m_buffer_next_tail_timestamp>=m_wrap_at) { debugError("Wrapping failed for m_buffer_next_tail_timestamp! " TIMESTAMP_FORMAT_SPEC "\n",m_buffer_next_tail_timestamp); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " IN=" TIMESTAMP_FORMAT_SPEC ", TS=" TIMESTAMP_FORMAT_SPEC ", NTS=" TIMESTAMP_FORMAT_SPEC "\n", new_timestamp, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp); } if(m_buffer_tail_timestamp==m_buffer_next_tail_timestamp) { debugError("Current and next timestamps are equal: " TIMESTAMP_FORMAT_SPEC " " TIMESTAMP_FORMAT_SPEC "\n", m_buffer_tail_timestamp,m_buffer_next_tail_timestamp); } // this DLL allows the calculation of any sample timestamp relative to the buffer tail, // to the next period and beyond (through extrapolation) // // ts(x) = m_buffer_tail_timestamp + // (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp)/(samples_between_updates)*x } /** * @brief Print status info. */ void TimestampedBuffer::dumpInfo() { ffado_timestamp_t ts_head; signed int fc; getBufferHeadTimestamp(&ts_head,&fc); #ifdef DEBUG ffado_timestamp_t diff=(ffado_timestamp_t)ts_head - (ffado_timestamp_t)m_buffer_tail_timestamp; #endif debugOutputShort( DEBUG_LEVEL_NORMAL, " TimestampedBuffer (%p): %04d frames, %04d events\n", this, m_framecounter, getBufferFill()); debugOutputShort( DEBUG_LEVEL_NORMAL, " Timestamps : head: " TIMESTAMP_FORMAT_SPEC ", Tail: " TIMESTAMP_FORMAT_SPEC ", Next tail: " TIMESTAMP_FORMAT_SPEC "\n", ts_head, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp); #ifdef DEBUG debugOutputShort( DEBUG_LEVEL_NORMAL, " Head - Tail : " TIMESTAMP_FORMAT_SPEC " (%f frames)\n", diff, diff/m_dll_e2*m_update_period); #endif debugOutputShort( DEBUG_LEVEL_NORMAL, " DLL Rate : %f (%f)\n", m_dll_e2, m_dll_e2/m_update_period); debugOutputShort( DEBUG_LEVEL_NORMAL, " DLL Bandwidth : %10e 1/ticks (%f Hz)\n", getBandwidth(), getBandwidth() * TICKS_PER_SECOND); } } // end of namespace Util libffado-2.4.5/src/libutil/TimestampedBuffer.h0000644000175000001440000002011214206145246020672 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_TIMESTAMPEDBUFFER__ #define __FFADO_TIMESTAMPEDBUFFER__ #include "debugmodule/debugmodule.h" #include "libutil/ringbuffer.h" #include //typedef float ffado_timestamp_t; //#define TIMESTAMP_FORMAT_SPEC "%14.3f" typedef double ffado_timestamp_t; #define TIMESTAMP_FORMAT_SPEC "%14.3f" #define TIMESTAMP_MAX 3145728000.0 // typedef int64_t ffado_timestamp_t; // #define TIMESTAMP_FORMAT_SPEC "%012lld" namespace Util { class TimestampedBufferClient; /** * \brief Class implementing a frame buffer that is time-aware * * This class implements a buffer that is time-aware. Whenever new frames * are written to the buffer, the timestamp corresponding to the last frame * in the buffer is updated. This allows to calculate the timestamp of any * other frame in the buffer. * * The buffer is a frame buffer, having the following parameters defining * it's behaviour: * - buff_size: buffer size in frames (setBufferSize()) * - events_per_frame: the number of events per frame (setEventsPerFrame()) * - event_size: the storage size of the events (in bytes) (setEventSize()) * * The total size of the buffer (in bytes) is at least * buff_size*events_per_frame*event_size. * * Timestamp tracking is done by requiring that a timestamp is specified every * time frames are added to the buffer. In combination with the buffer fill and * the frame rate (calculated internally), this allows to calculate the timestamp * of any frame in the buffer. In order to initialize the internal data structures, * the setNominalRate() and setUpdatePeriod() functions are provided. * * \note Currently the class only supports fixed size writes of size update_period. * This can change in the future, implementation ideas are already in place. * * The TimestampedBuffer class is time unit agnostic. It can handle any time unit * as long as it fits in a 64 bit unsigned integer. The buffer supports wrapped * timestamps using (...). * * There are two methods of reading and writing to the buffer. * * The first method uses conventional readFrames() and writeFrames() functions. * * The second method makes use of the TimestampedBufferClient interface. When a * TimestampedBuffer is created, it is required that a TimestampedBufferClient is * registered. This client implements the processReadBlock and processWriteBlock * functions. These are block processing 'callbacks' that allow zero-copy processing * of the buffer contents. In order to initiate block processing, the * blockProcessWriteFrames and blockProcessReadFrames functions are provided by * TimestampedBuffer. * */ class TimestampedBuffer { public: TimestampedBuffer ( TimestampedBufferClient * ); virtual ~TimestampedBuffer(); void setMaxAbsDiff ( unsigned int n ) { m_max_abs_diff = n; }; bool writeDummyFrame(); bool dropFrames ( unsigned int nbframes ); bool writeFrames ( unsigned int nbframes, char *data, ffado_timestamp_t ts ); bool readFrames ( unsigned int nbframes, char *data ); bool preloadFrames ( unsigned int nbframes, char *data, bool keep_head_ts ); bool blockProcessWriteFrames ( unsigned int nbframes, ffado_timestamp_t ts ); bool blockProcessReadFrames ( unsigned int nbframes ); bool prepare(); bool clearBuffer(); bool isEnabled() {return m_enabled;}; void enable() {m_enabled=true;}; void disable() {m_enabled=false;}; bool isTransparent() {return m_transparent;}; void setTransparent ( bool v ) {m_transparent=v;}; bool setEventSize ( unsigned int s ); bool setEventsPerFrame ( unsigned int s ); bool setBufferSize ( unsigned int s ); unsigned int getBufferSize() {return m_buffer_size;}; unsigned int getBytesPerFrame() {return m_bytes_per_frame;}; bool setWrapValue ( ffado_timestamp_t w ); unsigned int getBufferFill(); unsigned int getBufferSpace(); // timestamp stuff int getFrameCounter() {return m_framecounter;}; void getBufferHeadTimestamp ( ffado_timestamp_t *ts, signed int *fc ); void getBufferTailTimestamp ( ffado_timestamp_t *ts, signed int *fc ); void setBufferTailTimestamp ( ffado_timestamp_t new_timestamp ); void setBufferHeadTimestamp ( ffado_timestamp_t new_timestamp ); ffado_timestamp_t getTimestampFromTail ( int nframes ); ffado_timestamp_t getTimestampFromHead ( int nframes ); // dll stuff bool setBandwidth(double bw); double getBandwidth(); bool setNominalRate ( float r ); float getNominalRate() {return m_nominal_rate;}; float getRate(); void setRate(float rate); bool setUpdatePeriod ( unsigned int t ); unsigned int getUpdatePeriod(); // misc stuff void dumpInfo(); void setVerboseLevel ( int l ) {setDebugLevel ( l );}; bool resizeBuffer(unsigned int size); private: void decrementFrameCounter(unsigned int nbframes); void incrementFrameCounter(unsigned int nbframes, ffado_timestamp_t new_timestamp); void resetFrameCounter(); protected: ffado_ringbuffer_t * m_event_buffer; char* m_process_buffer; unsigned int m_cluster_size; unsigned int m_process_block_size; unsigned int m_event_size; // the size of one event unsigned int m_events_per_frame; // the number of events in a frame unsigned int m_buffer_size; // the number of frames in the buffer unsigned int m_bytes_per_frame; unsigned int m_bytes_per_buffer; bool m_enabled; // you can get frames FIXME: rename!! bool m_transparent; // the buffer should hold the frames put in it. if true, discards all frames ffado_timestamp_t m_wrap_at; // value to wrap at TimestampedBufferClient *m_Client; DECLARE_DEBUG_MODULE; private: // the framecounter gives the number of frames in the buffer signed int m_framecounter; // the buffer tail timestamp gives the timestamp of the last frame // that was put into the buffer ffado_timestamp_t m_buffer_tail_timestamp; ffado_timestamp_t m_buffer_next_tail_timestamp; // this mutex protects the access to the framecounter // and the buffer head timestamp. pthread_mutex_t m_framecounter_lock; // tracking DLL variables // JMW: try double for this too // float m_dll_e2; double m_dll_e2; float m_dll_b; float m_dll_c; float m_nominal_rate; float calculateRate(); float m_current_rate; unsigned int m_update_period; unsigned int m_max_abs_diff; }; /** * \brief Interface to be implemented by TimestampedBuffer clients */ class TimestampedBufferClient { public: TimestampedBufferClient() {}; virtual ~TimestampedBufferClient() {}; virtual bool processReadBlock ( char *data, unsigned int nevents, unsigned int offset ) =0; virtual bool processWriteBlock ( char *data, unsigned int nevents, unsigned int offset ) =0; }; } // end of namespace Util #endif /* __FFADO_TIMESTAMPEDBUFFER__ */ libffado-2.4.5/src/libutil/Watchdog.cpp0000644000175000001440000002521614206145246017371 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "Watchdog.h" #include "SystemTimeSource.h" #include "PosixThread.h" #include "config.h" // needed for clock_nanosleep #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include namespace Util { IMPL_DEBUG_MODULE( Watchdog, Watchdog, DEBUG_LEVEL_NORMAL ); // --- Watchdog thread common ancestor --- /// Watchdog::WatchdogTask::WatchdogTask(Watchdog& parent, unsigned int interval_usecs) : m_parent( parent ) , m_interval( interval_usecs ) , m_debugModule( parent.m_debugModule ) { } Watchdog::WatchdogTask::~WatchdogTask() { close(stop_msg_pipe[0]); close(stop_msg_pipe[1]); } bool Watchdog::WatchdogTask::Init() { if (pipe(stop_msg_pipe) == -1) { return false; } return true; } bool Watchdog::WatchdogTask::Execute() { // All watchdog threads share the need to sleep for m_interval usec, with // the ability for this to be interrupted early to speed up program exit. // // Use ppoll() rather than SystemTimeSource::SleepUsecRelative(m_interval) // so the stop message pipe can be monitored, permitting the interruption // of long timing intervals to facilitate program shutdown. struct pollfd fds; struct timespec ts; fds.fd = stop_msg_pipe[0]; fds.events = POLLIN; ts.tv_sec = (m_interval / 1000000); ts.tv_nsec = (m_interval % 1000000) * 1000; if (ppoll(&fds, 1, &ts, NULL)==1 && fds.revents!=0) { debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) watchdog %p received request to stop\n", this, &m_parent); return false; } return true; } void Watchdog::WatchdogTask::ReqStop() { // Signal to the task that it should shop via the message pipe. All // that's needed is to make stop_msg_pipe[0] readable. signed int data = 0; debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) watchdog %p requested to stop\n", this, &m_parent); write(stop_msg_pipe[1], &data, sizeof(data)); } // --- liveness check Thread --- // Watchdog::WatchdogCheckTask::WatchdogCheckTask(Watchdog& parent, unsigned int interval_usecs) : WatchdogTask( parent, interval_usecs ) , m_debugModule( parent.m_debugModule ) { } bool Watchdog::WatchdogCheckTask::Init() { #ifdef DEBUG m_last_loop_entry = 0; m_successive_short_loops = 0; #endif return Watchdog::WatchdogTask::Init(); } bool Watchdog::WatchdogCheckTask::Execute() { if (Watchdog::WatchdogTask::Execute() == false) return false; if(m_parent.getHartbeat()) { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "(%p) watchdog %p still alive\n", this, &m_parent); m_parent.clearHartbeat(); } else { debugWarning("(%p) watchdog %p died\n", this, &m_parent); // set all watched threads to non-rt scheduling m_parent.rescheduleThreads(); } #ifdef DEBUG uint64_t now = Util::SystemTimeSource::getCurrentTimeAsUsecs(); int diff = now - m_last_loop_entry; if(diff < 100) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p) short loop detected (%d usec), cnt: %d\n", this, diff, m_successive_short_loops); m_successive_short_loops++; if(m_successive_short_loops > 100) { debugError("Shutting down runaway thread\n"); return false; } } else { // reset the counter m_successive_short_loops = 0; } m_last_loop_entry = now; #endif return true; } // --- hartbeat Thread --- // Watchdog::WatchdogHartbeatTask::WatchdogHartbeatTask(Watchdog& parent, unsigned int interval_usecs) : WatchdogTask( parent, interval_usecs ) , m_debugModule( parent.m_debugModule ) { } bool Watchdog::WatchdogHartbeatTask::Init() { #ifdef DEBUG m_last_loop_entry = 0; m_successive_short_loops = 0; #endif return Watchdog::WatchdogTask::Init(); } bool Watchdog::WatchdogHartbeatTask::Execute() { if (Watchdog::WatchdogTask::Execute() == false) return false; debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "(%p) watchdog %p hartbeat\n", this, &m_parent); m_parent.setHartbeat(); #ifdef DEBUG uint64_t now = Util::SystemTimeSource::getCurrentTimeAsUsecs(); int diff = now - m_last_loop_entry; if(diff < 100) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p) short loop detected (%d usec), cnt: %d\n", this, diff, m_successive_short_loops); m_successive_short_loops++; if(m_successive_short_loops > 100) { debugError("Shutting down runaway thread\n"); return false; } } else { // reset the counter m_successive_short_loops = 0; } m_last_loop_entry = now; #endif return true; } // the actual watchdog class Watchdog::Watchdog() : m_hartbeat( true ) , m_check_interval( WATCHDOG_DEFAULT_CHECK_INTERVAL_USECS ) , m_realtime( WATCHDOG_DEFAULT_RUN_REALTIME ) , m_priority( WATCHDOG_DEFAULT_PRIORITY ) , m_CheckThread( NULL ) , m_HartbeatThread( NULL ) , m_CheckTask( NULL ) , m_HartbeatTask( NULL ) { } Watchdog::Watchdog(unsigned int interval_usec, bool realtime, unsigned int priority) : m_hartbeat( true ) , m_check_interval( interval_usec ) , m_realtime( realtime ) , m_priority( priority ) , m_CheckThread( NULL ) , m_HartbeatThread( NULL ) , m_CheckTask( NULL ) , m_HartbeatTask( NULL ) { } Watchdog::~Watchdog() { // kill threads instead of stoping them since they are sleeping. // Except that the threads call non-cancel-safe functions, so we have to // use Stop(). Task ReqStop() methods are used to allow the tasks to // prepare for exit by (for example) aborting sleeps. if (m_CheckThread) { m_CheckTask->ReqStop(); m_CheckThread->Stop(); //m_CheckThread->Kill(); delete m_CheckThread; } if (m_HartbeatThread) { m_HartbeatTask->ReqStop(); m_HartbeatThread->Stop(); //m_HartbeatThread->Kill(); delete m_HartbeatThread; } if (m_CheckTask) { delete m_CheckTask; } if (m_HartbeatTask) { delete m_HartbeatTask; } } void Watchdog::setVerboseLevel(int i) { setDebugLevel(i); } bool Watchdog::start() { debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) Starting watchdog...\n", this); debugOutput( DEBUG_LEVEL_VERBOSE, "Create hartbeat task/thread for %p...\n", this); m_HartbeatTask = new WatchdogHartbeatTask( *this, m_check_interval/2 ); if(!m_HartbeatTask) { debugFatal("No hartbeat task\n"); return false; } m_HartbeatThread = new Util::PosixThread(m_HartbeatTask, "WDGHBT", false, 0, PTHREAD_CANCEL_ASYNCHRONOUS); if(!m_HartbeatThread) { debugFatal("No hartbeat thread\n"); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, " hartbeat task: %p, thread %p...\n", m_HartbeatTask, m_HartbeatThread); debugOutput( DEBUG_LEVEL_VERBOSE, "Create check task/thread for %p...\n", this); m_CheckTask = new WatchdogCheckTask( *this, m_check_interval ); if(!m_CheckTask) { debugFatal("No check task\n"); return false; } m_CheckThread = new Util::PosixThread(m_CheckTask,"WDGCHK", false, 0, PTHREAD_CANCEL_ASYNCHRONOUS); if(!m_CheckThread) { debugFatal("No check thread\n"); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, " check task: %p, thread %p...\n", m_CheckTask, m_CheckThread); // switch to realtime if necessary if(m_realtime) { if(!m_CheckThread->AcquireRealTime(m_priority)) { debugWarning("(%p) Could not acquire realtime priotiry for watchdog thread.\n", this); } } // start threads if (m_HartbeatThread->Start() != 0) { debugFatal("Could not start hartbeat thread\n"); return false; } if (m_CheckThread->Start() != 0) { debugFatal("Could not start check thread\n"); return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) Watchdog running...\n", this); return true; } bool Watchdog::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_CheckThread) { if (m_realtime) { m_CheckThread->AcquireRealTime(m_priority); } else { m_CheckThread->DropRealTime(); } } return true; } /** * register a thread to the watchdog * @param thread * @return */ bool Watchdog::registerThread(Thread *thread) { assert(thread); debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) Adding thread %p\n", this, thread); for ( ThreadVectorIterator it = m_Threads.begin(); it != m_Threads.end(); ++it ) { if(*it == thread) { debugError("Thread %p already registered with watchdog\n", thread); return false; } } m_Threads.push_back(thread); return true; } bool Watchdog::unregisterThread(Thread *thread) { assert(thread); debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) unregistering thread %p\n", this, thread); for ( ThreadVectorIterator it = m_Threads.begin(); it != m_Threads.end(); ++it ) { if(*it == thread) { m_Threads.erase(it); return true; } } debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) thread %p not found \n", this, thread); return false; //not found } void Watchdog::rescheduleThreads() { debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) rescheduling threads\n", this); for ( ThreadVectorIterator it = m_Threads.begin(); it != m_Threads.end(); ++it ) { (*it)->DropRealTime(); } } } // end of namespace Util libffado-2.4.5/src/libutil/Watchdog.h0000644000175000001440000000637714206145246017045 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_WATCHDOG__ #define __FFADO_WATCHDOG__ #include "debugmodule/debugmodule.h" #include "libutil/Thread.h" namespace Util { typedef std::vector ThreadVector; typedef std::vector::iterator ThreadVectorIterator; class IsoHandlerManager; class Watchdog { private: class WatchdogTask : public Util::RunnableInterface { public: WatchdogTask(Watchdog& parent, unsigned int interval_usecs); virtual ~WatchdogTask(); bool Init(); bool Execute(); void ReqStop(); Watchdog& m_parent; private: unsigned int m_interval; int stop_msg_pipe[2]; DECLARE_DEBUG_MODULE_REFERENCE; }; class WatchdogCheckTask : public WatchdogTask { public: WatchdogCheckTask(Watchdog& parent, unsigned int interval_usecs); virtual ~WatchdogCheckTask() {}; bool Init(); bool Execute(); private: // debug stuff #ifdef DEBUG uint64_t m_last_loop_entry; int m_successive_short_loops; #endif DECLARE_DEBUG_MODULE_REFERENCE; }; class WatchdogHartbeatTask : public WatchdogTask { public: WatchdogHartbeatTask(Watchdog& parent, unsigned int interval_usecs); virtual ~WatchdogHartbeatTask() {}; bool Init(); bool Execute(); private: // debug stuff #ifdef DEBUG uint64_t m_last_loop_entry; int m_successive_short_loops; #endif DECLARE_DEBUG_MODULE_REFERENCE; }; public: Watchdog(); Watchdog(unsigned int interval_usec, bool realtime, unsigned int priority); virtual ~Watchdog(); bool registerThread(Thread *thread); bool unregisterThread(Thread *thread); bool start(); bool setThreadParameters(bool rt, int priority); void setVerboseLevel(int i); protected: bool getHartbeat() {return m_hartbeat;}; void clearHartbeat() {m_hartbeat=false;}; void setHartbeat() {m_hartbeat=true;}; void rescheduleThreads(); private: ThreadVector m_Threads; bool m_hartbeat; unsigned int m_check_interval; bool m_realtime; int m_priority; Util::Thread * m_CheckThread; Util::Thread * m_HartbeatThread; WatchdogCheckTask * m_CheckTask; WatchdogHartbeatTask * m_HartbeatTask; DECLARE_DEBUG_MODULE; }; } // end of namespace Util #endif /* __FFADO_WATCHDOG__ */ libffado-2.4.5/src/libutil/cmd_serialize.cpp0000644000175000001440000001540314206145246020440 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "cmd_serialize.h" #include #include #include #include #include "libutil/ByteSwap.h" namespace Util { namespace Cmd { IMPL_DEBUG_MODULE( CoutSerializer, CoutSerializer, DEBUG_LEVEL_NORMAL ); IMPL_DEBUG_MODULE( StringSerializer, StringSerializer, DEBUG_LEVEL_NORMAL ); IMPL_DEBUG_MODULE( BufferSerialize, BufferSerialize, DEBUG_LEVEL_NORMAL ); IMPL_DEBUG_MODULE( BufferDeserialize, BufferDeserialize, DEBUG_LEVEL_NORMAL ); bool CoutSerializer::write( byte_t d, const char* name ) { debugOutput( DEBUG_LEVEL_NORMAL, " %3d: 0x%02x %-60.60s\n", m_cnt, d, name ); m_cnt += sizeof( byte_t ); return true; } bool CoutSerializer::write( uint16_t d, const char* name ) { debugOutput( DEBUG_LEVEL_NORMAL, " %3d: 0x%04x %-60.60s\n", m_cnt, d, name ); m_cnt += sizeof( uint16_t ); return true; } bool CoutSerializer::write( quadlet_t d, const char* name ) { debugOutput( DEBUG_LEVEL_NORMAL, " %3d: 0x%08x %-60.60s\n", m_cnt, d, name ); m_cnt += sizeof( quadlet_t ); return true; } bool CoutSerializer::write( const char * v, size_t len, const char* name ) { debugOutput( DEBUG_LEVEL_NORMAL, " %3d: %s %-60.60s\n", m_cnt, v, name ); m_cnt += len; return true; } ////////////////////////////////////////////////// bool StringSerializer::write( byte_t d, const char* name ) { char* result; asprintf( &result, " %3d:\t0x%02x\t%s\n", m_cnt, d, name ); m_string += result; free( result ); m_cnt += sizeof( byte_t ); return true; } bool StringSerializer::write( uint16_t d, const char* name ) { char* result; asprintf( &result, " %3d:\t0x%04x\t%s\n", m_cnt, d, name ); m_string += result; free( result ); m_cnt += sizeof( uint16_t ); return true; } bool StringSerializer::write( quadlet_t d, const char* name ) { char* result; asprintf( &result, " %3d:\t0x%08x\t%s\n", m_cnt, d, name ); m_string += result; free( result ); m_cnt += sizeof( quadlet_t ); return true; } bool StringSerializer::write( const char * v, size_t len, const char* name ) { char* result; asprintf( &result, " %3d:\t%s\t%s\n", m_cnt, v, name ); m_string += result; free( result ); m_cnt += len; return true; } ////////////////////////////////////////////////// bool BufferSerialize::write( byte_t value, const char* name ) { bool result = false; if ( isCurPosValid() ) { *m_curPos = value; m_curPos += sizeof( byte_t ); result = true; } return result; } bool BufferSerialize::write( uint16_t value, const char* name ) { byte_t hi = (value & 0xFF00) >> 8; byte_t lo = value & 0xFF; bool result = false; if ( isCurPosValid() ) { *m_curPos = hi; m_curPos += sizeof( byte_t ); if ( isCurPosValid() ) { *m_curPos = lo; m_curPos += sizeof( byte_t ); result = true; } } return result; } bool BufferSerialize::write( quadlet_t value, const char* name ) { bool result = false; if ( isCurPosValid() ) { * ( quadlet_t* )m_curPos = value; m_curPos += sizeof( quadlet_t ); result = true; } return result; } bool BufferSerialize::write( const char * v, size_t len, const char* name ) { bool result = false; if ( isCurPosValid() ) { m_curPos += len; // avoid write beyond buffer if ( isCurPosValid() ) { m_curPos -= len; memcpy(m_curPos, v, len); m_curPos += len; result = true; } } return result; } bool BufferSerialize::isCurPosValid() const { if ( static_cast( ( m_curPos - m_buffer ) ) >= m_length ) { return false; } return true; } ////////////////////////////////////////////////// bool BufferDeserialize::read( byte_t* value ) { bool result = false; if ( isCurPosValid() ) { *value = *m_curPos; m_curPos += sizeof( byte_t ); result = true; } return result; } bool BufferDeserialize::read( uint16_t* value ) { byte_t hi; byte_t lo; bool result = false; if ( isCurPosValid() ) { hi = *((byte_t *)m_curPos); m_curPos += sizeof( byte_t ); if ( isCurPosValid() ) { lo = *((byte_t *)m_curPos); m_curPos += sizeof( byte_t ); *value=(hi << 8) | lo; result = true; } } return result; } bool BufferDeserialize::read( quadlet_t* value ) { bool result = false; if ( isCurPosValid() ) { *value = *( ( quadlet_t* )m_curPos ); m_curPos += sizeof( quadlet_t ); result = true; } return result; } bool BufferDeserialize::read( char** value, size_t length ) { bool result = false; if ( isCurPosValid() ) { *value = ( char* )m_curPos; m_curPos += length-1; if ( !isCurPosValid() ) { debugError("Read past end of response\n"); result=false; } else { m_curPos++; result = true; } } return result; } bool BufferDeserialize::peek( byte_t* value ) { bool result = false; if ( isCurPosValid() ) { *value = *m_curPos; result = true; } return result; } bool BufferDeserialize::peek( uint16_t* value, size_t offset ) { byte_t hi; byte_t lo; bool result = false; m_curPos+=offset; if ( isCurPosValid() ) { hi = *((byte_t *)m_curPos); m_curPos += sizeof( byte_t ); if ( isCurPosValid() ) { lo = *((byte_t *)m_curPos); *value=(hi << 8) | lo; result = true; } m_curPos -= sizeof( byte_t ); } m_curPos-=offset; return result; } bool BufferDeserialize::skip( size_t length ) { m_curPos+=length; return true; } bool BufferDeserialize::isCurPosValid() const { if ( static_cast( ( m_curPos - m_buffer ) ) >= m_length ) { return false; } return true; } } } libffado-2.4.5/src/libutil/cmd_serialize.h0000644000175000001440000001165614206145246020113 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef UTIL_CMD_SERIALIZE_H #define UTIL_CMD_SERIALIZE_H #include "debugmodule/debugmodule.h" #include // byte_t and quadlet_t declaration #include #include namespace Util { namespace Cmd { // Interfaces class IOSSerialize { public: IOSSerialize() {} virtual ~IOSSerialize() {} virtual bool write( byte_t value, const char* name = "" ) = 0; virtual bool write( uint16_t value, const char* name = "" ) = 0; virtual bool write( quadlet_t value, const char* name = "" ) = 0; virtual bool write( const char *values, size_t len, const char* name = "" ) = 0; }; class IISDeserialize { public: IISDeserialize() {} virtual ~IISDeserialize() {} virtual bool read( byte_t* value ) = 0; virtual bool read( uint16_t* value ) = 0; virtual bool read( quadlet_t* value ) = 0; // note that the value pointer is not valid outside deserialize() virtual bool read( char** value, size_t length ) = 0; virtual bool peek( byte_t* value ) = 0; virtual bool peek( uint16_t* value, size_t offset )=0; virtual bool skip( size_t length ) = 0; virtual int getNrOfConsumedBytes() const = 0; }; // Specialized implementations of previously defined interfaces class CoutSerializer: public IOSSerialize { public: CoutSerializer() : IOSSerialize() , m_cnt( 0 ) {} virtual ~CoutSerializer() {} virtual bool write( byte_t value, const char* name = "" ); virtual bool write( uint16_t value, const char* name = "" ); virtual bool write( quadlet_t value, const char* name = "" ); virtual bool write( const char *values, size_t len, const char* name = "" ); private: unsigned int m_cnt; DECLARE_DEBUG_MODULE; }; class StringSerializer: public IOSSerialize { public: StringSerializer() : IOSSerialize() , m_cnt( 0 ) {} virtual ~StringSerializer() {} virtual bool write( byte_t value, const char* name = "" ); virtual bool write( uint16_t value, const char* name = "" ); virtual bool write( quadlet_t value, const char* name = "" ); virtual bool write( const char *values, size_t len, const char* name = "" ); virtual std::string getString( ) { return m_string;}; private: unsigned int m_cnt; std::string m_string; DECLARE_DEBUG_MODULE; }; class BufferSerialize: public IOSSerialize { public: BufferSerialize( unsigned char* buffer, size_t length ) : IOSSerialize() , m_buffer( buffer ) , m_curPos( m_buffer ) , m_length( length ) {} virtual ~BufferSerialize() {} virtual bool write( byte_t value, const char* name = "" ); virtual bool write( uint16_t value, const char* name = "" ); virtual bool write( quadlet_t value, const char* name = "" ); virtual bool write( const char *values, size_t len, const char* name = "" ); int getNrOfProducesBytes() const { return m_curPos - m_buffer; } protected: inline bool isCurPosValid() const; private: unsigned char* m_buffer; unsigned char* m_curPos; size_t m_length; DECLARE_DEBUG_MODULE; }; class BufferDeserialize: public IISDeserialize { public: BufferDeserialize( const unsigned char* buffer, size_t length ) : IISDeserialize() , m_buffer( const_cast( buffer ) ) , m_curPos( m_buffer ) , m_length( length ) {} virtual ~BufferDeserialize() {} virtual bool read( byte_t* value ); virtual bool read( uint16_t* value ); virtual bool read( quadlet_t* value ); // note that the value pointer is not valid outside deserialize() virtual bool read( char** value, size_t length ); virtual bool peek( byte_t* value ); virtual bool peek( uint16_t* value, size_t offset ); virtual bool skip( size_t length ); int getNrOfConsumedBytes() const { return m_curPos - m_buffer; } protected: inline bool isCurPosValid() const; private: unsigned char* m_buffer; // start of the buffer unsigned char* m_curPos; // current read pos size_t m_length; // size of buffer DECLARE_DEBUG_MODULE; }; } } #endif // UTIL_CMD_SERIALIZE_H libffado-2.4.5/src/libutil/ringbuffer.c0000644000175000001440000002151314206145246017416 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Copied from the jackd sources * function names changed in order to avoid naming problems when using this in * a jackd backend. */ /* Original license: * note that LGPL2.1 allows relicensing the code to GPLv3 or higher * * Copyright (C) 2000 Paul Davis * Copyright (C) 2003 Rohan Drape * * This program 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) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ //#include #include #include #ifdef USE_MLOCK #include #endif /* USE_MLOCK */ #include "ringbuffer.h" /* Create a new ringbuffer to hold at least `sz' bytes of data. The actual buffer size is rounded up to the next power of two. */ ffado_ringbuffer_t * ffado_ringbuffer_create (size_t sz) { int power_of_two; ffado_ringbuffer_t *rb; rb = malloc (sizeof (ffado_ringbuffer_t)); for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++); rb->size = 1 << power_of_two; rb->size_mask = rb->size; rb->size_mask -= 1; rb->write_ptr = 0; rb->read_ptr = 0; rb->buf = malloc (rb->size); rb->mlocked = 0; return rb; } /* Free all data associated with the ringbuffer `rb'. */ void ffado_ringbuffer_free (ffado_ringbuffer_t * rb) { #ifdef USE_MLOCK if (rb->mlocked) { munlock (rb->buf, rb->size); } #endif /* USE_MLOCK */ free (rb->buf); } /* Lock the data block of `rb' using the system call 'mlock'. */ int ffado_ringbuffer_mlock (ffado_ringbuffer_t * rb) { #ifdef USE_MLOCK if (mlock (rb->buf, rb->size)) { return -1; } #endif /* USE_MLOCK */ rb->mlocked = 1; return 0; } /* Reset the read and write pointers to zero. This is not thread safe. */ void ffado_ringbuffer_reset (ffado_ringbuffer_t * rb) { rb->read_ptr = 0; rb->write_ptr = 0; } /* Return the number of bytes available for reading. This is the number of bytes in front of the read pointer and behind the write pointer. */ size_t ffado_ringbuffer_read_space (const ffado_ringbuffer_t * rb) { size_t w, r; w = rb->write_ptr; r = rb->read_ptr; if (w > r) { return w - r; } else { return (w - r + rb->size) & rb->size_mask; } } /* Return the number of bytes available for writing. This is the number of bytes in front of the write pointer and behind the read pointer. */ size_t ffado_ringbuffer_write_space (const ffado_ringbuffer_t * rb) { size_t w, r; w = rb->write_ptr; r = rb->read_ptr; if (w > r) { return ((r - w + rb->size) & rb->size_mask) - 1; } else if (w < r) { return (r - w) - 1; } else { return rb->size - 1; } } /* The copying data reader. Copy at most `cnt' bytes from `rb' to `dest'. Returns the actual number of bytes copied. */ size_t ffado_ringbuffer_read (ffado_ringbuffer_t * rb, char *dest, size_t cnt) { size_t free_cnt; size_t cnt2; size_t to_read; size_t n1, n2; if ((free_cnt = ffado_ringbuffer_read_space (rb)) == 0) { return 0; } to_read = cnt > free_cnt ? free_cnt : cnt; cnt2 = rb->read_ptr + to_read; if (cnt2 > rb->size) { n1 = rb->size - rb->read_ptr; n2 = cnt2 & rb->size_mask; } else { n1 = to_read; n2 = 0; } memcpy (dest, &(rb->buf[rb->read_ptr]), n1); rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask; if (n2) { memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2); rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask; } return to_read; } /* The copying data reader w/o read pointer advance. Copy at most `cnt' bytes from `rb' to `dest'. Returns the actual number of bytes copied. */ size_t ffado_ringbuffer_peek (ffado_ringbuffer_t * rb, char *dest, size_t cnt) { size_t free_cnt; size_t cnt2; size_t to_read; size_t n1, n2; size_t tmp_read_ptr; tmp_read_ptr = rb->read_ptr; if ((free_cnt = ffado_ringbuffer_read_space (rb)) == 0) { return 0; } to_read = cnt > free_cnt ? free_cnt : cnt; cnt2 = tmp_read_ptr + to_read; if (cnt2 > rb->size) { n1 = rb->size - tmp_read_ptr; n2 = cnt2 & rb->size_mask; } else { n1 = to_read; n2 = 0; } memcpy (dest, &(rb->buf[tmp_read_ptr]), n1); tmp_read_ptr += n1; tmp_read_ptr &= rb->size_mask; if (n2) { memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2); // FIXME: tmp_read_ptr is not used anymore tmp_read_ptr += n2; tmp_read_ptr &= rb->size_mask; } return to_read; } /* The copying data writer. Copy at most `cnt' bytes to `rb' from `src'. Returns the actual number of bytes copied. */ size_t ffado_ringbuffer_write (ffado_ringbuffer_t * rb, const char *src, size_t cnt) { size_t free_cnt; size_t cnt2; size_t to_write; size_t n1, n2; if ((free_cnt = ffado_ringbuffer_write_space (rb)) == 0) { return 0; } to_write = cnt > free_cnt ? free_cnt : cnt; cnt2 = rb->write_ptr + to_write; if (cnt2 > rb->size) { n1 = rb->size - rb->write_ptr; n2 = cnt2 & rb->size_mask; } else { n1 = to_write; n2 = 0; } memcpy (&(rb->buf[rb->write_ptr]), src, n1); rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask; if (n2) { memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2); rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask; } return to_write; } /* Advance the read pointer `cnt' places. */ void ffado_ringbuffer_read_advance (ffado_ringbuffer_t * rb, size_t cnt) { rb->read_ptr = (rb->read_ptr + cnt) & rb->size_mask; } /* Advance the write pointer `cnt' places. */ void ffado_ringbuffer_write_advance (ffado_ringbuffer_t * rb, size_t cnt) { rb->write_ptr = (rb->write_ptr + cnt) & rb->size_mask; } /* The non-copying data reader. `vec' is an array of two places. Set the values at `vec' to hold the current readable data at `rb'. If the readable data is in one segment the second segment has zero length. */ void ffado_ringbuffer_get_read_vector (const ffado_ringbuffer_t * rb, ffado_ringbuffer_data_t * vec) { size_t free_cnt; size_t cnt2; size_t w, r; w = rb->write_ptr; r = rb->read_ptr; if (w > r) { free_cnt = w - r; } else { free_cnt = (w - r + rb->size) & rb->size_mask; } cnt2 = r + free_cnt; if (cnt2 > rb->size) { /* Two part vector: the rest of the buffer after the current write ptr, plus some from the start of the buffer. */ vec[0].buf = &(rb->buf[r]); vec[0].len = rb->size - r; vec[1].buf = rb->buf; vec[1].len = cnt2 & rb->size_mask; } else { /* Single part vector: just the rest of the buffer */ vec[0].buf = &(rb->buf[r]); vec[0].len = free_cnt; vec[1].len = 0; } } /* The non-copying data writer. `vec' is an array of two places. Set the values at `vec' to hold the current writeable data at `rb'. If the writeable data is in one segment the second segment has zero length. */ void ffado_ringbuffer_get_write_vector (const ffado_ringbuffer_t * rb, ffado_ringbuffer_data_t * vec) { size_t free_cnt; size_t cnt2; size_t w, r; w = rb->write_ptr; r = rb->read_ptr; if (w > r) { free_cnt = ((r - w + rb->size) & rb->size_mask) - 1; } else if (w < r) { free_cnt = (r - w) - 1; } else { free_cnt = rb->size - 1; } cnt2 = w + free_cnt; if (cnt2 > rb->size) { /* Two part vector: the rest of the buffer after the current write ptr, plus some from the start of the buffer. */ vec[0].buf = &(rb->buf[w]); vec[0].len = rb->size - w; vec[1].buf = rb->buf; vec[1].len = cnt2 & rb->size_mask; } else { vec[0].buf = &(rb->buf[w]); vec[0].len = free_cnt; vec[1].len = 0; } } libffado-2.4.5/src/libutil/ringbuffer.h0000644000175000001440000002062114206145246017422 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Copied from the jackd sources * function names changed in order to avoid naming problems when using this in * a jackd backend. */ /* Original license: * note that LGPL2.1 allows relicensing the code to GPLv3 or higher * * Copyright (C) 2000 Paul Davis * Copyright (C) 2003 Rohan Drape * * This program 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) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #ifndef _FFADO_RINGBUFFER_H #define _FFADO_RINGBUFFER_H #ifdef __cplusplus extern "C" { #endif #include /** @file ringbuffer.h * * * The key attribute of a ringbuffer is that it can be safely accessed * by two threads simultaneously -- one reading from the buffer and * the other writing to it -- without using any synchronization or * mutual exclusion primitives. For this to work correctly, there can * only be a single reader and a single writer thread. Their * identities cannot be interchanged. */ typedef struct { char *buf; size_t len; } ffado_ringbuffer_data_t ; typedef struct { char *buf; volatile size_t write_ptr; volatile size_t read_ptr; size_t size; size_t size_mask; int mlocked; } ffado_ringbuffer_t ; /** * Allocates a ringbuffer data structure of a specified size. The * caller must arrange for a call to ffado_ringbuffer_free() to release * the memory associated with the ringbuffer. * * @param sz the ringbuffer size in bytes. * * @return a pointer to a new ffado_ringbuffer_t, if successful; NULL * otherwise. */ ffado_ringbuffer_t *ffado_ringbuffer_create(size_t sz); /** * Frees the ringbuffer data structure allocated by an earlier call to * ffado_ringbuffer_create(). * * @param rb a pointer to the ringbuffer structure. */ void ffado_ringbuffer_free(ffado_ringbuffer_t *rb); /** * Fill a data structure with a description of the current readable * data held in the ringbuffer. This description is returned in a two * element array of ffado_ringbuffer_data_t. Two elements are needed * because the data to be read may be split across the end of the * ringbuffer. * * The first element will always contain a valid @a len field, which * may be zero or greater. If the @a len field is non-zero, then data * can be read in a contiguous fashion using the address given in the * corresponding @a buf field. * * If the second element has a non-zero @a len field, then a second * contiguous stretch of data can be read from the address given in * its corresponding @a buf field. * * @param rb a pointer to the ringbuffer structure. * @param vec a pointer to a 2 element array of ffado_ringbuffer_data_t. * */ void ffado_ringbuffer_get_read_vector(const ffado_ringbuffer_t *rb, ffado_ringbuffer_data_t *vec); /** * Fill a data structure with a description of the current writable * space in the ringbuffer. The description is returned in a two * element array of ffado_ringbuffer_data_t. Two elements are needed * because the space available for writing may be split across the end * of the ringbuffer. * * The first element will always contain a valid @a len field, which * may be zero or greater. If the @a len field is non-zero, then data * can be written in a contiguous fashion using the address given in * the corresponding @a buf field. * * If the second element has a non-zero @a len field, then a second * contiguous stretch of data can be written to the address given in * the corresponding @a buf field. * * @param rb a pointer to the ringbuffer structure. * @param vec a pointer to a 2 element array of ffado_ringbuffer_data_t. */ void ffado_ringbuffer_get_write_vector(const ffado_ringbuffer_t *rb, ffado_ringbuffer_data_t *vec); /** * Read data from the ringbuffer. * * @param rb a pointer to the ringbuffer structure. * @param dest a pointer to a buffer where data read from the * ringbuffer will go. * @param cnt the number of bytes to read. * * @return the number of bytes read, which may range from 0 to cnt. */ size_t ffado_ringbuffer_read(ffado_ringbuffer_t *rb, char *dest, size_t cnt); /** * Read data from the ringbuffer. Opposed to ffado_ringbuffer_read() * this function does not move the read pointer. Thus it's * a convenient way to inspect data in the ringbuffer in a * continous fashion. The price is that the data is copied * into a user provided buffer. For "raw" non-copy inspection * of the data in the ringbuffer use ffado_ringbuffer_get_read_vector(). * * @param rb a pointer to the ringbuffer structure. * @param dest a pointer to a buffer where data read from the * ringbuffer will go. * @param cnt the number of bytes to read. * * @return the number of bytes read, which may range from 0 to cnt. */ size_t ffado_ringbuffer_peek(ffado_ringbuffer_t *rb, char *dest, size_t cnt); /** * Advance the read pointer. * * After data have been read from the ringbuffer using the pointers * returned by ffado_ringbuffer_get_read_vector(), use this function to * advance the buffer pointers, making that space available for future * write operations. * * @param rb a pointer to the ringbuffer structure. * @param cnt the number of bytes read. */ void ffado_ringbuffer_read_advance(ffado_ringbuffer_t *rb, size_t cnt); /** * Return the number of bytes available for reading. * * @param rb a pointer to the ringbuffer structure. * * @return the number of bytes available to read. */ size_t ffado_ringbuffer_read_space(const ffado_ringbuffer_t *rb); /** * Lock a ringbuffer data block into memory. * * Uses the mlock() system call. This is not a realtime operation. * * @param rb a pointer to the ringbuffer structure. */ int ffado_ringbuffer_mlock(ffado_ringbuffer_t *rb); /** * Reset the read and write pointers, making an empty buffer. * * This is not thread safe. * * @param rb a pointer to the ringbuffer structure. */ void ffado_ringbuffer_reset(ffado_ringbuffer_t *rb); /** * Write data into the ringbuffer. * * @param rb a pointer to the ringbuffer structure. * @param src a pointer to the data to be written to the ringbuffer. * @param cnt the number of bytes to write. * * @return the number of bytes write, which may range from 0 to cnt */ size_t ffado_ringbuffer_write(ffado_ringbuffer_t *rb, const char *src, size_t cnt); /** * Advance the write pointer. * * After data have been written the ringbuffer using the pointers * returned by ffado_ringbuffer_get_write_vector(), use this function * to advance the buffer pointer, making the data available for future * read operations. * * @param rb a pointer to the ringbuffer structure. * @param cnt the number of bytes written. */ void ffado_ringbuffer_write_advance(ffado_ringbuffer_t *rb, size_t cnt); /** * Return the number of bytes available for writing. * * @param rb a pointer to the ringbuffer structure. * * @return the amount of free space (in bytes) available for writing. */ size_t ffado_ringbuffer_write_space(const ffado_ringbuffer_t *rb); #ifdef __cplusplus } #endif #endif // FFADO_RINGBUFFER libffado-2.4.5/src/libutil/serialize.h0000644000175000001440000000176714206145246017272 0ustar jwoitheusers/* * Copyright (C) 2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_UTIL_SERIALIZE_H__ #define __FFADO_UTIL_SERIALIZE_H__ #if SERIALIZE_USE_EXPAT #include "serialize_expat.h" #else #include "serialize_libxml.h" #endif #endif libffado-2.4.5/src/libutil/serialize_expat.cpp0000644000175000001440000002527014206145246021021 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // FOR CACHE_VERSION #include "serialize.h" #include "version.h" #include using namespace std; IMPL_DEBUG_MODULE( Util::XMLSerialize, XMLSerialize, DEBUG_LEVEL_NORMAL ); IMPL_DEBUG_MODULE( Util::XMLDeserialize, XMLDeserialize, DEBUG_LEVEL_NORMAL ); Util::XMLSerialize::XMLSerialize( std::string fileName ) : IOSerialize() , m_filepath( fileName ) , m_verboseLevel( DEBUG_LEVEL_NORMAL ) { setDebugLevel( DEBUG_LEVEL_NORMAL ); try { m_doc.create_root_node( "ffado_cache" ); writeVersion(); } catch ( const exception& ex ) { cout << "Exception caught: " << ex.what(); } } Util::XMLSerialize::XMLSerialize( std::string fileName, int verboseLevel ) : IOSerialize() , m_filepath( fileName ) , m_verboseLevel( verboseLevel ) { setDebugLevel(verboseLevel); try { m_doc.create_root_node( "ffado_cache" ); writeVersion(); } catch ( const exception& ex ) { cout << "Exception caught: " << ex.what(); } } Util::XMLSerialize::~XMLSerialize() { try { m_doc.write_to_file_formatted( m_filepath ); } catch ( const exception& ex ) { cout << "Exception caugth: " << ex.what(); } } void Util::XMLSerialize::writeVersion() { Xml::Node* pNode = m_doc.get_root_node(); if(pNode) { char* valstr; asprintf( &valstr, "%s", CACHE_VERSION ); // check whether we already have a version node Xml::Nodes versionfields = (*pNode)["CacheVersion"]; if(versionfields.size()) { // set the value versionfields.at(0)->set_child_text( valstr ); } else { // make a new field Xml::Node &n = pNode->add( Xml::Node("CacheVersion", NULL) ); n.set_child_text( valstr ); } free( valstr ); } else { debugError("No root node\n"); } } bool Util::XMLSerialize::write( std::string strMemberName, long long value ) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "write %s = %d\n", strMemberName.c_str(), value ); std::vector tokens; tokenize( strMemberName, tokens, "/" ); if ( tokens.size() == 0 ) { debugWarning( "token size is 0\n" ); return false; } Xml::Node* pNode = m_doc.get_root_node(); if(pNode) { pNode = getNodePath( pNode, tokens ); if(pNode) { // element to be added Xml::Node& n = pNode->add( Xml::Node(tokens[tokens.size() - 1].c_str(), NULL) ); char* valstr; asprintf( &valstr, "%"PRId64"", value ); n.set_child_text( valstr ); free( valstr ); } else { return false; } } else { return false; } return true; } bool Util::XMLSerialize::write( std::string strMemberName, std::string str) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "write %s = %s\n", strMemberName.c_str(), str.c_str() ); std::vector tokens; tokenize( strMemberName, tokens, "/" ); if ( tokens.size() == 0 ) { debugWarning( "token size is 0\n" ); return false; } Xml::Node* pNode = m_doc.get_root_node(); if(pNode) { pNode = getNodePath( pNode, tokens ); if(pNode) { // element to be added Xml::Node& n = pNode->add( Xml::Node(tokens[tokens.size() - 1].c_str(), NULL) ); n.set_child_text( str ); } else { return false; } } else { return false; } return true; } Util::Xml::Node* Util::XMLSerialize::getNodePath( Util::Xml::Node* pRootNode, std::vector& tokens ) { // returns the correct node on which the new element has to be added. // if the path does not exist, it will be created. if ( tokens.size() == 1 ) { return pRootNode; } unsigned int iTokenIdx = 0; Xml::Node* pCurNode = pRootNode; for (bool bFound = false; ( iTokenIdx < tokens.size() - 1 ); bFound = false, iTokenIdx++ ) { Xml::Node::Children& nodeList = pCurNode->children; for ( Xml::Node::Children::iterator it = nodeList.begin(); it != nodeList.end(); ++it ) { Xml::Node* thisNode = &( *it ); if ( thisNode->name.compare(tokens[iTokenIdx]) == 0 ) { pCurNode = thisNode; bFound = true; break; } } if ( !bFound ) { break; } } for ( unsigned int i = iTokenIdx; i < tokens.size() - 1; i++, iTokenIdx++ ) { Xml::Node& n = pCurNode->add( Xml::Node(tokens[iTokenIdx].c_str(), NULL) ); pCurNode = &n; } return pCurNode; } /***********************************/ Util::XMLDeserialize::XMLDeserialize( std::string fileName ) : IODeserialize() , m_filepath( fileName ) , m_verboseLevel( DEBUG_LEVEL_NORMAL ) { setDebugLevel(DEBUG_LEVEL_NORMAL); try { // m_parser.set_substitute_entities(); //We just want the text to //be resolved/unescaped //automatically. m_doc.load_from_file(m_filepath); } catch ( const exception& ex ) { cout << "Exception caught: " << ex.what(); } } Util::XMLDeserialize::XMLDeserialize( std::string fileName, int verboseLevel ) : IODeserialize() , m_filepath( fileName ) , m_verboseLevel( verboseLevel ) { setDebugLevel(verboseLevel); try { // m_parser.set_substitute_entities(); //We just want the text to //be resolved/unescaped //automatically. m_doc.load_from_file(m_filepath); } catch ( const exception& ex ) { cout << "Exception caught: " << ex.what(); } } Util::XMLDeserialize::~XMLDeserialize() { } bool Util::XMLDeserialize::isValid() { return checkVersion(); } bool Util::XMLDeserialize::checkVersion() { std::string savedVersion; if (read( "CacheVersion", savedVersion )) { std::string expectedVersion = CACHE_VERSION; debugOutput( DEBUG_LEVEL_NORMAL, "Cache version: %s, expected: %s.\n", savedVersion.c_str(), expectedVersion.c_str() ); if (expectedVersion == savedVersion) { debugOutput( DEBUG_LEVEL_VERBOSE, "Cache version OK.\n" ); return true; } else { debugOutput( DEBUG_LEVEL_VERBOSE, "Cache version not OK.\n" ); return false; } } else return false; } bool Util::XMLDeserialize::read( std::string strMemberName, long long& value ) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "lookup %s\n", strMemberName.c_str() ); Xml::Node* pNode = m_doc.get_root_node(); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "pNode = %s\n", pNode->name.c_str() ); if(pNode) { Xml::Nodes nodeSet = pNode->find( strMemberName ); for ( Xml::Nodes::iterator it = nodeSet.begin(); it != nodeSet.end(); ++it ) { Xml::Node *n = *it; if(n && n->has_child_text() ) { char* tail; value = strtoll( n->get_child_text().c_str(), &tail, 0 ); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "found %s = %d\n", strMemberName.c_str(), value ); return true; } debugWarning( "no such a node %s\n", strMemberName.c_str() ); return false; } debugWarning( "no such a node %s\n", strMemberName.c_str() ); return false; } else { debugError("No root node\n"); return false; } } bool Util::XMLDeserialize::read( std::string strMemberName, std::string& str ) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "lookup %s\n", strMemberName.c_str() ); Xml::Node* pNode = m_doc.get_root_node(); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "pNode = %s\n", pNode->name.c_str() ); if(pNode) { Xml::Nodes nodeSet = pNode->find( strMemberName ); for ( Xml::Nodes::iterator it = nodeSet.begin(); it != nodeSet.end(); ++it ) { Xml::Node *n = *it; if(n) { if(n->has_child_text() ) { str = n->get_child_text(); } else { str = ""; } debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "found %s = %s\n", strMemberName.c_str(), str.c_str() ); return true; } debugWarning( "no such a node %s\n", strMemberName.c_str() ); return false; } debugWarning( "no such a node %s\n", strMemberName.c_str() ); return false; } else { debugError("No root node\n"); return false; } } bool Util::XMLDeserialize::isExisting( std::string strMemberName ) { Xml::Node* pNode = m_doc.get_root_node(); if(pNode) { Xml::Nodes nodeSet = pNode->find( strMemberName ); return nodeSet.size() > 0; } else return false; } void tokenize(const string& str, vector& tokens, const string& delimiters) { // Skip delimiters at beginning. string::size_type lastPos = str.find_first_not_of(delimiters, 0); // Find first "non-delimiter". string::size_type pos = str.find_first_of(delimiters, lastPos); while (string::npos != pos || string::npos != lastPos) { // Found a token, add it to the vector. tokens.push_back(str.substr(lastPos, pos - lastPos)); // Skip delimiters. Note the "not_of" lastPos = str.find_first_not_of(delimiters, pos); // Find next "non-delimiter" pos = str.find_first_of(delimiters, lastPos); } } libffado-2.4.5/src/libutil/serialize_expat.h0000644000175000001440000000772714206145246020475 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_UTIL_SERIALIZE_EXPAT_H__ #define __FFADO_UTIL_SERIALIZE_EXPAT_H__ #include "debugmodule/debugmodule.h" #include "serialize_expat_xml.h" #include #include namespace Util { class IOSerialize { public: IOSerialize() {} virtual ~IOSerialize() {} virtual bool write( std::string strMemberName, long long value ) = 0; virtual bool write( std::string strMemberName, std::string str) = 0; template bool write( std::string strMemberName, T value ); }; class IODeserialize { public: IODeserialize() {} virtual ~IODeserialize() {} virtual bool read( std::string strMemberName, long long& value ) = 0; virtual bool read( std::string strMemberName, std::string& str ) = 0; template bool read( std::string strMemberName, T& value ); virtual bool isExisting( std::string strMemberName ) = 0; }; class XMLSerialize: public IOSerialize { public: XMLSerialize( std::string fileName ); XMLSerialize( std::string fileName, int verboseLevel ); virtual ~XMLSerialize(); virtual bool write( std::string strMemberName, long long value ); virtual bool write( std::string strMemberName, std::string str); private: void writeVersion(); std::string m_filepath; Xml::Document m_doc; int m_verboseLevel; DECLARE_DEBUG_MODULE; Xml::Node* getNodePath( Xml::Node* pRootNode, std::vector& tokens ); }; class XMLDeserialize: public IODeserialize { public: XMLDeserialize( std::string fileName ); XMLDeserialize( std::string fileName, int verboseLevel ); virtual ~XMLDeserialize(); virtual bool read( std::string strMemberName, long long& value ); virtual bool read( std::string strMemberName, std::string& str ); virtual bool isExisting( std::string strMemberName ); bool isValid(); bool checkVersion(); private: std::string m_filepath; Xml::Document m_doc; int m_verboseLevel; DECLARE_DEBUG_MODULE; }; ////////////////////////////////////////// template bool IOSerialize::write( std::string strMemberName, T value ) { return write( strMemberName, static_cast( value ) ); } template bool IODeserialize::read( std::string strMemberName, T& value ) { long long tmp; bool result = read( strMemberName, tmp ); value = static_cast( tmp ); return result; } } void tokenize(const std::string& str, std::vector& tokens, const std::string& delimiters = " "); #endif libffado-2.4.5/src/libutil/serialize_expat_xml.cpp0000644000175000001440000002243314206145246021677 0ustar jwoitheusers/* * Parts Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * * 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 "serialize_expat_xml.h" #include #include #include IMPL_DEBUG_MODULE( Util::Xml::Node, Node, DEBUG_LEVEL_NORMAL ); IMPL_DEBUG_MODULE( Util::Xml::Document, Document, DEBUG_LEVEL_NORMAL ); std::istream& operator >> ( std::istream& in, Util::Xml::Document& doc ) { std::stringbuf xmlbuf; in.get(xmlbuf, '\0'); doc.from_xml(xmlbuf.str()); return in; } std::ostream& operator << ( std::ostream& out, const Util::Xml::Document& doc ) { return out << doc.to_xml(); } using namespace Util; using namespace Util::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]; debugOutput(DEBUG_LEVEL_VERBOSE, "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; } Nodes Node::find(const std::string &path) { std::string::size_type slash_pos = path.find_first_of( "/" ); if(slash_pos == std::string::npos) { // end of the line return (*this)[path]; } else { // grab the prefix and suffix std::string prefix = path.substr(0, slash_pos); std::string suffix = path.substr(slash_pos+1); // accumulate results over all nodes Nodes results; Nodes nodes = (*this)[prefix]; for(int i=0; ifind(suffix); for(int j=0; jfirst+"=\""+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; debugOutput(DEBUG_LEVEL_VERBOSE, "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 char *name ) { Document* doc = (Document*)data; debugOutput(DEBUG_LEVEL_VERBOSE, "xml:%d <- %s", doc->_depth, name); doc->_depth--; } void Document::create_root_node( const XML_Char *name ) { if(!root) { delete root; _depth = 0; } root = new Node(name, NULL); _depth++; } void Document::write_to_file_formatted( const std::string filename ) { std::string xml = to_xml(); std::ofstream file; file.open(filename.c_str()); file << xml; file.close(); } void Document::load_from_file( const std::string filename ) { std::string xml = ""; std::string chunk = ""; std::ifstream file; if (!file) { debugError("file not opened\n"); return; } file.open(filename.c_str()); while (file >> chunk) { xml = xml + chunk; } file.close(); from_xml(xml); } libffado-2.4.5/src/libutil/serialize_expat_xml.h0000644000175000001440000000774114206145246021351 0ustar jwoitheusers/* * Parts Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * * 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 __FFADO_UTIL_SERIALIZE_EXPAT_XML_H__ #define __FFADO_UTIL_SERIALIZE_EXPAT_XML_H__ #include #include #include #include #include #include #include "debugmodule/debugmodule.h" namespace Util { 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(); } Nodes find(const std::string &path); bool has_child_text() {return cdata.size() !=0;}; std::string get_child_text() {return cdata;}; void set_child_text(std::string t) {cdata=t;}; private: void _raw_xml( std::string& xml, int& depth ) const; Attributes _attrs; DECLARE_DEBUG_MODULE; }; 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; void create_root_node( const char *); Xml::Node* get_root_node() {return root;}; void write_to_file_formatted( const std::string ); void load_from_file( const std::string ); private: int _depth; DECLARE_DEBUG_MODULE; }; } /* namespace Xml */ } /* namespace Util */ std::istream& operator >> ( std::istream&, Util::Xml::Document& ); std::ostream& operator << ( std::ostream&, Util::Xml::Document& ); #endif//__FFADO_UTIL_SERIALIZE_EXPAT_XML_H__ libffado-2.4.5/src/libutil/serialize_libxml.cpp0000644000175000001440000002704714206145246021173 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "version.h" // FOR CACHE_VERSION #include "serialize.h" #include #include #include using namespace std; IMPL_DEBUG_MODULE( Util::XMLSerialize, XMLSerialize, DEBUG_LEVEL_NORMAL ); IMPL_DEBUG_MODULE( Util::XMLDeserialize, XMLDeserialize, DEBUG_LEVEL_NORMAL ); Util::XMLSerialize::XMLSerialize( std::string fileName ) : IOSerialize() , m_filepath( fileName ) , m_verboseLevel( DEBUG_LEVEL_NORMAL ) { setDebugLevel( DEBUG_LEVEL_NORMAL ); try { m_doc.create_root_node( "ffado_cache" ); writeVersion(); } catch ( const exception& ex ) { cout << "Exception caught: " << ex.what(); } } Util::XMLSerialize::XMLSerialize( std::string fileName, int verboseLevel ) : IOSerialize() , m_filepath( fileName ) , m_verboseLevel( verboseLevel ) { setDebugLevel(verboseLevel); try { m_doc.create_root_node( "ffado_cache" ); writeVersion(); } catch ( const exception& ex ) { cout << "Exception caught: " << ex.what(); } } Util::XMLSerialize::~XMLSerialize() { try { m_doc.write_to_file_formatted( m_filepath ); } catch ( const exception& ex ) { cout << "Exception caugth: " << ex.what(); } } void Util::XMLSerialize::writeVersion() { #if LIBXMLXX_MAJOR_VERSION == 3 xmlpp::Element* pElem = m_doc.get_root_node()->add_child_element( "CacheVersion" ); #else xmlpp::Node* pNode = m_doc.get_root_node(); xmlpp::Element* pElem = pNode->add_child( "CacheVersion" ); #endif char* valstr; asprintf( &valstr, "%s", CACHE_VERSION ); #if LIBXMLXX_MAJOR_VERSION == 3 pElem->set_first_child_text( valstr ); #else pElem->set_child_text( valstr ); #endif free( valstr ); } bool Util::XMLSerialize::write( std::string strMemberName, long long value ) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "write %s = %lld\n", strMemberName.c_str(), value ); vector tokens; tokenize( strMemberName, tokens, "/" ); if ( tokens.size() == 0 ) { debugWarning( "token size is 0\n" ); return false; } xmlpp::Element* pNode = m_doc.get_root_node(); pNode = getNodePath( pNode, tokens ); // element to be added #if LIBXMLXX_MAJOR_VERSION == 3 xmlpp::Element* pElem = pNode->add_child_element( tokens[tokens.size() - 1] ); #else xmlpp::Element* pElem = pNode->add_child( tokens[tokens.size() - 1] ); #endif char* valstr; asprintf( &valstr, "%lld", value ); #if LIBXMLXX_MAJOR_VERSION == 3 pElem->set_first_child_text( valstr ); #else pElem->set_child_text( valstr ); #endif free( valstr ); return true; } bool Util::XMLSerialize::write( std::string strMemberName, std::string str) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "write %s = %s\n", strMemberName.c_str(), str.c_str() ); vector tokens; tokenize( strMemberName, tokens, "/" ); if ( tokens.size() == 0 ) { debugWarning( "token size is 0\n" ); return false; } xmlpp::Element* pNode = m_doc.get_root_node(); pNode = getNodePath( pNode, tokens ); // element to be added #if LIBXMLXX_MAJOR_VERSION == 3 xmlpp::Element* pElem = pNode->add_child_element( tokens[tokens.size() - 1] ); pElem->set_first_child_text( str ); #else xmlpp::Element* pElem = pNode->add_child( tokens[tokens.size() - 1] ); pElem->set_child_text( str ); #endif return true; } xmlpp::Element* Util::XMLSerialize::getNodePath( xmlpp::Element* pRootNode, std::vector& tokens ) { // returns the correct node on which the new element has to be added. // if the path does not exist, it will be created. if ( tokens.size() == 1 ) { return pRootNode; } unsigned int iTokenIdx = 0; xmlpp::Element* pCurNode = pRootNode; for (bool bFound = false; ( iTokenIdx < tokens.size() - 1 ); bFound = false, iTokenIdx++ ) { xmlpp::Node::NodeList nodeList = pCurNode->get_children(); for ( xmlpp::Node::NodeList::iterator it = nodeList.begin(); it != nodeList.end(); ++it ) { if ( ( *it )->get_name() == tokens[iTokenIdx] ) { pCurNode = (xmlpp::Element*) *it; bFound = true; break; } } if ( !bFound ) { break; } } for ( unsigned int i = iTokenIdx; i < tokens.size() - 1; i++, iTokenIdx++ ) { #if LIBXMLXX_MAJOR_VERSION == 3 pCurNode = pCurNode->add_child_element( tokens[iTokenIdx] ); #else pCurNode = pCurNode->add_child( tokens[iTokenIdx] ); #endif } return pCurNode; } /***********************************/ Util::XMLDeserialize::XMLDeserialize( std::string fileName ) : IODeserialize() , m_filepath( fileName ) , m_verboseLevel( DEBUG_LEVEL_NORMAL ) { setDebugLevel(DEBUG_LEVEL_NORMAL); try { m_parser.set_substitute_entities(); //We just want the text to //be resolved/unescaped //automatically. m_parser.parse_file( m_filepath ); } catch ( const exception& ex ) { cout << "Exception caught: " << ex.what(); } } Util::XMLDeserialize::XMLDeserialize( std::string fileName, int verboseLevel ) : IODeserialize() , m_filepath( fileName ) , m_verboseLevel( verboseLevel ) { setDebugLevel(verboseLevel); try { m_parser.set_substitute_entities(); //We just want the text to //be resolved/unescaped //automatically. m_parser.parse_file( m_filepath ); } catch ( const exception& ex ) { cout << "Exception caught: " << ex.what(); } } Util::XMLDeserialize::~XMLDeserialize() { } bool Util::XMLDeserialize::isValid() { return checkVersion(); } bool Util::XMLDeserialize::checkVersion() { std::string savedVersion; if (read( "CacheVersion", savedVersion )) { Glib::ustring expectedVersion = CACHE_VERSION; debugOutput( DEBUG_LEVEL_NORMAL, "Cache version: %s, expected: %s.\n", savedVersion.c_str(), expectedVersion.c_str() ); if (expectedVersion == savedVersion) { debugOutput( DEBUG_LEVEL_VERBOSE, "Cache version OK.\n" ); return true; } else { debugOutput( DEBUG_LEVEL_VERBOSE, "Cache version not OK.\n" ); return false; } } else return false; } bool Util::XMLDeserialize::read( std::string strMemberName, long long& value ) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "lookup %s\n", strMemberName.c_str() ); xmlpp::Document *pDoc=m_parser.get_document(); if(!pDoc) { debugWarning( "no document found\n" ); return false; } xmlpp::Node* pNode = pDoc->get_root_node(); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "pNode = %s\n", pNode->get_name().c_str() ); #if LIBXMLXX_MAJOR_VERSION == 3 xmlpp::Node::NodeSet nodeSet = pNode->find( strMemberName ); for ( xmlpp::Node::NodeSet::iterator it = nodeSet.begin(); it != nodeSet.end(); ++it ) #else xmlpp::NodeSet nodeSet = pNode->find( strMemberName ); for ( xmlpp::NodeSet::iterator it = nodeSet.begin(); it != nodeSet.end(); ++it ) #endif { const xmlpp::Element* pElement = dynamic_cast< const xmlpp::Element* >( *it ); if ( pElement && pElement->has_child_text() ) { char* tail; #if LIBXMLXX_MAJOR_VERSION == 3 value = strtoll( pElement->get_first_child_text()->get_content().c_str(), &tail, 0 ); #else value = strtoll( pElement->get_child_text()->get_content().c_str(), &tail, 0 ); #endif debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "found %s = %lld\n", strMemberName.c_str(), value ); return true; } debugWarning( "no such a node %s\n", strMemberName.c_str() ); return false; } debugWarning( "no such a node %s\n", strMemberName.c_str() ); return false; } bool Util::XMLDeserialize::read( std::string strMemberName, std::string& str ) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "lookup %s\n", strMemberName.c_str() ); xmlpp::Document *pDoc=m_parser.get_document(); if(!pDoc) { debugWarning( "no document found\n" ); return false; } xmlpp::Node* pNode = pDoc->get_root_node(); #if LIBXMLXX_MAJOR_VERSION == 3 xmlpp::Node::NodeSet nodeSet = pNode->find( strMemberName ); for ( xmlpp::Node::NodeSet::iterator it = nodeSet.begin(); it != nodeSet.end(); ++it ) #else xmlpp::NodeSet nodeSet = pNode->find( strMemberName ); for ( xmlpp::NodeSet::iterator it = nodeSet.begin(); it != nodeSet.end(); ++it ) #endif { const xmlpp::Element* pElement = dynamic_cast< const xmlpp::Element* >( *it ); if ( pElement ) { if ( pElement->has_child_text() ) { #if LIBXMLXX_MAJOR_VERSION == 3 str = pElement->get_first_child_text()->get_content(); #else str = pElement->get_child_text()->get_content(); #endif } else { str = ""; } debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "found %s = %s\n", strMemberName.c_str(), str.c_str() ); return true; } debugWarning( "no such a node %s\n", strMemberName.c_str() ); return false; } debugWarning( "no such a node %s\n", strMemberName.c_str() ); return false; } bool Util::XMLDeserialize::isExisting( std::string strMemberName ) { xmlpp::Document *pDoc=m_parser.get_document(); if(!pDoc) { return false; } xmlpp::Node* pNode = pDoc->get_root_node(); #if LIBXMLXX_MAJOR_VERSION == 3 xmlpp::Node::NodeSet nodeSet = pNode->find( strMemberName ); #else xmlpp::NodeSet nodeSet = pNode->find( strMemberName ); #endif return nodeSet.size() > 0; } void tokenize(const string& str, vector& tokens, const string& delimiters) { // Skip delimiters at beginning. string::size_type lastPos = str.find_first_not_of(delimiters, 0); // Find first "non-delimiter". string::size_type pos = str.find_first_of(delimiters, lastPos); while (string::npos != pos || string::npos != lastPos) { // Found a token, add it to the vector. tokens.push_back(str.substr(lastPos, pos - lastPos)); // Skip delimiters. Note the "not_of" lastPos = str.find_first_not_of(delimiters, pos); // Find next "non-delimiter" pos = str.find_first_of(delimiters, lastPos); } } libffado-2.4.5/src/libutil/serialize_libxml.h0000644000175000001440000000764514206145246020642 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __FFADO_UTIL_SERIALIZE_XMLPP_H__ #define __FFADO_UTIL_SERIALIZE_XMLPP_H__ #include "debugmodule/debugmodule.h" #include #include #include namespace Util { class IOSerialize { public: IOSerialize() {} virtual ~IOSerialize() {} virtual bool write( std::string strMemberName, long long value ) = 0; virtual bool write( std::string strMemberName, std::string str) = 0; template bool write( std::string strMemberName, T value ); }; class IODeserialize { public: IODeserialize() {} virtual ~IODeserialize() {} virtual bool read( std::string strMemberName, long long& value ) = 0; virtual bool read( std::string strMemberName, std::string& str ) = 0; template bool read( std::string strMemberName, T& value ); virtual bool isExisting( std::string strMemberName ) = 0; }; class XMLSerialize: public IOSerialize { public: XMLSerialize( std::string fileName ); XMLSerialize( std::string fileName, int verboseLevel ); virtual ~XMLSerialize(); virtual bool write( std::string strMemberName, long long value ); virtual bool write( std::string strMemberName, std::string str); private: void writeVersion(); std::string m_filepath; xmlpp::Document m_doc; int m_verboseLevel; DECLARE_DEBUG_MODULE; xmlpp::Element* getNodePath( xmlpp::Element* pRootNode, std::vector& tokens ); }; class XMLDeserialize: public IODeserialize { public: XMLDeserialize( std::string fileName ); XMLDeserialize( std::string fileName, int verboseLevel ); virtual ~XMLDeserialize(); virtual bool read( std::string strMemberName, long long& value ); virtual bool read( std::string strMemberName, std::string& str ); virtual bool isExisting( std::string strMemberName ); bool isValid(); bool checkVersion(); private: std::string m_filepath; xmlpp::DomParser m_parser; int m_verboseLevel; DECLARE_DEBUG_MODULE; }; ////////////////////////////////////////// template bool IOSerialize::write( std::string strMemberName, T value ) { return write( strMemberName, static_cast( value ) ); } template bool IODeserialize::read( std::string strMemberName, T& value ) { long long tmp; bool result = read( strMemberName, tmp ); value = static_cast( tmp ); return result; } } void tokenize(const std::string& str, std::vector& tokens, const std::string& delimiters = " "); #endif libffado-2.4.5/src/libutil/test-dll.cpp0000644000175000001440000000733714206145246017365 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "DelayLockedLoop.h" #include using namespace Util; int main() { int i=0; int i2=0; #define MAX_TEST_ORDER 2 #define NB_VALUES 12 #define NB_LOOPS 50000 // this test is for a second order loop, float omega=6.28*0.001; float coeffs[MAX_TEST_ORDER]; coeffs[0]=1.41*omega; coeffs[1]=omega*omega; DelayLockedLoop d1(1, coeffs); DelayLockedLoop d2(2, coeffs); // this sequence represents the average deviation of the sample period float deviation[NB_VALUES]={-0.001, 0.0, 0.001, 0.001, -0.001, 0.001, -0.001, 0.001, -0.001, 0.00, 0.001, -0.001}; float average=0.0; // these are the actual period times float ideal_values[NB_LOOPS]; float actual_values[NB_LOOPS]; float actual_values2[NB_LOOPS]; // we define a nominal sample time: float ts_nominal=1.0/48000.0; float period=100.0; // we calculate the deviated sample times for (i=0;i. * */ #include "serialize.h" #include "OptionContainer.h" #include #include #include using namespace Util; #define TEST_SHOULD_RETURN_TRUE(test) \ (test ? true : printf( "'" #test "' should return true\n") && false ) #define TEST_SHOULD_RETURN_FALSE(test) \ (test ? printf( "'" #test "' should return true\n") && false : true ) /////////////////////////////////////// class U0_SerializeMe { public: U0_SerializeMe(); bool operator == ( const U0_SerializeMe& rhs ); bool serialize( IOSerialize& ser ); bool deserialize( IODeserialize& deser ); byte_t m_byte; quadlet_t m_quadlet; }; U0_SerializeMe::U0_SerializeMe() : m_byte( 0 ) , m_quadlet( 0 ) { } //-------------------------- bool U0_SerializeMe::operator == ( const U0_SerializeMe& rhs ) { return ( m_byte == rhs.m_byte ) && ( m_quadlet == rhs.m_quadlet ); } bool U0_SerializeMe::serialize( IOSerialize& ser ) { bool result; result = ser.write( "SerializeMe/m_byte", m_byte ); result &= ser.write( "SerializeMe/m_quadlet", m_quadlet ); return result; } bool U0_SerializeMe::deserialize( IODeserialize& deser ) { bool result; result = deser.read( "SerializeMe/m_byte", m_byte ); result &= deser.read( "SerializeMe/m_quadlet", m_quadlet ); return result; } static bool testU0() { U0_SerializeMe sme1; sme1.m_byte = 0x12; sme1.m_quadlet = 0x12345678; { XMLSerialize xmlSerialize( "unittest_u0.xml" ); if ( !sme1.serialize( xmlSerialize ) ) { printf( "(serializing failed)" ); return false; } } U0_SerializeMe sme2; { XMLDeserialize xmlDeserialize( "unittest_u0.xml" ); if ( !sme2.deserialize( xmlDeserialize ) ) { printf( "(deserializing failed)" ); return false; } } bool result = sme1 == sme2; if ( !result ) { printf( "(wrong values)" ); } return result; } /////////////////////////////////////// class U1_SerializeMe { public: U1_SerializeMe(); bool operator == ( const U1_SerializeMe& rhs ); bool serialize( IOSerialize& ser ); bool deserialize( IODeserialize& deser ); quadlet_t m_quadlet0; quadlet_t m_quadlet1; quadlet_t m_quadlet2; }; U1_SerializeMe::U1_SerializeMe() : m_quadlet0( 0 ) , m_quadlet1( 0 ) , m_quadlet2( 0 ) { } //-------------------------- bool U1_SerializeMe::operator == ( const U1_SerializeMe& rhs ) { return ( m_quadlet0 == rhs.m_quadlet0 ) && ( m_quadlet1 == rhs.m_quadlet1) && ( m_quadlet2 == rhs.m_quadlet2); } bool U1_SerializeMe::serialize( IOSerialize& ser ) { bool result; result = ser.write( "here/and/not/there/m_quadlet0", m_quadlet0 ); result &= ser.write( "here/and/not/m_quadlet1", m_quadlet1 ); result &= ser.write( "here/and/m_quadlet2", m_quadlet2 ); return result; } bool U1_SerializeMe::deserialize( IODeserialize& deser ) { bool result; result = deser.read( "here/and/not/there/m_quadlet0", m_quadlet0 ); result &= deser.read( "here/and/not/m_quadlet1", m_quadlet1 ); result &= deser.read( "here/and/m_quadlet2", m_quadlet2 ); return result; } static bool testU1() { U1_SerializeMe sme1; sme1.m_quadlet0 = 0; sme1.m_quadlet1 = 1; sme1.m_quadlet2 = 2; { XMLSerialize xmlSerialize( "unittest_u1.xml" ); if ( !sme1.serialize( xmlSerialize ) ) { printf( "(serializing failed)" ); return false; } } U1_SerializeMe sme2; { XMLDeserialize xmlDeserialize( "unittest_u1.xml" ); if ( !sme2.deserialize( xmlDeserialize ) ) { printf( "(deserializing failed)" ); return false; } } bool result = sme1 == sme2; if ( !result ) { printf( "(wrong values)" ); } return result; } /////////////////////////////////////// class U2_SerializeMe { public: U2_SerializeMe(); bool operator == ( const U2_SerializeMe& rhs ); bool serialize( IOSerialize& ser ); bool deserialize( IODeserialize& deser ); char m_char; unsigned char m_unsigned_char; short m_short; unsigned short m_unsigned_short; int m_int; unsigned int m_unsigned_int; }; U2_SerializeMe::U2_SerializeMe() : m_char( 0 ) , m_unsigned_char( 0 ) , m_short( 0 ) , m_unsigned_short( 0 ) , m_int( 0 ) , m_unsigned_int( 0 ) { } //-------------------------- bool U2_SerializeMe::operator == ( const U2_SerializeMe& rhs ) { return ( m_char == rhs.m_char ) && ( m_unsigned_char == rhs.m_unsigned_char ) && ( m_short == rhs.m_short ) && ( m_unsigned_short == rhs.m_unsigned_short ) && ( m_int == rhs.m_int ) && ( m_unsigned_int == rhs.m_unsigned_int ); } bool U2_SerializeMe::serialize( IOSerialize& ser ) { bool result; result = ser.write( "m_char", m_char ); result &= ser.write( "m_unsigned_char", m_unsigned_char ); result &= ser.write( "m_short", m_short ); result &= ser.write( "m_unsigned_short", m_unsigned_short ); result &= ser.write( "m_int", m_int ); result &= ser.write( "m_unsigned_int", m_unsigned_int ); return result; } bool U2_SerializeMe::deserialize( IODeserialize& deser ) { bool result; result = deser.read( "m_char", m_char ); result &= deser.read( "m_unsigned_char", m_unsigned_char ); result &= deser.read( "m_short", m_short ); result &= deser.read( "m_unsigned_short", m_unsigned_short ); result &= deser.read( "m_int", m_int ); result &= deser.read( "m_unsigned_int", m_unsigned_int ); return result; } static bool testU2execute( U2_SerializeMe& sme1 ) { { XMLSerialize xmlSerialize( "unittest_u2.xml" ); if ( !sme1.serialize( xmlSerialize ) ) { printf( "(serializing failed)" ); return false; } } U2_SerializeMe sme2; { XMLDeserialize xmlDeserialize( "unittest_u2.xml" ); if ( !sme2.deserialize( xmlDeserialize ) ) { printf( "(deserializing failed)" ); return false; } } bool result = sme1 == sme2; if ( !result ) { printf( "(wrong values)" ); } return result; } static bool testU2() { U2_SerializeMe sme1; sme1.m_char = 0; sme1.m_unsigned_char = 1; sme1.m_short = 2; sme1.m_unsigned_short = 3; sme1.m_int = 4; sme1.m_unsigned_int = 5; bool result; result = testU2execute( sme1 ); sme1.m_char = 0xff; sme1.m_unsigned_char = 0xff; sme1.m_short = 0xffff; sme1.m_unsigned_short = 0xffff; sme1.m_int = 0xffffffff; sme1.m_unsigned_int = 0xffffffff; result &= testU2execute( sme1 ); return result; } /////////////////////////////////////// class U3_SerializeMe { public: U3_SerializeMe(); ~U3_SerializeMe(); bool operator == ( const U3_SerializeMe& rhs ); bool serialize( IOSerialize& ser ); bool deserialize( IODeserialize& deser ); const char* m_pString; }; U3_SerializeMe::U3_SerializeMe() : m_pString( 0 ) { } U3_SerializeMe::~U3_SerializeMe() { delete m_pString; } //-------------------------- bool U3_SerializeMe::operator == ( const U3_SerializeMe& rhs ) { return strcmp( m_pString, rhs.m_pString ) == 0; } bool U3_SerializeMe::serialize( IOSerialize& ser ) { bool result; result = ser.write( "m_pString", std::string( m_pString ) ); return result; } bool U3_SerializeMe::deserialize( IODeserialize& deser ) { bool result; std::string str; result = deser.read( "m_pString", str ); m_pString = strdup( str.c_str() ); return result; } static bool testU3() { U3_SerializeMe sme1; sme1.m_pString = strdup( "fancy string" ); { XMLSerialize xmlSerialize( "unittest_u3.xml" ); if ( !sme1.serialize( xmlSerialize ) ) { printf( "(serializing failed)" ); return false; } } U3_SerializeMe sme2; { XMLDeserialize xmlDeserialize( "unittest_u3.xml" ); if ( !sme2.deserialize( xmlDeserialize ) ) { printf( "(deserializing failed)" ); return false; } } bool result = sme1 == sme2; if ( !result ) { printf( "(wrong values)" ); } return result; } ///////////////////////////////////// class testOC : public OptionContainer { public: testOC() {}; ~testOC() {}; bool test() { bool result=true; Option op1=Option(); result &= TEST_SHOULD_RETURN_FALSE(addOption(op1)); op1=Option("option1"); result &= TEST_SHOULD_RETURN_FALSE(addOption(op1)); op1=Option("option1", (float)(1.0)); result &= TEST_SHOULD_RETURN_TRUE(addOption(op1)); result &= TEST_SHOULD_RETURN_FALSE(addOption(op1)); result &= TEST_SHOULD_RETURN_TRUE(removeOption(op1)); result &= TEST_SHOULD_RETURN_FALSE(hasOption(op1)); op1=Option("option1", (int64_t)1); result &= TEST_SHOULD_RETURN_TRUE(addOption(op1)); result &= TEST_SHOULD_RETURN_TRUE(removeOption("option1")); result &= TEST_SHOULD_RETURN_FALSE(hasOption(op1)); op1=Option("option1", (int64_t)(-1)); result &= TEST_SHOULD_RETURN_TRUE(addOption(op1)); Option op2=Option("option1", (double)(1.75)); result &= TEST_SHOULD_RETURN_FALSE(addOption(op2)); op2=Option("option2", (double)(1.75)); result &= TEST_SHOULD_RETURN_TRUE(addOption(op2)); Option op3=Option("option3", (int64_t)(1.75)); result &= TEST_SHOULD_RETURN_TRUE(addOption(op3)); result &= TEST_SHOULD_RETURN_TRUE(countOptions() == 3); int i=0; for ( OptionContainer::iterator it = begin(); it != end(); ++it ) { // printf(" (%s)",(*it).getName().c_str()); i++; } result &= TEST_SHOULD_RETURN_TRUE(i==3); clearOptions(); return result; } void prepare() { Option op1=Option("option1", (int64_t)(-1)); if(!addOption(op1)) { printf( "prepare: could not add valid option (3)\n" ); } Option op2=Option("option2", (double)(1.75)); if(!addOption(op2)) { printf( "prepare: adding an option with a different name should be allowed (1)\n" ); } Option op3=Option("option3", (int64_t)(1.75)); if(!addOption(op3)) { printf( "prepare: adding an option with a different name should be allowed (2)\n" ); } } }; static bool testU4() { bool result=true; testOC oc; result &= TEST_SHOULD_RETURN_TRUE(oc.test()); // now manipulate it externally oc.prepare(); result &= TEST_SHOULD_RETURN_TRUE(oc.hasOption("option1")); result &= TEST_SHOULD_RETURN_TRUE(oc.hasOption("option2")); result &= TEST_SHOULD_RETURN_TRUE(oc.hasOption("option3")); result &= TEST_SHOULD_RETURN_FALSE(oc.hasOption("option4")); oc.setOption("option1", 1024); int tst; result &= TEST_SHOULD_RETURN_TRUE(oc.getOption("option1", tst)); result &= TEST_SHOULD_RETURN_TRUE(tst == 1024); return result; } ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// typedef bool ( *test_func ) (); struct TestEntry { const char* m_pName; test_func m_pFunc; }; TestEntry TestTable[] = { { "serialize 0", testU0 }, { "serialize 1", testU1 }, { "serialize 2", testU2 }, { "serialize 3", testU3 }, { "OptionContainer 1", testU4 }, }; int main( int argc, char** argv ) { int iNrOfPassedTests = 0; int iNrOfFailedTests = 0; int iNrOfTests = sizeof( TestTable )/sizeof( TestTable[0] ); for ( int i = 0; i < iNrOfTests ; ++i ) { TestEntry* pEntry = &TestTable[i]; printf( "Test \"%s\"... ", pEntry->m_pName ); if ( pEntry->m_pFunc() ) { puts( " passed" ); iNrOfPassedTests++; } else { puts( " failed" ); iNrOfFailedTests++; } } printf( "passed: %d\n", iNrOfPassedTests ); printf( "failed: %d\n", iNrOfFailedTests ); printf( "total: %d\n", iNrOfTests ); return 0; } libffado-2.4.5/src/libutil/float_cast.h0000644000175000001440000001315210767315631017416 0ustar jwoitheusers/* ** Copyright (C) 2001-2004 Erik de Castro Lopo ** ** This program 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 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 Lesser General Public License for more details. ** ** You should have received a copy of the GNU Lesser General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Version 1.4 */ #ifndef FLOAT_CAST_HEADER #define FLOAT_CAST_HEADER /*============================================================================ ** On Intel Pentium processors (especially PIII and probably P4), converting ** from float to int is very slow. To meet the C specs, the code produced by ** most C compilers targeting Pentium needs to change the FPU rounding mode ** before the float to int conversion is performed. ** ** Changing the FPU rounding mode causes the FPU pipeline to be flushed. It ** is this flushing of the pipeline which is so slow. ** ** Fortunately the ISO C99 specifications define the functions lrint, lrintf, ** llrint and llrintf which fix this problem as a side effect. ** ** On Unix-like systems, the configure process should have detected the ** presence of these functions. If they weren't found we have to replace them ** here with a standard C cast. */ /* ** The C99 prototypes for lrint and lrintf are as follows: ** ** long int lrintf (float x) ; ** long int lrint (double x) ; */ #include "config.h" /* ** The presence of the required functions are detected during the configure ** process and the values HAVE_LRINT and HAVE_LRINTF are set accordingly in ** the config.h file. */ #define HAVE_LRINT_REPLACEMENT 0 #if (HAVE_LRINT && HAVE_LRINTF) /* ** These defines enable functionality introduced with the 1999 ISO C ** standard. They must be defined before the inclusion of math.h to ** engage them. If optimisation is enabled, these functions will be ** inlined. With optimisation switched off, you have to link in the ** maths library using -lm. */ #define _ISOC9X_SOURCE 1 #define _ISOC99_SOURCE 1 #define __USE_ISOC9X 1 #define __USE_ISOC99 1 #include #elif (defined (__CYGWIN__)) #include #undef HAVE_LRINT_REPLACEMENT #define HAVE_LRINT_REPLACEMENT 1 #undef lrint #undef lrintf #define lrint double2int #define lrintf float2int /* ** The native CYGWIN lrint and lrintf functions are buggy: ** http://sourceware.org/ml/cygwin/2005-06/msg00153.html ** http://sourceware.org/ml/cygwin/2005-09/msg00047.html ** and slow. ** These functions (pulled from the Public Domain MinGW math.h header) ** replace the native versions. */ static inline long double2int (double in) { long retval ; __asm__ __volatile__ ( "fistpl %0" : "=m" (retval) : "t" (in) : "st" ) ; return retval ; } /* double2int */ static inline long float2int (float in) { long retval ; __asm__ __volatile__ ( "fistpl %0" : "=m" (retval) : "t" (in) : "st" ) ; return retval ; } /* float2int */ #elif (defined (WIN32) || defined (_WIN32)) #undef HAVE_LRINT_REPLACEMENT #define HAVE_LRINT_REPLACEMENT 1 #include /* ** Win32 doesn't seem to have these functions. ** Therefore implement inline versions of these functions here. */ __inline long int lrint (double flt) { int intgr ; _asm { fld flt fistp intgr } ; return intgr ; } __inline long int lrintf (float flt) { int intgr ; _asm { fld flt fistp intgr } ; return intgr ; } #elif (defined (__MWERKS__) && defined (macintosh)) /* This MacOS 9 solution was provided by Stephane Letz */ #undef HAVE_LRINT_REPLACEMENT #define HAVE_LRINT_REPLACEMENT 1 #include #undef lrint #undef lrintf #define lrint double2int #define lrintf float2int inline int float2int (register float in) { long res [2] ; asm { fctiw in, in stfd in, res } return res [1] ; } /* float2int */ inline int double2int (register double in) { long res [2] ; asm { fctiw in, in stfd in, res } return res [1] ; } /* double2int */ #elif (defined (__MACH__) && defined (__APPLE__)) /* For Apple MacOSX. */ #undef HAVE_LRINT_REPLACEMENT #define HAVE_LRINT_REPLACEMENT 1 #include #undef lrint #undef lrintf #define lrint double2int #define lrintf float2int inline static long float2int (register float in) { int res [2] ; __asm__ __volatile__ ( "fctiw %1, %1\n\t" "stfd %1, %0" : "=m" (res) /* Output */ : "f" (in) /* Input */ : "memory" ) ; return res [1] ; } /* lrintf */ inline static long double2int (register double in) { int res [2] ; __asm__ __volatile__ ( "fctiw %1, %1\n\t" "stfd %1, %0" : "=m" (res) /* Output */ : "f" (in) /* Input */ : "memory" ) ; return res [1] ; } /* lrint */ #else #ifndef __sgi #warning "Don't have the functions lrint() and lrintf()." #warning "Replacing these functions with a standard C cast." #endif #include #define lrint(dbl) ((long) (dbl)) #define lrintf(flt) ((long) (flt)) #endif #endif /* FLOAT_CAST_HEADER */ /* ** Do not edit or modify anything in this comment block. ** The arch-tag line is a file identity tag for the GNU Arch ** revision control system. ** ** arch-tag: 42db1693-ff61-4051-bac1-e4d24c4e30b7 */ libffado-2.4.5/src/metrichalo/0000755000175000001440000000000014206145613015600 5ustar jwoitheuserslibffado-2.4.5/src/metrichalo/mh_avdevice.cpp0000644000175000001440000001051614206145246020563 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #warning Metric Halo support is currently useless #include "metrichalo/mh_avdevice.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libavc/avc_definitions.h" #include "debugmodule/debugmodule.h" #include "devicemanager.h" #include #include #include #include "libutil/ByteSwap.h" #include #include #include namespace MetricHalo { Device::Device( DeviceManager& d, ffado_smartptr( configRom )) : FFADODevice( d, configRom ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created MetricHalo::Device (NodeID %d)\n", getConfigRom().getNodeId() ); } Device::~Device() { } bool Device::probe( Util::Configuration& c, ConfigRom& configRom, bool generic ) { if (generic) { return false; } else { // check if device is in supported devices list unsigned int vendorId = configRom.getNodeVendorId(); unsigned int modelId = configRom.getModelId(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); return c.isValid(vme) && vme.driver == Util::Configuration::eD_MetricHalo; } } FFADODevice * Device::createDevice( DeviceManager& d, ffado_smartptr( configRom )) { return new Device(d, configRom ); } bool Device::discover() { unsigned int vendorId = getConfigRom().getNodeVendorId(); unsigned int modelId = getConfigRom().getModelId(); Util::Configuration &c = getDeviceManager().getConfiguration(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); if (c.isValid(vme) && vme.driver == Util::Configuration::eD_MetricHalo) { debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", vme.vendor_name.c_str(), vme.model_name.c_str()); } else { debugWarning("Using generic Metric Halo support for unsupported device '%s %s'\n", getConfigRom().getVendorName().c_str(), getConfigRom().getModelName().c_str()); } return false; } int Device::getSamplingFrequency( ) { return 0; } std::vector Device::getSupportedSamplingFrequencies() { std::vector frequencies; return frequencies; } FFADODevice::ClockSourceVector Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; return r; } bool Device::setActiveClockSource(ClockSource s) { return false; } FFADODevice::ClockSource Device::getActiveClockSource() { ClockSource s; return s; } int Device::getConfigurationId( ) { return 0; } bool Device::setSamplingFrequency( int samplingFrequency ) { return false; } bool Device::lock() { return true; } bool Device::unlock() { return true; } void Device::showDevice() { unsigned int vendorId = getConfigRom().getNodeVendorId(); unsigned int modelId = getConfigRom().getModelId(); Util::Configuration &c = getDeviceManager().getConfiguration(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); debugOutput(DEBUG_LEVEL_VERBOSE, "%s %s at node %d\n", vme.vendor_name.c_str(), vme.model_name.c_str(), getNodeId()); } bool Device::prepare() { return true; } int Device::getStreamCount() { return 0; } Streaming::StreamProcessor * Device::getStreamProcessorByIndex(int i) { return NULL; } bool Device::startStreamByIndex(int i) { return false; } bool Device::stopStreamByIndex(int i) { return false; } } libffado-2.4.5/src/metrichalo/mh_avdevice.h0000644000175000001440000000436514206145246020235 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef MHDEVICE_H #define MHDEVICE_H #include "ffadodevice.h" #include "debugmodule/debugmodule.h" #include "libavc/avc_definitions.h" #include "libutil/Configuration.h" // #include "libstreaming/mh/MHStreamProcessor.h" class ConfigRom; class Ieee1394Service; namespace MetricHalo { class Device : public FFADODevice { public: Device( DeviceManager& d, ffado_smartptr( configRom )); virtual ~Device(); static bool probe( Util::Configuration& c, ConfigRom& configRom, bool generic = false ); static FFADODevice * createDevice( DeviceManager& d, ffado_smartptr( configRom )); static int getConfigurationId(); virtual bool discover(); virtual void showDevice(); virtual bool setSamplingFrequency( int ); virtual int getSamplingFrequency( ); virtual std::vector getSupportedSamplingFrequencies(); 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); }; } #endif libffado-2.4.5/src/motu/0000755000175000001440000000000014206145613014435 5ustar jwoitheuserslibffado-2.4.5/src/motu/motu_avdevice.cpp0000644000175000001440000024260014206145246020001 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "motu/motu_avdevice.h" #include "motu/motu_mixerdefs.h" #include "motu/motu_mark3_mixerdefs.h" #include "devicemanager.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/Time.h" #include "libutil/Configuration.h" #include "libcontrol/BasicElements.h" #include #include #include #include "libutil/ByteSwap.h" #include #include #include namespace Motu { // Define the supported devices. Device ordering is arbitary here. To // include a MOTU device which cannot yet be used (for identification // purposes only), set the model field to MOTU_MODEL_NONE. // // The V4HD device includes 4 sub-devices. Include all in the definition as // a way of documenting it. It's likely that only one of these is of // interest for audio but that's still to be determined. static VendorModelEntry supportedDeviceList[] = { // {vendor_id, model_id, unit_version, unit_specifier_id, model, vendor_name,model_name} {FW_VENDORID_MOTU, 0, 0x00000003, 0x000001f2, MOTU_MODEL_828mkII, "MOTU", "828MkII"}, {FW_VENDORID_MOTU, 0, 0x00000009, 0x000001f2, MOTU_MODEL_TRAVELER, "MOTU", "Traveler"}, {FW_VENDORID_MOTU, 0, 0x0000000d, 0x000001f2, MOTU_MODEL_ULTRALITE, "MOTU", "UltraLite"}, {FW_VENDORID_MOTU, 0, 0x0000000f, 0x000001f2, MOTU_MODEL_8PRE, "MOTU", "8pre"}, {FW_VENDORID_MOTU, 0, 0x00000001, 0x000001f2, MOTU_MODEL_828MkI, "MOTU", "828MkI"}, {FW_VENDORID_MOTU, 0, 0x00000005, 0x000001f2, MOTU_MODEL_896HD, "MOTU", "896HD"}, {FW_VENDORID_MOTU, 0, 0x00000015, 0x000001f2, MOTU_MODEL_828mk3, "MOTU", "828Mk3"}, {FW_VENDORID_MOTU, 0, 0x00000017, 0x000001f2, MOTU_MODEL_896mk3, "MOTU", "896Mk3"}, {FW_VENDORID_MOTU, 0, 0x00000019, 0x000001f2, MOTU_MODEL_ULTRALITEmk3, "MOTU", "UltraLiteMk3"}, {FW_VENDORID_MOTU, 0, 0x0000001b, 0x000001f2, MOTU_MODEL_TRAVELERmk3, "MOTU", "TravelerMk3"}, {FW_VENDORID_MOTU, 0, 0x00000021, 0x000001f2, MOTU_MODEL_NONE, "MOTU", "V4HD subdevice 0"}, {FW_VENDORID_MOTU, 0, 0x00000022, 0x000001f2, MOTU_MODEL_NONE, "MOTU", "V4HD subdevice 1"}, {FW_VENDORID_MOTU, 0, 0x00000023, 0x000001f2, MOTU_MODEL_NONE, "MOTU", "V4HD subdevice 2"}, {FW_VENDORID_MOTU, 0, 0x00000024, 0x000001f2, MOTU_MODEL_NONE, "MOTU", "V4HD subdevice 3"}, {FW_VENDORID_MOTU, 0, 0x00000030, 0x000001f2, MOTU_MODEL_ULTRALITEmk3_HYB, "MOTU", "UltraLiteMk3-hybrid"}, {FW_VENDORID_MOTU, 0, 0x00000045, 0x000001f2, MOTU_MODEL_4PRE, "MOTU", "4pre"}, }; // Ports declarations PortGroupEntry PortGroups_828MKI[] = { {"Analog%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, -1, }, {"SPDIF%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, }, {"ADAT%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, }, {"ADAT%d", 4, MOTU_PA_INOUT | MOTU_PA_RATE_2x|MOTU_PA_OPTICAL_ADAT, }, }; PortGroupEntry PortGroups_896HD[] = { {"Mix-%s", 2, MOTU_PA_IN | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, -1, }, {"Phones-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, }, {"Analog%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, }, {"MainOut-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, }, {"unknown-%d", 2, MOTU_PA_IN | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, }, {"ADAT%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, }, {"ADAT%d", 4, MOTU_PA_INOUT | MOTU_PA_RATE_2x|MOTU_PA_OPTICAL_ADAT, }, {"AES/EBU%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, }, }; PortGroupEntry PortGroups_828MKII[] = { {"Mix-%s", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, -1}, {"Phones-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, }, {"Analog%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, }, {"Mic%d", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, }, {"Main-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, }, {"SPDIF%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, }, {"ADAT%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, }, {"ADAT%d", 4, MOTU_PA_INOUT | MOTU_PA_RATE_2x|MOTU_PA_OPTICAL_ADAT, }, }; PortGroupEntry PortGroups_TRAVELER[] = { {"Mix-%s", 2, MOTU_PA_IN | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, -1, }, {"Phones-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, }, {"Analog%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, }, {"AES/EBU%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, }, {"SPDIF%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_OFF|MOTU_PA_OPTICAL_ADAT, }, {"Toslink%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_TOSLINK, }, {"ADAT%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, }, {"ADAT%d", 4, MOTU_PA_INOUT | MOTU_PA_RATE_2x|MOTU_PA_OPTICAL_ADAT, }, }; PortGroupEntry PortGroups_ULTRALITE[] = { {"Mix-%s", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 1, }, {"Phones-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 2, }, {"Mic%d", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 3, }, {"Analog%d", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 4, }, {"Analog%d", 6, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 5, 2}, {"Main-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 0, }, {"SPDIF%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 6, }, {"Padding%d", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY|MOTU_PA_PADDING, 7, }, }; PortGroupEntry PortGroups_8PRE[] = { {"Mix-%s", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 2, }, {"Phones-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 3, }, {"Analog%d", 8, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 0, }, {"Main-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 1, }, {"Padding%d", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY|MOTU_PA_PADDING, 4, }, {"ADAT%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 5, }, {"ADAT%d", 4, MOTU_PA_INOUT | MOTU_PA_RATE_2x|MOTU_PA_OPTICAL_ADAT, 6, }, }; PortGroupEntry PortGroups_828mk3[] = { {"Mic-%d", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_MK3_OPT_ANY, -1, }, {"Phones-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_ANY, }, {"Unknown-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_4x|MOTU_PA_MK3_OPT_ANY, }, {"Analog%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_MK3_OPT_ANY, }, {"Return-%d", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_MK3_OPT_ANY, }, {"MainOut-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_ANY, }, {"SPDIF%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_ANY, }, {"Unknown-%d", 2, MOTU_PA_OUT | MOTU_PA_RATE_4x|MOTU_PA_MK3_OPT_ANY, }, {"Reverb-%d", 2, MOTU_PA_IN | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_ANY, }, // // For input at 1x rates there are an additional 2 channel slots which // are yet to be identified. // {"UnknownA-%d", 2, MOTU_PA_IN | MOTU_PA_RATE_1x|MOTU_PA_MK3_OPT_ANY, }, // // Optical ports. Note: UnknownB is flagged as being always present at // 2x rates in the input stream. This is yet to be verified. In the // case of the old PortEntry definition UnknownB's equivalent was only // flagged as present if optical B was set to ADAT; this seems wrong on // reflection. // // The purpose of the Toslink-{A,B}-extra-* ports is not yet known, // beyond the observed fact that allowance must be made for them in the // packet format whenever the corresponding optical port is set to // Toslink mode. They may be padding, but for now connect them with // audio ports so users can experiment to see if they correspond to // anything interesting. // {"Toslink-A%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_A_TOSLINK|MOTU_PA_MK3_OPT_B_ANY, }, {"Toslink-A-extra-%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_A_TOSLINK|MOTU_PA_MK3_OPT_B_ANY, }, {"ADAT-A%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_1x|MOTU_PA_MK3_OPT_A_ADAT|MOTU_PA_MK3_OPT_B_ANY, }, {"ADAT-A%d", 4, MOTU_PA_INOUT | MOTU_PA_RATE_2x|MOTU_PA_MK3_OPT_A_ADAT|MOTU_PA_MK3_OPT_B_ANY, }, {"Toslink-B%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_TOSLINK, }, {"Toslink-B-extra-%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_TOSLINK, }, {"ADAT-B%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_1x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_ADAT, }, {"ADAT-B%d", 4, MOTU_PA_INOUT | MOTU_PA_RATE_2x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_ADAT, }, {"UnknownB-%d", 2, MOTU_PA_IN | MOTU_PA_RATE_2x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_ANY, }, }; PortGroupEntry PortGroups_ULTRALITEmk3[] = { {"Mix-%s", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 6, }, {"Main-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 0, }, {"Mic%d", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 1, }, {"Analog%d", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 2, }, {"Analog%d", 6, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 3, 2}, {"Phones-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 4, }, {"SPDIF%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 5, }, {"Reverb-%s", 2, MOTU_PA_IN | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 7, }, {"Padding%d", 4, MOTU_PA_IN | MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ANY|MOTU_PA_PADDING, 8, }, {"Pad out%d", 2, MOTU_PA_OUT | MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY|MOTU_PA_PADDING, 9, }, }; PortGroupEntry PortGroups_ULTRALITEmk3_hybrid[] = { {"Mix-%s", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 1, }, {"Phones-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 2, }, {"Mic%d", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 3, }, {"Analog%d", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 4, }, {"Analog%d", 6, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 5, 2, }, {"Main-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 0, }, {"SPDIF%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 6, }, {"Reverb%d", 2, MOTU_PA_IN | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 7, }, {"Unknown%d", 4, MOTU_PA_IN | MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ANY, 8, }, }; /* FIXME: as of 5 Aug 2010 this is still under development */ PortGroupEntry PortGroups_TRAVELERmk3[] = { {"Mix-%s", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_MK3_OPT_ANY, -1, }, {"Phones-%s",2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_MK3_OPT_ANY, }, {"Analog%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_MK3_OPT_ANY, }, {"AES/EBU%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_ANY, }, {"SPDIF%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_ANY, }, {"Reverb%d", 2, MOTU_PA_IN | MOTU_PA_RATE_1x|MOTU_PA_MK3_OPT_ANY, }, {"Unknown%d", 2, MOTU_PA_IN | MOTU_PA_RATE_1x|MOTU_PA_MK3_OPT_ANY, }, // // FIXME: optical port details still being determined. // // The purpose of the Toslink-{A,B}-extra-* ports is not yet known, // beyond the observed fact that allowance must be made for them in the // packet format whenever the corresponding optical port is set to // Toslink mode. They may be padding, but for now connect them with // audio ports so users can experiment to see if they correspond to // anything interesting. // {"Toslink-A%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_A_TOSLINK|MOTU_PA_MK3_OPT_B_ANY, }, {"Toslink-A-extra-%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_A_TOSLINK|MOTU_PA_MK3_OPT_B_ANY, }, {"ADAT-A%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_1x|MOTU_PA_MK3_OPT_A_ADAT|MOTU_PA_MK3_OPT_B_ANY, }, {"ADAT-A%d", 4, MOTU_PA_INOUT | MOTU_PA_RATE_2x|MOTU_PA_MK3_OPT_A_ADAT|MOTU_PA_MK3_OPT_B_ANY, }, {"Toslink-B%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_TOSLINK, }, {"Toslink-B-extra-%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_TOSLINK, }, {"ADAT-B%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_1x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_ADAT, }, {"ADAT-B%d", 4, MOTU_PA_INOUT | MOTU_PA_RATE_2x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_ADAT, }, {"UnknownB-%d", 2, MOTU_PA_IN | MOTU_PA_RATE_2x|MOTU_PA_MK3_OPT_A_TOSLINK|MOTU_PA_MK3_OPT_B_TOSLINK, }, }; /* FIXME: as of 8 Oct 2010 this is still under development. Presently this * layout is nothing more than an educated guess. The presence of the * Toslink-{A,B}-extra-* ports is even more of a guess, and is based on * the observation that they have so far proved necessary on the mk3 * devices which we have been able to explicitly verify. */ PortGroupEntry PortGroups_896mk3[] = { {"Mix-%s", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_MK3_OPT_ANY, -1, }, {"Phones-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_1x|MOTU_PA_MK3_OPT_ANY, }, {"Analog%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_ANY|MOTU_PA_MK3_OPT_ANY, }, {"MainOut-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_MK3_OPT_ANY, }, {"UnknownIn-%d", 4, MOTU_PA_IN | MOTU_PA_RATE_1x|MOTU_PA_MK3_OPT_ANY, }, {"AES/EBU%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_ANY, }, {"SPDIF%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_ANY, }, {"UnknownOut-%d", 2, MOTU_PA_OUT | MOTU_PA_RATE_1x|MOTU_PA_MK3_OPT_ANY, }, {"Toslink-A%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_A_TOSLINK|MOTU_PA_MK3_OPT_B_ANY, }, {"ADAT-A%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_1x|MOTU_PA_MK3_OPT_A_ADAT|MOTU_PA_MK3_OPT_B_ANY, }, {"Toslink-A-extra-%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_A_TOSLINK|MOTU_PA_MK3_OPT_B_ANY, }, {"ADAT-A%d", 4, MOTU_PA_INOUT | MOTU_PA_RATE_2x|MOTU_PA_MK3_OPT_A_ADAT|MOTU_PA_MK3_OPT_B_ANY, }, {"Toslink-B%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_TOSLINK, }, {"Toslink-B-extra-%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_TOSLINK, }, {"ADAT-B%d", 8, MOTU_PA_INOUT | MOTU_PA_RATE_1x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_ADAT, }, {"ADAT-B%d", 4, MOTU_PA_INOUT | MOTU_PA_RATE_2x|MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_ADAT, }, }; /* Believed correct as of 24 Feb 2014. Thanks to Tim Radvan for testing. */ PortGroupEntry PortGroups_4PRE[] = { {"Mix-%s", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 1, }, {"Phones-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 2, }, {"Mic/Line-%d", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 3, }, {"Mic/Guitar-%d", 2, MOTU_PA_IN | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 4, }, {"Analog%d", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 5, }, {"Main-%s", 2, MOTU_PA_OUT | MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 0, }, {"SPDIF%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 6, }, {"Extra-%d", 2, MOTU_PA_INOUT | MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 7, }, }; #define PORTGROUPS(__model) PortGroups_ ## __model, N_ELEMENTS(PortGroups_ ## __model) /* The order of DevicesProperty entries must match the numeric order of the * MOTU model enumeration (EMotuModel). */ const DevicePropertyEntry DevicesProperty[] = { // { PortGroups_map, N_ELEMENTS( PortGroups_map ), // Ports_map, N_ELEMENTS( Ports_map ), MaxSR, MixerDescrPtr, Mark3MixerDescrPtr }, { PORTGROUPS(828MKII), 96000, &Mixer_828Mk2, NULL, }, { PORTGROUPS(TRAVELER), 192000, &Mixer_Traveler, NULL, }, { PORTGROUPS(ULTRALITE), 96000, &Mixer_Ultralite, NULL, }, { PORTGROUPS(8PRE), 96000, &Mixer_8pre, NULL, }, { PORTGROUPS(828MKI), 48000, &Mixer_828Mk1, NULL, }, { PORTGROUPS(896HD), 192000, &Mixer_896HD, NULL, }, { PORTGROUPS(828mk3), 192000, }, { PORTGROUPS(ULTRALITEmk3), 192000, }, // Ultralite mk3 { PORTGROUPS(ULTRALITEmk3_hybrid), 192000, }, // Ultralite mk3 hybrid { PORTGROUPS(TRAVELERmk3), 192000, }, { PORTGROUPS(896mk3), 192000, }, // 896 Mk 3 { PORTGROUPS(4PRE), 96000, }, }; MotuDevice::MotuDevice( DeviceManager& d, ffado_smartptr( configRom )) : FFADODevice( d, configRom ) , m_motu_model( MOTU_MODEL_NONE ) , m_iso_recv_channel ( -1 ) , m_iso_send_channel ( -1 ) , m_rx_bandwidth ( -1 ) , m_tx_bandwidth ( -1 ) , m_rx_event_size ( 0 ) , m_tx_event_size ( 0 ) , m_receiveProcessor ( 0 ) , m_transmitProcessor ( 0 ) , m_MixerContainer ( NULL ) , m_ControlContainer ( NULL ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Motu::MotuDevice (NodeID %d)\n", getConfigRom().getNodeId() ); } MotuDevice::~MotuDevice() { delete m_receiveProcessor; delete m_transmitProcessor; // Free ieee1394 bus resources if they have been allocated if (m_iso_recv_channel>=0 && !get1394Service().freeIsoChannel(m_iso_recv_channel)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Could not free recv iso channel %d\n", m_iso_recv_channel); } if (m_iso_send_channel>=0 && !get1394Service().freeIsoChannel(m_iso_send_channel)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Could not free send iso channel %d\n", m_iso_send_channel); } destroyMixer(); } bool MotuDevice::probe( Util::Configuration& c, ConfigRom& configRom, bool generic) { if(generic) return false; unsigned int vendorId = configRom.getNodeVendorId(); unsigned int unitVersion = configRom.getUnitVersion(); unsigned int unitSpecifierId = configRom.getUnitSpecifierId(); for ( unsigned int i = 0; i < ( sizeof( supportedDeviceList )/sizeof( VendorModelEntry ) ); ++i ) { if ( ( supportedDeviceList[i].vendor_id == vendorId ) && ( supportedDeviceList[i].unit_version == unitVersion ) && ( supportedDeviceList[i].unit_specifier_id == unitSpecifierId ) ) { if (supportedDeviceList[i].model == MOTU_MODEL_NONE) { debugOutput( DEBUG_LEVEL_VERBOSE, "%s %s found but is not currently supported by FFADO\n", supportedDeviceList[i].vendor_name, supportedDeviceList[i].model_name); debugOutput( DEBUG_LEVEL_VERBOSE, " unitVersion=0x%08x\n", unitVersion); return false; } return true; } } return false; } FFADODevice * MotuDevice::createDevice(DeviceManager& d, ffado_smartptr( configRom )) { return new MotuDevice(d, configRom); } bool MotuDevice::discover() { unsigned int vendorId = getConfigRom().getNodeVendorId(); unsigned int unitVersion = getConfigRom().getUnitVersion(); unsigned int unitSpecifierId = getConfigRom().getUnitSpecifierId(); for ( unsigned int i = 0; i < ( sizeof( supportedDeviceList )/sizeof( VendorModelEntry ) ); ++i ) { if ( ( supportedDeviceList[i].vendor_id == vendorId ) && ( supportedDeviceList[i].unit_version == unitVersion ) && ( supportedDeviceList[i].unit_specifier_id == unitSpecifierId ) ) { m_model = &(supportedDeviceList[i]); m_motu_model=supportedDeviceList[i].model; } } if (m_model == NULL) { return false; } debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", m_model->vendor_name, m_model->model_name); if (m_motu_model == MOTU_MODEL_NONE) { debugOutput( DEBUG_LEVEL_VERBOSE, "This MOTU device is not currently supported by FFADO\n"); return false; } // The MOTU 8pre seems to power up in "converter" mode. To toggle it // into "interface" mode it is necessary to do a write to the clock // control register. Since setClockCtrlRegister() will only do a write // if something is explicitly set it isn't sufficient to do something like // setClockCtrlRegister(-1, MOTU_CLKSRC_UNCHANGED) // Instead, we request that the clock source be set to its present value; // effectively this preserves the interface's current clock settings. if (m_motu_model == MOTU_MODEL_8PRE) { setClockCtrlRegister(-1, getHwClockSource()); } // The MOTU 828mk1 device seems to power up without a valid clock source // configured (the relevant bits don't seem to map to anything sensible). // Deal with this if necessary. if (m_motu_model == MOTU_MODEL_828MkI) { signed int csrc = getHwClockSource(); if (csrc == MOTU_CLKSRC_NONE) csrc = MOTU_CLKSRC_INTERNAL; setClockCtrlRegister(-1, csrc); } if (!buildMixer()) { debugWarning("Could not build mixer\n"); } return true; } enum FFADODevice::eStreamingState MotuDevice::getStreamingState() { unsigned int val = ReadRegister(MOTU_REG_ISOCTRL); /* Streaming is active if either bit 22 (Motu->PC streaming * enable) or bit 30 (PC->Motu streaming enable) is set. */ debugOutput(DEBUG_LEVEL_VERBOSE, "MOTU_REG_ISOCTRL: %08x\n", val); if((val & 0x40400000) != 0) { return eSS_Both; } else if ((val & 0x40000000) != 0) { return eSS_Receiving; } else if ((val & 0x00400000) != 0) { return eSS_Sending; } else { return eSS_Idle; } } int MotuDevice::getSamplingFrequency( ) { /* * Retrieve the current sample rate from the MOTU device. */ quadlet_t q = 0; int rate = 0; unsigned int rate_base_mask, rate_base48k; unsigned int rate_mult_mask, rate_mult2, rate_mult4; if (m_motu_model == MOTU_MODEL_828MkI) { /* The original MOTU interfaces did things rather differently */ q = ReadRegister(MOTU_G1_REG_CONFIG); if ((q & MOTU_G1_RATE_MASK) == MOTU_G1_RATE_44100) rate = 44100; else rate = 48000; return rate; } /* The way the rate is managed is the same across G2 and G3 devices, * but the actual bits used in the clock control register is different. */ if (getDeviceGeneration() == MOTU_DEVICE_G2) { rate_base_mask = MOTU_RATE_BASE_MASK; rate_base48k = MOTU_RATE_BASE_48000; rate_mult_mask = MOTU_RATE_MULTIPLIER_MASK; rate_mult2 = MOTU_RATE_MULTIPLIER_2X; rate_mult4 = MOTU_RATE_MULTIPLIER_4X; } else { rate_base_mask = MOTU_G3_RATE_BASE_MASK; rate_base48k = MOTU_G3_RATE_BASE_48000; rate_mult_mask = MOTU_G3_RATE_MULTIPLIER_MASK; rate_mult2 = MOTU_G3_RATE_MULTIPLIER_2X; rate_mult4 = MOTU_G3_RATE_MULTIPLIER_4X; } q = ReadRegister(MOTU_REG_CLK_CTRL); if ((q & rate_base_mask) == rate_base48k) rate = 48000; else rate = 44100; if ((q & rate_mult_mask) == rate_mult4) rate *= 4; else if ((q & rate_mult_mask) == rate_mult2) rate *= 2; return rate; } int MotuDevice::getConfigurationId() { return 0; } unsigned int MotuDevice::getHwClockSource() { unsigned int reg; if (m_motu_model == MOTU_MODEL_828MkI) { reg = ReadRegister(MOTU_G1_REG_CONFIG); switch (reg & MOTU_G1_CLKSRC_MASK) { case MOTU_G1_CLKSRC_INTERNAL: return MOTU_CLKSRC_INTERNAL; case MOTU_G1_CLKSRC_ADAT_9PIN: return MOTU_CLKSRC_ADAT_9PIN; case MOTU_G1_CLKSRC_SPDIF: return MOTU_CLKSRC_SPDIF_TOSLINK; case MOTU_G1_CLKSRC_ADAT_OPTICAL: return MOTU_CLKSRC_ADAT_OPTICAL; } return MOTU_CLKSRC_NONE; } reg = ReadRegister(MOTU_REG_CLK_CTRL); if (getDeviceGeneration() == MOTU_DEVICE_G2) { switch (reg & MOTU_G2_CLKSRC_MASK) { case MOTU_G2_CLKSRC_INTERNAL: return MOTU_CLKSRC_INTERNAL; case MOTU_G2_CLKSRC_ADAT_OPTICAL: return MOTU_CLKSRC_ADAT_OPTICAL; case MOTU_G2_CLKSRC_SPDIF_TOSLINK: return MOTU_CLKSRC_SPDIF_TOSLINK; case MOTU_G2_CLKSRC_SMPTE: return MOTU_CLKSRC_SMPTE; case MOTU_G2_CLKSRC_WORDCLOCK: return MOTU_CLKSRC_WORDCLOCK; case MOTU_G2_CLKSRC_ADAT_9PIN: return MOTU_CLKSRC_ADAT_9PIN; case MOTU_G2_CLKSRC_AES_EBU: return MOTU_CLKSRC_AES_EBU; } } else { /* Handle G3 devices */ switch (reg & MOTU_G3_CLKSRC_MASK) { case MOTU_G3_CLKSRC_INTERNAL: return MOTU_CLKSRC_INTERNAL; case MOTU_G3_CLKSRC_SPDIF: return MOTU_CLKSRC_SPDIF_TOSLINK; case MOTU_G3_CLKSRC_SMPTE: return MOTU_CLKSRC_SMPTE; case MOTU_G3_CLKSRC_WORDCLOCK: return MOTU_CLKSRC_WORDCLOCK; case MOTU_G3_CLKSRC_OPTICAL_A: return MOTU_CLKSRC_OPTICAL_A; case MOTU_G3_CLKSRC_OPTICAL_B: return MOTU_CLKSRC_OPTICAL_B; } } return MOTU_CLKSRC_NONE; } #include bool MotuDevice::setClockCtrlRegister(signed int samplingFrequency, unsigned int clock_source) { /* * Set the MOTU device's samplerate and/or clock source via the clock * control register. If samplingFrequency <= 0 it remains unchanged. If * clock_source is MOTU_CLKSRC_UNCHANGED the clock source remains unchanged. */ const char *src_name; quadlet_t q, new_rate=0xffffffff; signed int i, supported=true, cancel_adat=false; quadlet_t reg; unsigned int rate_mask = 0; unsigned int old_clock_src = getHwClockSource(); signed int device_gen = getDeviceGeneration(); /* Don't touch anything if there's nothing to do */ if (samplingFrequency<=0 && clock_source==MOTU_CLKSRC_NONE) return true; if ( samplingFrequency > DevicesProperty[m_motu_model-1].MaxSampleRate ) return false; /* The original MOTU devices do things differently; they are much * simpler than the later interfaces. */ if (m_motu_model == MOTU_MODEL_828MkI) { reg = ReadRegister(MOTU_G1_REG_CONFIG); if (samplingFrequency > 0) { reg &= ~MOTU_G1_RATE_MASK; switch (samplingFrequency) { case 44100: reg |= MOTU_G1_RATE_44100; break; case 48000: reg |= MOTU_G1_RATE_48000; break; default: // Unsupported rate return false; } } if (clock_source != MOTU_CLKSRC_UNCHANGED) { switch (clock_source) { case MOTU_CLKSRC_INTERNAL: clock_source = MOTU_G1_CLKSRC_INTERNAL; break; case MOTU_CLKSRC_SPDIF_TOSLINK: clock_source = MOTU_G1_CLKSRC_SPDIF; break; case MOTU_CLKSRC_ADAT_9PIN: clock_source = MOTU_G1_CLKSRC_ADAT_9PIN; break; case MOTU_CLKSRC_ADAT_OPTICAL: clock_source = MOTU_G1_CLKSRC_ADAT_OPTICAL; break; default: // Unsupported clock source return false; } reg &= ~MOTU_G1_CLKSRC_MASK; reg |= clock_source; } if (WriteRegister(MOTU_G1_REG_CONFIG, reg) != 0) return false; return true; } /* The rest of this function deals with later generation devices */ reg = ReadRegister(MOTU_REG_CLK_CTRL); /* The method of controlling the sampling rate is the same for G2/G3 * devices but the actual bits used in the rate control register differ. */ if (device_gen == MOTU_DEVICE_G2) { rate_mask = MOTU_RATE_BASE_MASK | MOTU_RATE_MULTIPLIER_MASK; switch ( samplingFrequency ) { case -1: break; case 44100: new_rate = MOTU_RATE_BASE_44100 | MOTU_RATE_MULTIPLIER_1X; break; case 48000: new_rate = MOTU_RATE_BASE_48000 | MOTU_RATE_MULTIPLIER_1X; break; case 88200: new_rate = MOTU_RATE_BASE_44100 | MOTU_RATE_MULTIPLIER_2X; break; case 96000: new_rate = MOTU_RATE_BASE_48000 | MOTU_RATE_MULTIPLIER_2X; break; case 176400: new_rate = MOTU_RATE_BASE_44100 | MOTU_RATE_MULTIPLIER_4X; break; case 192000: new_rate = MOTU_RATE_BASE_48000 | MOTU_RATE_MULTIPLIER_4X; break; default: supported=false; } } else if (device_gen == MOTU_DEVICE_G3) { rate_mask = MOTU_G3_RATE_BASE_MASK | MOTU_G3_RATE_MULTIPLIER_MASK; switch ( samplingFrequency ) { case -1: break; case 44100: new_rate = MOTU_G3_RATE_BASE_44100 | MOTU_G3_RATE_MULTIPLIER_1X; break; case 48000: new_rate = MOTU_G3_RATE_BASE_48000 | MOTU_G3_RATE_MULTIPLIER_1X; break; case 88200: new_rate = MOTU_G3_RATE_BASE_44100 | MOTU_G3_RATE_MULTIPLIER_2X; break; case 96000: new_rate = MOTU_G3_RATE_BASE_48000 | MOTU_G3_RATE_MULTIPLIER_2X; break; case 176400: new_rate = MOTU_G3_RATE_BASE_44100 | MOTU_G3_RATE_MULTIPLIER_4X; break; case 192000: new_rate = MOTU_G3_RATE_BASE_48000 | MOTU_G3_RATE_MULTIPLIER_4X; break; default: supported=false; } } /* ADAT output is only possible for sample rates up to 96 kHz. For * anything higher, force the ADAT channels off. */ if (samplingFrequency > 96000) { cancel_adat = true; } // Sanity check the clock source if (clock_source>MOTU_CLKSRC_LAST && clock_source!=MOTU_CLKSRC_UNCHANGED) supported = false; // Update the clock control register. FIXME: while this is now rather // comprehensive there may still be a need to manipulate MOTU_REG_CLK_CTRL // a little more than we do. if (supported) { // If optical port must be disabled (because a 4x sample rate has // been selected) then do so before changing the sample rate. At // this stage it will be up to the user to re-enable the optical // port if the sample rate is set to a 1x or 2x rate later. if (cancel_adat) { setOpticalMode(MOTU_CTRL_DIR_INOUT, MOTU_OPTICAL_MODE_OFF, MOTU_OPTICAL_MODE_OFF); } // Set up new frequency if requested if (new_rate != 0xffffffff) { reg &= ~rate_mask; reg |= new_rate; } // Set up new clock source if required if (clock_source != MOTU_CLKSRC_UNCHANGED) { if (device_gen == MOTU_DEVICE_G2) { reg &= ~MOTU_G2_CLKSRC_MASK; switch (clock_source) { case MOTU_CLKSRC_INTERNAL: reg |= MOTU_G2_CLKSRC_INTERNAL; break; case MOTU_CLKSRC_ADAT_OPTICAL: reg |= MOTU_G2_CLKSRC_ADAT_OPTICAL; break; case MOTU_CLKSRC_SPDIF_TOSLINK: reg |= MOTU_G2_CLKSRC_SPDIF_TOSLINK; break; case MOTU_CLKSRC_SMPTE: reg |= MOTU_G2_CLKSRC_SMPTE; break; case MOTU_CLKSRC_WORDCLOCK: reg |= MOTU_G2_CLKSRC_WORDCLOCK; break; case MOTU_CLKSRC_ADAT_9PIN: reg |= MOTU_G2_CLKSRC_ADAT_9PIN; break; case MOTU_CLKSRC_AES_EBU: reg |= MOTU_G2_CLKSRC_AES_EBU; break; } } else { reg &= ~MOTU_G3_CLKSRC_MASK; switch (clock_source) { case MOTU_CLKSRC_INTERNAL: reg |= MOTU_G3_CLKSRC_INTERNAL; break; case MOTU_CLKSRC_SPDIF_TOSLINK: reg |= MOTU_G3_CLKSRC_SPDIF; break; case MOTU_CLKSRC_SMPTE: reg |= MOTU_G3_CLKSRC_SMPTE; break; case MOTU_CLKSRC_WORDCLOCK: reg |= MOTU_G3_CLKSRC_WORDCLOCK; break; case MOTU_CLKSRC_OPTICAL_A: reg |= MOTU_G3_CLKSRC_OPTICAL_A; break; case MOTU_CLKSRC_OPTICAL_B: reg |= MOTU_G3_CLKSRC_OPTICAL_B; break; } } } else { /* Use the device's current clock source to set the clock * source name registers, which must be done even if we aren't * changing the clock source. */ clock_source = old_clock_src; } // Bits 24-26 of MOTU_REG_CLK_CTRL behave a little differently // depending on the model. In addition, different bit patterns are // written depending on whether streaming is enabled, disabled or is // changing state. For now we go with the combination used when // streaming is enabled since it seems to work for the other states // as well. Since device muting can be effected by these bits, we // may utilise this in future during streaming startup to prevent // noises during stabilisation. // // For most models (possibly all except the Ultralite) all 3 bits // can be zero and audio is still output. // // For the Traveler, if bit 26 is set (as it is under other OSes), // bit 25 functions as a device mute bit: if set, audio is output // while if 0 the entire device is muted. If bit 26 is unset, // setting bit 25 doesn't appear to be detrimental. // // For the Ultralite, other OSes leave bit 26 unset. However, unlike // other devices bit 25 seems to function as a mute bit in this case. // // The function of bit 24 is currently unknown. Other OSes set it // for all devices so we will too. reg &= 0xf8ffffff; if (m_motu_model == MOTU_MODEL_TRAVELER) reg |= 0x04000000; reg |= 0x03000000; if (WriteRegister(MOTU_REG_CLK_CTRL, reg) == 0) { supported=true; } else { supported=false; } // For testing ultralite mk3 debugOutput(DEBUG_LEVEL_VERBOSE, "supported: %d\n", supported); { signed int cx = 0; while (cx<3000) { if (WriteRegister(MOTU_REG_CLKSRC_NAME0, 0x496e7465) == 0) break; usleep(1000); cx++; } if (cx<3000) debugOutput(DEBUG_LEVEL_VERBOSE, "guard write ok"); else debugOutput(DEBUG_LEVEL_VERBOSE, "guard write timeout"); } // 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. This appears to be the same for // both G2 and G3 devices. switch (clock_source) { case MOTU_CLKSRC_INTERNAL: src_name = "Internal "; break; case MOTU_CLKSRC_ADAT_OPTICAL: src_name = "ADAT Optical "; break; case MOTU_CLKSRC_SPDIF_TOSLINK: { unsigned int p0_mode; if (device_gen < MOTU_DEVICE_G3) { getOpticalMode(MOTU_DIR_IN, &p0_mode, NULL); } else p0_mode = MOTU_OPTICAL_MODE_OFF; if (p0_mode == MOTU_OPTICAL_MODE_TOSLINK) src_name = "TOSLink "; else src_name = "SPDIF "; break; } case MOTU_CLKSRC_SMPTE: src_name = "SMPTE "; break; case MOTU_CLKSRC_WORDCLOCK: src_name = "Word Clock In "; break; case MOTU_CLKSRC_ADAT_9PIN: src_name = "ADAT 9-pin "; break; case MOTU_CLKSRC_AES_EBU: src_name = "AES-EBU "; break; case MOTU_CLKSRC_OPTICAL_A: { unsigned int p0_mode; getOpticalMode(MOTU_DIR_IN, &p0_mode, NULL); if (p0_mode == MOTU_OPTICAL_MODE_TOSLINK) src_name = "Toslink-A "; else src_name = "ADAT-A Optical "; break; } case MOTU_CLKSRC_OPTICAL_B: { unsigned int p1_mode; getOpticalMode(MOTU_DIR_IN, NULL, &p1_mode); if (p1_mode == MOTU_OPTICAL_MODE_TOSLINK) src_name = "Toslink-B "; else src_name = "ADAT-B Optical "; break; } default: src_name = "Unknown "; } for (i=0; i<16; i+=4) { q = (src_name[i]<<24) | (src_name[i+1]<<16) | (src_name[i+2]<<8) | src_name[i+3]; WriteRegister(MOTU_REG_CLKSRC_NAME0+i, q); } } return supported; } bool MotuDevice::setSamplingFrequency( int samplingFrequency ) { /* * Set the MOTU device's samplerate. */ return setClockCtrlRegister(samplingFrequency, MOTU_CLKSRC_UNCHANGED); } std::vector MotuDevice::getSupportedSamplingFrequencies() { std::vector frequencies; signed int max_freq = DevicesProperty[m_motu_model-1].MaxSampleRate; /* All MOTUs support 1x rates. All others must be conditional. */ frequencies.push_back(44100); frequencies.push_back(48000); if (88200 <= max_freq) frequencies.push_back(88200); if (96000 <= max_freq) frequencies.push_back(96000); if (176400 <= max_freq) frequencies.push_back(176400); if (192000 <= max_freq) frequencies.push_back(192000); return frequencies; } FFADODevice::ClockSource MotuDevice::clockIdToClockSource(unsigned int id) { ClockSource s; signed int device_gen = getDeviceGeneration(); s.id = id; // Assume a clock source is valid/active unless otherwise overridden. s.valid = true; s.locked = true; s.active = true; switch (id) { case MOTU_CLKSRC_INTERNAL: s.type = eCT_Internal; s.description = "Internal sync"; break; case MOTU_CLKSRC_ADAT_OPTICAL: s.type = eCT_ADAT; s.description = "ADAT optical"; s.valid = s.active = s.locked = (device_gen!=MOTU_DEVICE_G1); break; case MOTU_CLKSRC_SPDIF_TOSLINK: s.type = eCT_SPDIF; if (device_gen < MOTU_DEVICE_G3) s.description = "SPDIF/Toslink"; else s.description = "SPDIF"; break; case MOTU_CLKSRC_SMPTE: s.type = eCT_SMPTE; s.description = "SMPTE"; // Since we don't currently know how to deal with SMPTE on these // devices make sure the SMPTE clock source is disabled. s.valid = false; s.active = false; s.locked = false; break; case MOTU_CLKSRC_WORDCLOCK: s.type = eCT_WordClock; s.description = "Wordclock"; s.valid = s.active = s.locked = (device_gen!=MOTU_DEVICE_G1); break; case MOTU_CLKSRC_ADAT_9PIN: s.type = eCT_ADAT; s.description = "ADAT 9-pin"; break; case MOTU_CLKSRC_AES_EBU: s.type = eCT_AES; s.description = "AES/EBU"; s.valid = s.active = s.locked = (device_gen!=MOTU_DEVICE_G1); break; case MOTU_CLKSRC_OPTICAL_A: s.type = eCT_ADAT; s.description = "ADAT/Toslink port A"; break; case MOTU_CLKSRC_OPTICAL_B: s.type = eCT_ADAT; s.description = "ADAT/Toslink port B"; break; default: s.type = eCT_Invalid; } s.slipping = false; return s; } FFADODevice::ClockSourceVector MotuDevice::getSupportedClockSources() { FFADODevice::ClockSourceVector r; ClockSource s; signed int device_gen = getDeviceGeneration(); /* Form a list of clocks supported by MOTU interfaces */ /* All interfaces support an internal clock */ s = clockIdToClockSource(MOTU_CLKSRC_INTERNAL); r.push_back(s); if (device_gen==MOTU_DEVICE_G2 || device_gen==MOTU_DEVICE_G1) { s = clockIdToClockSource(MOTU_CLKSRC_ADAT_OPTICAL); r.push_back(s); } s = clockIdToClockSource(MOTU_CLKSRC_SPDIF_TOSLINK); r.push_back(s); s = clockIdToClockSource(MOTU_CLKSRC_SMPTE); r.push_back(s); /* The 828mk1 didn't have a wordclock sync option */ if (device_gen != MOTU_DEVICE_G1) { s = clockIdToClockSource(MOTU_CLKSRC_WORDCLOCK); r.push_back(s); } /* The 9-pin ADAT sync was only present on selected G2 * devices. The 828mk1 also offered this. */ if (m_motu_model==MOTU_MODEL_828mkII || m_motu_model==MOTU_MODEL_TRAVELER || m_motu_model==MOTU_MODEL_896HD || m_motu_model==MOTU_MODEL_828MkI) { s = clockIdToClockSource(MOTU_CLKSRC_ADAT_9PIN); r.push_back(s); } /* AES/EBU is present on the G2/G3 Travelers and 896HDs */ if (m_motu_model==MOTU_MODEL_TRAVELER || m_motu_model==MOTU_MODEL_TRAVELERmk3 || m_motu_model==MOTU_MODEL_896HD || m_motu_model==MOTU_MODEL_896mk3) { s = clockIdToClockSource(MOTU_CLKSRC_AES_EBU); r.push_back(s); } /* Dual-port ADAT is a feature of the G3 devices, and then only some */ if (m_motu_model==MOTU_MODEL_828mk3 || m_motu_model==MOTU_MODEL_TRAVELERmk3 || m_motu_model==MOTU_MODEL_896mk3) { s = clockIdToClockSource(MOTU_CLKSRC_OPTICAL_A); r.push_back(s); s = clockIdToClockSource(MOTU_CLKSRC_OPTICAL_B); r.push_back(s); } return r; } bool MotuDevice::setActiveClockSource(ClockSource s) { debugOutput(DEBUG_LEVEL_VERBOSE, "setting clock source to id: %d\n",s.id); // FIXME: this could do with some error checking return setClockCtrlRegister(-1, s.id); } FFADODevice::ClockSource MotuDevice::getActiveClockSource() { ClockSource s; quadlet_t clock_id = getHwClockSource(); s = clockIdToClockSource(clock_id); s.active = true; return s; } bool MotuDevice::lock() { return true; } bool MotuDevice::unlock() { return true; } void MotuDevice::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "%s %s at node %d\n", m_model->vendor_name, m_model->model_name, getNodeId()); } bool MotuDevice::prepare() { int samp_freq = getSamplingFrequency(); unsigned int optical_in_mode_a, optical_out_mode_a; unsigned int optical_in_mode_b, optical_out_mode_b; unsigned int event_size_in; unsigned int event_size_out; debugOutput(DEBUG_LEVEL_NORMAL, "Preparing MotuDevice...\n" ); /* The 828mk1 powers up without the optical mode register fields set * to anything in particular. For this interface, hardcode a default * optical mode if it appears that the interface is uninitialised. * On powerup, the unknown-2 register is 0xffffffff and this is reset * by software to 0; this provides a convenient test for the status * of the interface. */ if (m_motu_model==MOTU_MODEL_828MkI && ReadRegister(MOTU_G1_REG_UNKNOWN_2)!=0) { optical_in_mode_a = optical_out_mode_a = MOTU_OPTICAL_MODE_OFF; optical_in_mode_b = optical_out_mode_b = MOTU_OPTICAL_MODE_NONE; } else { getOpticalMode(MOTU_DIR_IN, &optical_in_mode_a, &optical_in_mode_b); getOpticalMode(MOTU_DIR_OUT, &optical_out_mode_a, &optical_out_mode_b); } // Initialise port groups and determine the event sizes based on the // current device mode and sample rate. initDirPortGroups(Streaming::Port::E_Capture, samp_freq, optical_in_mode_a, optical_in_mode_b); initDirPortGroups(Streaming::Port::E_Playback, samp_freq, optical_out_mode_a, optical_out_mode_b); event_size_in = getEventSize(MOTU_DIR_IN); event_size_out= getEventSize(MOTU_DIR_OUT); // Explicitly set the optical mode, primarily to ensure that the // MOTU_REG_OPTICAL_CTRL register is initialised. We need to do this to // because some interfaces (the Ultralite for example) appear to power // up without this set to anything sensible. In this case, writes to // MOTU_REG_ISOCTRL fail more often than not, which is bad. setOpticalMode(MOTU_DIR_IN, optical_in_mode_a, optical_in_mode_b); setOpticalMode(MOTU_DIR_OUT, optical_out_mode_a, optical_out_mode_b); // The original 828 (aka 828mk1) was seen to write to these two // registers as part of its startup routine. It's not entirely clear // what these registers control. The inclusion of the local node ID is // an educated guess on experiments with bus topology when using other // systems. if (m_motu_model == MOTU_MODEL_828MkI) { WriteRegister(MOTU_G1_REG_UNKNOWN_1, 0xffc00001 | ((get1394Service().getLocalNodeId()&0x3f)<<16)); WriteRegister(MOTU_G1_REG_UNKNOWN_2, 0x00000000); } // 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_send_channel < 0) m_iso_send_channel = get1394Service().allocateIsoChannelGeneric(m_tx_bandwidth); if (m_iso_recv_channel < 0) m_iso_recv_channel = get1394Service().allocateIsoChannelGeneric(m_rx_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; } // get the device specific and/or global SP configuration Util::Configuration &config = getDeviceManager().getConfiguration(); // base value is the config.h value float recv_sp_dll_bw = STREAMPROCESSOR_DLL_BW_HZ; float xmit_sp_dll_bw = STREAMPROCESSOR_DLL_BW_HZ; // we can override that globally config.getValueForSetting("streaming.spm.recv_sp_dll_bw", recv_sp_dll_bw); config.getValueForSetting("streaming.spm.xmit_sp_dll_bw", xmit_sp_dll_bw); // or override in the device section config.getValueForDeviceSetting(getConfigRom().getNodeVendorId(), getConfigRom().getModelId(), "recv_sp_dll_bw", recv_sp_dll_bw); config.getValueForDeviceSetting(getConfigRom().getNodeVendorId(), getConfigRom().getModelId(), "xmit_sp_dll_bw", xmit_sp_dll_bw); m_receiveProcessor=new Streaming::MotuReceiveStreamProcessor(*this, event_size_in); m_receiveProcessor->setVerboseLevel(getDebugLevel()); // 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; } if(!m_receiveProcessor->setDllBandwidth(recv_sp_dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete m_receiveProcessor; m_receiveProcessor = NULL; return false; } // 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, defaulting to 'dev?'\n"); } // Add audio capture ports if (!addDirPortGroups(Streaming::Port::E_Capture, samp_freq, optical_in_mode_a, optical_in_mode_b)) { return false; } // Add MIDI port. Every MOTU interface except the original 828 has // exactly one MIDI input port, with each MIDI byte sent using a 3 byte // sequence starting at byte 4 of the event data. if (m_motu_model != MOTU_MODEL_828MkI) { asprintf(&buff,"%s_cap_MIDI0",id.c_str()); p = new Streaming::MotuMidiPort(*m_receiveProcessor, buff, Streaming::Port::E_Capture, 4); if (!p) { debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n", buff); } free(buff); } // example of adding an control port: // asprintf(&buff,"%s_cap_%s",id.c_str(),"myportnamehere"); // p=new Streaming::MotuControlPort( // buff, // Streaming::Port::E_Capture, // 0 // you can add all other port specific stuff you // // need to pass by extending MotuXXXPort and MotuPortInfo // ); // free(buff); // // if (!p) { // debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n",buff); // } else { // // if (!m_receiveProcessor->addPort(p)) { // debugWarning("Could not register port with stream processor\n"); // return false; // } else { // debugOutput(DEBUG_LEVEL_VERBOSE, "Added port %s\n",buff); // } // } // Do the same for the transmit processor m_transmitProcessor=new Streaming::MotuTransmitStreamProcessor(*this, event_size_out); m_transmitProcessor->setVerboseLevel(getDebugLevel()); if(!m_transmitProcessor->init()) { debugFatal("Could not initialize transmit processor!\n"); return false; } if(!m_transmitProcessor->setDllBandwidth(xmit_sp_dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete m_transmitProcessor; m_transmitProcessor = NULL; return false; } // Now we add ports to the processor debugOutput(DEBUG_LEVEL_VERBOSE,"Adding ports to transmit processor\n"); // Add audio playback ports if (!addDirPortGroups(Streaming::Port::E_Playback, samp_freq, optical_out_mode_a, optical_out_mode_b)) { return false; } // Add MIDI port. Every MOTU interface except the original 828 has // exactly one MIDI input port, with each MIDI byte sent using a 3 byte // sequence starting at byte 4 of the event data. if (m_motu_model != MOTU_MODEL_828MkI) { asprintf(&buff,"%s_pbk_MIDI0",id.c_str()); p = new Streaming::MotuMidiPort(*m_transmitProcessor, buff, Streaming::Port::E_Playback, 4); if (!p) { debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n", buff); } free(buff); } // example of adding an control port: // asprintf(&buff,"%s_pbk_%s",id.c_str(),"myportnamehere"); // // p=new Streaming::MotuControlPort( // buff, // Streaming::Port::E_Playback, // 0 // you can add all other port specific stuff you // // need to pass by extending MotuXXXPort and MotuPortInfo // ); // free(buff); // // if (!p) { // debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n",buff); // } else { // if (!m_transmitProcessor->addPort(p)) { // debugWarning("Could not register port with stream processor\n"); // return false; // } else { // debugOutput(DEBUG_LEVEL_VERBOSE, "Added port %s\n",buff); // } // } return true; } int MotuDevice::getStreamCount() { return 2; // one receive, one transmit } Streaming::StreamProcessor * MotuDevice::getStreamProcessorByIndex(int i) { switch (i) { case 0: return m_receiveProcessor; case 1: return m_transmitProcessor; default: return NULL; } return 0; } bool MotuDevice::startStreamByIndex(int i) { quadlet_t isoctrl = ReadRegister(MOTU_REG_ISOCTRL); if (m_motu_model == MOTU_MODEL_828MkI) { // The 828MkI device does this differently. In particular it does // not appear possible to enable tx and rx separately since they // share a global enable (MOTU_G1_C1_ISO_ENABLE). Therefore we // enable both when the 0th index is requested and ignore any // request for index 1. Also note that on the G1 devices, // MOTU_REG_ISOCTRL and MOTU_G1_REG_CONFIG are one and the same. quadlet_t config2_reg = ReadRegister(MOTU_G1_REG_CONFIG_2); if (i == 1) return true; m_receiveProcessor->setChannel(m_iso_recv_channel); m_transmitProcessor->setChannel(m_iso_send_channel); /* Start with a setting of the config2 register. The purpose of * doing this is unclear, but it's done by other drivers so we * should too, at least until we have something working. */ WriteRegister(MOTU_G1_REG_CONFIG_2, config2_reg); /* Send the iso details to the control register. Note that as for * other MOTU devices bit 24 enables changes to the MOTU's iso tx * settings while bit 31 enables iso rx changes. */ debugOutput(DEBUG_LEVEL_VERBOSE, "MOTU g1: read isoctl: %x\n", isoctrl); debugOutput(DEBUG_LEVEL_VERBOSE, "MOTU g1: read config2: %x\n", config2_reg); isoctrl &= ~MOTU_G1_C1_ISO_INFO_MASK; isoctrl |= (MOTU_G1_C1_ISO_TX_WREN | MOTU_G1_C1_ISO_RX_WREN); isoctrl |= (m_iso_recv_channel << MOTU_G1_C1_ISO_TX_CH_BIT0); isoctrl |= (m_iso_send_channel << MOTU_G1_C1_ISO_RX_CH_BIT0); isoctrl |= (MOTU_G1_C1_ISO_TX_ACTIVE | MOTU_G1_C1_ISO_RX_ACTIVE); isoctrl |= (MOTU_G1_IO_ENABLE_0); WriteRegister(MOTU_REG_ISOCTRL, isoctrl); debugOutput(DEBUG_LEVEL_VERBOSE, "MOTU g1: isoctrl 1: %08x\n", isoctrl); /* Streaming should be started with the conclusion of the above * register writes. There's one final bit that's set afterwards * by other systems, so we'll do the same until its demonstrated * that this write can be merged with the previous one without ill * effects. */ isoctrl &= ~MOTU_G1_C1_ISO_INFO_MASK; isoctrl |= MOTU_G1_C1_ISO_ENABLE; WriteRegister(MOTU_REG_ISOCTRL, isoctrl); debugOutput(DEBUG_LEVEL_VERBOSE, "MOTU g1: isoctrl 2: %08x\n", isoctrl); return true; } // 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 (which correspond to our receive channel). Turn bit 24 // on to enable changes to the MOTU's iso transmit settings when the // iso control register is written. Bit 23 enables iso transmit // from the MOTU. isoctrl &= 0xff00ffff; isoctrl |= (m_iso_recv_channel << 16); isoctrl |= 0x00c00000; WriteRegister(MOTU_REG_ISOCTRL, isoctrl); break; case 1: // TODO: do the stuff that is nescessary to make the device // transmit a stream // Set the streamprocessor channel to the one obtained by // the connection management m_transmitProcessor->setChannel(m_iso_send_channel); // Mask out current receive settings of the MOTU and replace with // new ones (which correspond to our transmit channel). Turn bit 31 // on to enable changes to the MOTU's iso receive settings when the // iso control register is written. Bit 30 enables iso receive by // the MOTU. isoctrl &= 0x00ffffff; isoctrl |= (m_iso_send_channel << 24); isoctrl |= 0xc0000000; WriteRegister(MOTU_REG_ISOCTRL, isoctrl); break; default: // Invalid stream index return false; } return true; } bool MotuDevice::stopStreamByIndex(int i) { quadlet_t isoctrl = ReadRegister(MOTU_REG_ISOCTRL); // Shut down streaming. Essentially the opposite of the start function. if (m_motu_model == MOTU_MODEL_828MkI) { quadlet_t reg = isoctrl; // The 828MkI device does this differently. In particular it does // not appear possible to disable tx and rx separately since they // share a global enable (MOTU_G1_C1_ISO_ENABLE). Therefore we // disable both when the 0th index is requested and ignore any // request for index 1. if (i == 1) return true; /* First disable the streaming */ reg &= ~MOTU_G1_C1_ISO_INFO_MASK; reg &= ~MOTU_G1_C1_ISO_ENABLE; WriteRegister(MOTU_REG_ISOCTRL, reg); /* Next, turn off individual stream enable flags. As for the stream * start case, this is separated from the first write because that's * what other systems do. */ reg |= (isoctrl & MOTU_G1_C1_ISO_INFO_MASK); reg &= ~(MOTU_G1_C1_ISO_TX_ACTIVE | MOTU_G1_C1_ISO_RX_ACTIVE); WriteRegister(MOTU_REG_ISOCTRL, reg); return true; } // NOTE: this assumes that you have two streams switch (i) { case 0: // Turn bit 22 off to disable iso send by the MOTU. Turn // bit 23 on to enable changes to the MOTU's iso transmit // settings when the iso control register is written. isoctrl &= 0xffbfffff; isoctrl |= 0x00800000; WriteRegister(MOTU_REG_ISOCTRL, isoctrl); break; case 1: // Turn bit 30 off to disable iso receive by the MOTU. Turn // bit 31 on to enable changes to the MOTU's iso receive // settings when the iso control register is written. isoctrl &= 0xbfffffff; isoctrl |= 0x80000000; WriteRegister(MOTU_REG_ISOCTRL, isoctrl); break; default: // Invalid stream index return false; } return true; } signed int MotuDevice::getIsoRecvChannel(void) { return m_iso_recv_channel; } signed int MotuDevice::getIsoSendChannel(void) { return m_iso_send_channel; } signed int MotuDevice::getDeviceGeneration(void) { if (m_motu_model == MOTU_MODEL_828MkI) return MOTU_DEVICE_G1; if (m_motu_model==MOTU_MODEL_828mk3 || m_motu_model==MOTU_MODEL_ULTRALITEmk3 || m_motu_model==MOTU_MODEL_ULTRALITEmk3_HYB || m_motu_model==MOTU_MODEL_TRAVELERmk3 || m_motu_model==MOTU_MODEL_896mk3) return MOTU_DEVICE_G3; return MOTU_DEVICE_G2; } unsigned int MotuDevice::getOpticalMode(unsigned int dir, unsigned int *port_a_mode, unsigned int *port_b_mode) { // Only the "Mark 3" (aka G3) MOTU devices had more than one optical // port. Therefore the "port_b_mode" parameter is unused by all // devices other than the Mark 3 devices. // // If a mode parameter pointer is NULL it will not be returned. unsigned int reg, reg2; unsigned int mask, shift; if (port_b_mode != NULL) *port_b_mode = MOTU_OPTICAL_MODE_NONE; if (getDeviceGeneration()!=MOTU_DEVICE_G3 && port_a_mode==NULL) return 0; if (m_motu_model == MOTU_MODEL_828MkI) { // The early devices used a different register layout. unsigned int mask2; reg = ReadRegister(MOTU_G1_REG_CONFIG); reg2 = ReadRegister(MOTU_G1_REG_CONFIG_2); mask = (dir==MOTU_DIR_IN)?MOTU_G1_C1_OPT_TOSLINK_IN:MOTU_G1_C1_OPT_TOSLINK_OUT; mask2 = (dir==MOTU_DIR_IN)?MOTU_G1_C2_OPT_nADAT_IN:MOTU_G1_C2_OPT_nADAT_OUT; if ((reg & mask) && (reg2 & mask2)) { /* Toslink bit set, nADAT bit set -> Toslink mode */ *port_a_mode = MOTU_OPTICAL_MODE_TOSLINK; } else if ((reg & mask)==0 && (reg2 & mask2)==0) { /* Toslink bit clear, nADAT bit clear -> ADAT mode */ *port_a_mode = MOTU_OPTICAL_MODE_ADAT; } else { /* All other combinations are unexpected except toslink clear/ * nADAT set, so just assume the optical port is off if we get * here. */ *port_a_mode = MOTU_OPTICAL_MODE_OFF; } return 0; } if (getDeviceGeneration() == MOTU_DEVICE_G3) { unsigned int enable, toslink; /* The Ultralite Mk3s don't have any optical ports. All others have 2. */ if (m_motu_model==MOTU_MODEL_ULTRALITEmk3 || m_motu_model==MOTU_MODEL_ULTRALITEmk3_HYB) { if (port_a_mode != NULL) *port_a_mode = MOTU_OPTICAL_MODE_NONE; if (port_b_mode != NULL) *port_b_mode = MOTU_OPTICAL_MODE_NONE; return 0; } reg = ReadRegister(MOTU_G3_REG_OPTICAL_CTRL); debugOutput(DEBUG_LEVEL_VERBOSE, "mark3 optical control register = 0x%08x\n", reg); if (port_a_mode != NULL) { enable = (dir==MOTU_DIR_IN)?MOTU_G3_OPT_A_IN_ENABLE:MOTU_G3_OPT_A_OUT_ENABLE; toslink = (dir==MOTU_DIR_IN)?MOTU_G3_OPT_A_IN_TOSLINK:MOTU_G3_OPT_A_OUT_TOSLINK; if ((reg & enable) == 0) *port_a_mode = MOTU_OPTICAL_MODE_OFF; else if ((reg & toslink) != 0) *port_a_mode = MOTU_OPTICAL_MODE_TOSLINK; else *port_a_mode = MOTU_OPTICAL_MODE_ADAT; } if (port_b_mode != NULL) { enable = (dir==MOTU_DIR_IN)?MOTU_G3_OPT_B_IN_ENABLE:MOTU_G3_OPT_B_OUT_ENABLE; toslink = (dir==MOTU_DIR_IN)?MOTU_G3_OPT_B_IN_TOSLINK:MOTU_G3_OPT_B_OUT_TOSLINK; if ((reg & enable) == 0) *port_b_mode = MOTU_OPTICAL_MODE_OFF; else if ((reg & toslink) != 0) *port_b_mode = MOTU_OPTICAL_MODE_TOSLINK; else *port_b_mode = MOTU_OPTICAL_MODE_ADAT; } return 0; } reg = ReadRegister(MOTU_REG_ROUTE_PORT_CONF); mask = (dir==MOTU_DIR_IN)?MOTU_G2_OPTICAL_IN_MODE_MASK:MOTU_G2_OPTICAL_OUT_MODE_MASK; shift = (dir==MOTU_DIR_IN)?MOTU_G2_OPTICAL_IN_MODE_BIT0:MOTU_G2_OPTICAL_OUT_MODE_BIT0; switch ((reg & mask) >> shift) { case MOTU_G2_OPTICAL_MODE_OFF: *port_a_mode = MOTU_OPTICAL_MODE_OFF; break; case MOTU_G2_OPTICAL_MODE_ADAT: *port_a_mode = MOTU_OPTICAL_MODE_ADAT; break; case MOTU_G2_OPTICAL_MODE_TOSLINK: *port_a_mode = MOTU_OPTICAL_MODE_TOSLINK; break; } return 0; } signed int MotuDevice::setOpticalMode(unsigned int dir, unsigned int port_a_mode, unsigned int port_b_mode) { // Only the "Mark 3" (aka G3) MOTU devices had more than one optical port. // Therefore the "port B" mode is ignored for all devices other than // the Mark 3 devices. unsigned int reg, g2mode; unsigned int opt_ctrl = 0x0000002; /* THe 896HD doesn't have an SPDIF/TOSLINK optical mode, so don't try to * set it */ if (m_motu_model==MOTU_MODEL_896HD && port_a_mode==MOTU_OPTICAL_MODE_TOSLINK) return -1; if (getDeviceGeneration()!=MOTU_DEVICE_G3 && port_a_mode==MOTU_OPTICAL_MODE_KEEP) return 0; if (m_motu_model == MOTU_MODEL_828MkI) { // The earlier MOTUs handle this differently. unsigned int g1_conf1_ref, g1_conf2_ref; unsigned int g1_conf1, g1_conf2; unsigned int toslink, n_adat; signed int err = 0; g1_conf1 = ReadRegister(MOTU_G1_REG_CONFIG); g1_conf2 = ReadRegister(MOTU_G1_REG_CONFIG_2); toslink = (dir==MOTU_DIR_IN)?MOTU_G1_C1_OPT_TOSLINK_IN:MOTU_G1_C1_OPT_TOSLINK_OUT; n_adat = (dir==MOTU_DIR_IN)?MOTU_G1_C2_OPT_nADAT_IN:MOTU_G1_C2_OPT_nADAT_OUT; // Don't send ISO information g1_conf1 &= ~MOTU_G1_C1_ISO_INFO_MASK; // This bit seems to always be set g1_conf1 |= (MOTU_G1_IO_ENABLE_0); /* This bit seems to always be set when this register is set. * It may be a write enable bit. */ g1_conf2 |= MOTU_G1_C2_OPT_nADAT_WREN; g1_conf1_ref = g1_conf1; g1_conf2_ref = g1_conf2; /* Set registers as needed by the requested mode */ if (port_a_mode == MOTU_OPTICAL_MODE_TOSLINK) { g1_conf1 |= toslink; } else { g1_conf1 &= ~toslink; } if (port_a_mode == MOTU_OPTICAL_MODE_ADAT) { g1_conf2 &= ~n_adat; } else { g1_conf2 |= n_adat; } // Under other systems, MOTU_G1_REG_CONFIG is always written // first, but only if its value has been changed. Similarly, // MOTU_G1_REG_CONFIG_2 is only written if its value has been // altered. if (!err && g1_conf1!=g1_conf1_ref) err = WriteRegister(MOTU_G1_REG_CONFIG, g1_conf1) != 0; if (!err && g1_conf2!=g1_conf2_ref) err = WriteRegister(MOTU_G1_REG_CONFIG_2, g1_conf2) != 0; if (err) return -1; return 0; } /* The G3 devices are also quite a bit different to the G2 units */ if (getDeviceGeneration() == MOTU_DEVICE_G3) { unsigned int mask, enable, toslink; reg = ReadRegister(MOTU_G3_REG_OPTICAL_CTRL); if (port_a_mode != MOTU_OPTICAL_MODE_KEEP) { mask = enable = toslink = 0; if (dir & MOTU_DIR_IN) { mask |= MOTU_G3_OPT_A_IN_MASK; enable |= MOTU_G3_OPT_A_IN_ENABLE; toslink |= MOTU_G3_OPT_A_IN_TOSLINK; } if (dir & MOTU_DIR_OUT) { mask |= MOTU_G3_OPT_A_OUT_MASK; enable |= MOTU_G3_OPT_A_OUT_ENABLE; toslink |= MOTU_G3_OPT_A_OUT_TOSLINK; } reg = (reg & ~mask) | enable; switch (port_a_mode) { case MOTU_OPTICAL_MODE_OFF: reg &= ~enable; break; case MOTU_OPTICAL_MODE_TOSLINK: reg |= toslink; break; } } if (port_b_mode != MOTU_OPTICAL_MODE_KEEP) { mask = enable = toslink = 0; if (dir & MOTU_DIR_IN) { mask |= MOTU_G3_OPT_B_IN_MASK; enable |= MOTU_G3_OPT_B_IN_ENABLE; toslink |= MOTU_G3_OPT_B_IN_TOSLINK; } if (dir & MOTU_DIR_OUT) { mask |= MOTU_G3_OPT_B_OUT_MASK; enable |= MOTU_G3_OPT_B_OUT_ENABLE; toslink |= MOTU_G3_OPT_B_OUT_TOSLINK; } reg = (reg & ~mask) | enable; switch (port_a_mode) { case MOTU_OPTICAL_MODE_OFF: reg &= ~enable; break; case MOTU_OPTICAL_MODE_TOSLINK: reg |= toslink; break; } reg = (reg & ~mask) | enable; switch (port_b_mode) { case MOTU_OPTICAL_MODE_OFF: reg &= ~enable; break; case MOTU_OPTICAL_MODE_TOSLINK: reg |= toslink; break; } } return WriteRegister(MOTU_G3_REG_OPTICAL_CTRL, reg); } reg = ReadRegister(MOTU_REG_ROUTE_PORT_CONF); // Map from user mode to values sent to the device registers. g2mode = 0; switch (port_a_mode) { case MOTU_OPTICAL_MODE_OFF: g2mode = MOTU_G2_OPTICAL_MODE_OFF; break; case MOTU_OPTICAL_MODE_ADAT: g2mode = MOTU_G2_OPTICAL_MODE_ADAT; break; case MOTU_OPTICAL_MODE_TOSLINK: g2mode = MOTU_G2_OPTICAL_MODE_TOSLINK; break; } // Set up the optical control register value according to the current // optical port modes. At this stage it's not completely understood // what the "Optical control" register does, so the values it's set to // are more or less "magic" numbers. if ((reg & MOTU_G2_OPTICAL_IN_MODE_MASK) != (MOTU_G2_OPTICAL_MODE_ADAT<n_portgroup_entries; if (n_groups <= 0) return true; /* Port data starts at offset 10 on most models, and 4 on the 828mk1 */ if (m_motu_model == MOTU_MODEL_828MkI) pkt_ofs = 4; else pkt_ofs = 10; if ( sample_rate > 96000 ) flags |= MOTU_PA_RATE_4x; else if ( sample_rate > 48000 ) flags |= MOTU_PA_RATE_2x; else flags |= MOTU_PA_RATE_1x; switch (optical_a_mode) { case MOTU_OPTICAL_MODE_NONE: flags |= MOTU_PA_OPTICAL_ANY; break; case MOTU_OPTICAL_MODE_OFF: flags |= MOTU_PA_OPTICAL_OFF; break; case MOTU_OPTICAL_MODE_ADAT: flags |= MOTU_PA_OPTICAL_ADAT; break; case MOTU_OPTICAL_MODE_TOSLINK: flags |= MOTU_PA_OPTICAL_TOSLINK; break; } switch (optical_b_mode) { case MOTU_OPTICAL_MODE_NONE: flags |= MOTU_PA_MK3_OPT_B_ANY; break; case MOTU_OPTICAL_MODE_OFF: flags |= MOTU_PA_MK3_OPT_B_OFF; break; case MOTU_OPTICAL_MODE_ADAT: flags |= MOTU_PA_MK3_OPT_B_ADAT; break; case MOTU_OPTICAL_MODE_TOSLINK: flags |= MOTU_PA_MK3_OPT_B_TOSLINK; break; } debugOutput(DEBUG_LEVEL_VERBOSE, "flags=0x%08x, opta=0x%x, optb=0x%x\n", flags, optical_a_mode, optical_b_mode); /* Scan through the port groups, allocating packet offsets for all * port groups which are found to be active in the device's current state. */ for (i=0; iportgroup_entry[i].flags; /* For devices without one or more optical ports, ensure the tests * on the optical ports always returns "true". */ if (optical_a_mode == MOTU_OPTICAL_MODE_NONE) portgroup_flags |= MOTU_PA_OPTICAL_ANY; if (optical_b_mode == MOTU_OPTICAL_MODE_NONE) portgroup_flags |= MOTU_PA_MK3_OPT_B_ANY; devprop->portgroup_entry[i].group_pkt_offset[mode_idx] = -1; if (( portgroup_flags & dir ) && ( portgroup_flags & MOTU_PA_RATE_MASK & flags ) && ( portgroup_flags & MOTU_PA_OPTICAL_MASK & flags ) && ( portgroup_flags & MOTU_PA_MK3_OPT_B_MASK & flags )) { if ((portgroup_flags & MOTU_PA_PADDING) == 0) { devprop->portgroup_entry[i].group_pkt_offset[mode_idx] = pkt_ofs; } pkt_ofs += 3*devprop->portgroup_entry[i].n_channels; } } /* The 828mk1 has an additional 6 bytes tacked onto the end of the * packet sent by it, which we must account for when using pkt_ofs as a * proxy for size. */ if (direction==Streaming::Port::E_Capture && m_motu_model==MOTU_MODEL_828MkI) pkt_ofs += 6; if (direction == Streaming::Port::E_Capture) m_rx_event_size = pkt_ofs; else m_tx_event_size = pkt_ofs; debugOutput(DEBUG_LEVEL_VERBOSE, "rxsize=%d, txsize=%d\n", m_rx_event_size, m_tx_event_size); return true; } /* ======================================================================= */ bool MotuDevice::addDirPortGroups( enum Streaming::Port::E_Direction direction, unsigned int sample_rate, unsigned int optical_a_mode, unsigned int optical_b_mode) { /* * Internal helper method. Using a PortGroupEntry array previously * initialised with a call to initPortGroups(), ports are created for each * active channel of the interface. The locations of channels within a * packet are obtained from the group_pkt_offset field which was set by * initPortGroups() and the order they should be created in is specifed by * the port_order field of a port group. * * The port_order functionality is helpful if it's more convenient to have a * particular port show up first in jackd even through it's located in the * middle of the packet. If the port_order field of the first port group is * -1 it is assumed that the port creation is to happen in the order * specified in the port group list. * * A port group is taken to be inactive if its group_pkt_offset is set to -1. */ const char *mode_str = direction==Streaming::Port::E_Capture?"cap":"pbk"; const signed int mode_idx = direction==Streaming::Port::E_Capture?1:0; Streaming::StreamProcessor *s_processor; signed int i; char *buff; const DevicePropertyEntry *devprop = &DevicesProperty[m_motu_model-1]; signed int n_groups = devprop->n_portgroup_entries; signed int creation_indices[n_groups]; signed int create_in_order; if (n_groups <= 0) return true; // retrieve the ID std::string id=std::string("dev?"); if(!getOption("id", id)) { debugWarning("Could not retrieve id parameter, defaulting to 'dev?'\n"); } if (direction == Streaming::Port::E_Capture) { s_processor = m_receiveProcessor; } else { s_processor = m_transmitProcessor; } for (i=0; iportgroup_entry[0].port_order<0; /* First scan through the port groups to determine the creation order */ for (i=0; iportgroup_entry[i].group_pkt_offset[mode_idx] >= 0) { if (create_in_order) creation_indices[i] = i; else creation_indices[devprop->portgroup_entry[i].port_order] = i; } } /* Now run through the portgroup list again, this time in creation order, * to make the ports. */ for (i=0; iportgroup_entry[entry].n_channels; ch++) { /* Deduce the full channel name */ if (strstr(devprop->portgroup_entry[entry].group_name_format, "%d") != NULL) snprintf(namestr, sizeof(namestr), devprop->portgroup_entry[entry].group_name_format, ch+1+devprop->portgroup_entry[entry].port_num_offset); else if (strstr(devprop->portgroup_entry[entry].group_name_format, "%s") != NULL) snprintf(namestr, sizeof(namestr), devprop->portgroup_entry[entry].group_name_format, (ch & 0x1)?"R":"L"); else snprintf(namestr, sizeof(namestr), "%s", devprop->portgroup_entry[entry].group_name_format); asprintf(&buff,"%s_%s_%s" , id.c_str(), mode_str, namestr); if (!addPort(s_processor, buff, direction, devprop->portgroup_entry[entry].group_pkt_offset[mode_idx]+3*ch, 0)) return false; } } return true; } /* ======================================================================== */ unsigned int MotuDevice::ReadRegister(fb_nodeaddr_t reg) { /* * Attempts to read the requested register from the MOTU. */ quadlet_t quadlet = 0; /* If the supplied register has no upper bits set assume it's a G1/G2 * register which is assumed to be relative to MOTU_REG_BASE_ADDR. */ if ((reg & MOTU_REG_BASE_ADDR) == 0) reg |= MOTU_REG_BASE_ADDR; // Note: 1394Service::read() expects a physical ID, not the node id if (get1394Service().read(0xffc0 | getNodeId(), reg, 1, &quadlet) <= 0) { debugError("Error doing motu read from register 0x%012" PRId64 "\n",reg); } return CondSwapFromBus32(quadlet); } signed int MotuDevice::readBlock(fb_nodeaddr_t reg, quadlet_t *buf, signed int n_quads) { // // Read "n_quads" quadlets from the device starting at register "reg" into // the buffer pointed to by "buf". "buf" is assumed to have been // preallocated and be large enough for the requested data. // signed int i; if (get1394Service().read(0xffc0 | getNodeId(), reg, n_quads, buf) <= 0) { debugError("Error doing motu block read of %d quadlets from register 0x%" PRIx64 "\n", n_quads, reg); return -1; } for (i=0; i. * */ #ifndef MOTUDEVICE_H #define MOTUDEVICE_H #include "ffadodevice.h" #include "debugmodule/debugmodule.h" #include "libavc/avc_definitions.h" #include "libstreaming/motu/MotuReceiveStreamProcessor.h" #include "libstreaming/motu/MotuTransmitStreamProcessor.h" #include "motu_controls.h" #include "motu_mark3_controls.h" /* Bitmasks and values used when setting MOTU device registers. Note that * the only "generation 1" device presently supported is the original 828. * "Generation 2" devices include the original Traveler, the 828Mk2, the * original Ultralite, the 896/896HD and so forth. "Generation 3" devices * are all those which carry the "Mark 3" moniker. */ #define MOTU_RATE_BASE_44100 (0<<3) #define MOTU_RATE_BASE_48000 (1<<3) #define MOTU_RATE_MULTIPLIER_1X (0<<4) #define MOTU_RATE_MULTIPLIER_2X (1<<4) #define MOTU_RATE_MULTIPLIER_4X (2<<4) #define MOTU_RATE_BASE_MASK (0x00000008) #define MOTU_RATE_MULTIPLIER_MASK (0x00000030) #define MOTU_G2_OPTICAL_MODE_OFF 0x00 #define MOTU_G2_OPTICAL_MODE_ADAT 0x01 #define MOTU_G2_OPTICAL_MODE_TOSLINK 0x02 #define MOTU_G2_OPTICAL_IN_MODE_MASK (0x00000300) #define MOTU_G2_OPTICAL_IN_MODE_BIT0 8 #define MOTU_G2_OPTICAL_OUT_MODE_MASK (0x00000c00) #define MOTU_G2_OPTICAL_OUT_MODE_BIT0 10 #define MOTU_G2_OPTICAL_MODE_MASK (MOTU_G2_OPTICAL_IN_MODE_MASK|MOTU_G2_OPTICAL_MODE_MASK) #define MOTU_G2_CLKSRC_MASK 0x00000007 #define MOTU_G2_CLKSRC_INTERNAL 0 #define MOTU_G2_CLKSRC_ADAT_OPTICAL 1 #define MOTU_G2_CLKSRC_SPDIF_TOSLINK 2 #define MOTU_G2_CLKSRC_SMPTE 3 #define MOTU_G2_CLKSRC_WORDCLOCK 4 #define MOTU_G2_CLKSRC_ADAT_9PIN 5 #define MOTU_G2_CLKSRC_AES_EBU 7 #define MOTU_METER_PEAKHOLD_MASK 0x3800 #define MOTU_METER_PEAKHOLD_SHIFT 11 #define MOTU_METER_CLIPHOLD_MASK 0x0700 #define MOTU_METER_CLIPHOLD_SHIFT 8 #define MOTU_METER_AESEBU_SRC_MASK 0x0004 #define MOTU_METER_AESEBU_SRC_SHIFT 2 #define MOTU_METER_PROG_SRC_MASK 0x0003 #define MOTU_METER_PROG_SRC_SHIFT 0 /* Device registers. The "base" registers (that is, those shared * between the G1, G2 and G3 interfaces) are specified and assumed to * be relative to MOTU_REG_BASE_ADDR. The following block is notionally * for G2 devices, although some of these are shared with G1 and G3 * units too. */ #define MOTU_REG_BASE_ADDR 0xfffff0000000ULL #define MOTU_REG_ISOCTRL 0x0b00 #define MOTU_REG_OPTICAL_CTRL 0x0b10 #define MOTU_REG_CLK_CTRL 0x0b14 #define MOTU_REG_896HD_METER_REG 0x0b1c #define MOTU_REG_896HD_METER_CONF 0x0b24 #define MOTU_REG_ROUTE_PORT_CONF 0x0c04 #define MOTU_REG_INPUT_LEVEL 0x0c08 #define MOTU_REG_INPUT_BOOST 0x0c14 #define MOTU_REG_INPUT_GAIN_PAD_0 0x0c1c #define MOTU_REG_CLKSRC_NAME0 0x0c60 #define MOTU_REG_INPUT_GAIN_PHINV0 0x0c70 #define MOTU_REG_INPUT_GAIN_PHINV1 0x0c74 #define MOTU_REG_INPUT_GAIN_PHINV2 0x0c78 /* Device register definitions for the earliest generation devices */ #define MOTU_G1_REG_CONFIG 0x0b00 #define MOTU_G1_REG_UNKNOWN_1 0x0b04 // Precise function unknown #define MOTU_G1_REG_UNKNOWN_2 0x0b08 // Precise function unknown #define MOTU_G1_REG_CONFIG_2 0x0b10 /* Values written to registers in G1 devices. * * Control of the optical mode on the G1 devices (such as the 828mk1) is * strange in that it's split across two configration registers. * Furthermore, while the toslink mode is controlled by "enable" bits, the * ADAT mode is controlled by "disable" bits (that is, the mode is active if * the bits are clear. */ #define MOTU_G1_C1_ISO_ENABLE 0x00000080 #define MOTU_G1_C1_OPT_TOSLINK_IN 0x00008000 #define MOTU_G1_C1_OPT_TOSLINK_OUT 0x00004000 #define MOTU_G1_C1_ISO_TX_CH_MASK 0x000f0000 #define MOTU_G1_C1_ISO_TX_CH_BIT0 16 #define MOTU_G1_C1_ISO_TX_ACTIVE 0x00400000 #define MOTU_G1_C1_ISO_TX_WREN 0x00800000 // Enable iso tx conf changes #define MOTU_G1_C1_ISO_RX_CH_MASK 0x0f000000 #define MOTU_G1_C1_ISO_RX_CH_BIT0 24 #define MOTU_G1_C1_ISO_RX_ACTIVE 0x40000000 #define MOTU_G1_C1_ISO_RX_WREN 0x80000000 // Enable iso rx conf changes #define MOTU_G1_C1_ISO_INFO_MASK 0xffff0000 #define MOTU_G1_C2_OPT_nADAT_IN 0x00000080 #define MOTU_G1_C2_OPT_nADAT_OUT 0x00000040 #define MOTU_G1_C2_OPT_nADAT_WREN 0x00000002 // Purpose to be confirmed // Rate control bits in MOTU_G1_REG_CONFIG #define MOTU_G1_RATE_MASK 0x0004 #define MOTU_G1_RATE_44100 0x0000 #define MOTU_G1_RATE_48000 0x0004 // Clock source control bits in MOTU_G1_REG_CONFIG. The SMTPE clock source // is not presently supported in FFADO because software requirements of this // source are not yet known. #define MOTU_G1_CLKSRC_MASK 0x0023 #define MOTU_G1_CLKSRC_ADAT_9PIN 0x0001 #define MOTU_G1_CLKSRC_SMTPE 0x0003 // Not currently supported in FFADO #define MOTU_G1_CLKSRC_INTERNAL 0x0020 #define MOTU_G1_CLKSRC_SPDIF 0x0022 #define MOTU_G1_CLKSRC_TOSLINK 0x0022 #define MOTU_G1_CLKSRC_ADAT_OPTICAL 0x0021 #define MOTU_G1_CLKSRC_UNCHANGED MOTU_CLKSRC_UNCHANGED // Monitor control bits in MOTU_G1_REG_CONFIG #define MOTU_G1_MONIN_MUTE 0x0040 #define MOTU_G1_MONIN_MASK 0x3f00 #define MOTU_G1_MONIN_L_SRC_MASK 0x0700 #define MOTU_G1_MONIN_R_SRC_MASK 0x3800 #define MOTU_G1_MONIN_L_CH_BIT0 8 #define MOTU_G1_MONIN_R_CH_BIT0 11 #define MOTU_G1_MONIN_L_CH1 0x0000 #define MOTU_G1_MONIN_L_CH2 0x0100 #define MOTU_G1_MONIN_L_CH3 0x0200 #define MOTU_G1_MONIN_L_CH4 0x0300 #define MOTU_G1_MONIN_L_CH5 0x0400 #define MOTU_G1_MONIN_L_CH6 0x0500 #define MOTU_G1_MONIN_L_CH7 0x0600 #define MOTU_G1_MONIN_L_CH8 0x0700 #define MOTU_G1_MONIN_R_CH1 0x0000 #define MOTU_G1_MONIN_R_CH2 0x0800 #define MOTU_G1_MONIN_R_CH3 0x1000 #define MOTU_G1_MONIN_R_CH4 0x1800 #define MOTU_G1_MONIN_R_CH5 0x2000 #define MOTU_G1_MONIN_R_CH6 0x2800 #define MOTU_G1_MONIN_R_CH7 0x3000 #define MOTU_G1_MONIN_R_CH8 0x3800 // Other control bits in MOTU_G1_REG_CONFIG #define MOTU_G1_IO_ENABLE_0 0x0008 // To be confirmed /* Mark3 device registers - these don't always have MOTU_BASE_ADDR as the * base address so for now we'll define them as absolute addresses. The * "Mark 3" (aka G3) devices share a number of control registers with the G2 * devices. Where this occurs we just use the G2 definitions since there's * little to be gained by adding duplicate defines. */ #define MOTU_G3_REG_MIXER 0xffff00010000ULL #define MOTU_G3_REG_OPTICAL_CTRL 0xfffff0000c94ULL /* Mark3 (aka G3) register constants for cases where the G3 devices differ * from the G2s. */ #define MOTU_G3_CLKSRC_MASK 0x0000001b #define MOTU_G3_CLKSRC_INTERNAL 0x00000000 #define MOTU_G3_CLKSRC_WORDCLOCK 0x00000001 #define MOTU_G3_CLKSRC_SMPTE 0x00000002 #define MOTU_G3_CLKSRC_SPDIF 0x00000010 #define MOTU_G3_CLKSRC_OPTICAL_A 0x00000018 #define MOTU_G3_CLKSRC_OPTICAL_B 0x00000019 #define MOTU_G3_CLKSRC_ADAT_A MOTU_G3_CLKSRC_OPTICAL_A #define MOTU_G3_CLKSRC_ADAT_B MOTU_G3_CLKSRC_OPTICAL_B #define MOTU_G3_CLKSRC_TOSLINK_A MOTU_G3_CLKSRC_OPTICAL_A #define MOTU_G3_CLKSRC_TOSLINK_B MOTU_G3_CLKSRC_OPTICAL_A #define MOTU_G3_RATE_BASE_44100 (0<<8) #define MOTU_G3_RATE_BASE_48000 (1<<8) #define MOTU_G3_RATE_MULTIPLIER_1X (0<<9) #define MOTU_G3_RATE_MULTIPLIER_2X (1<<9) #define MOTU_G3_RATE_MULTIPLIER_4X (2<<9) #define MOTU_G3_RATE_BASE_MASK (0x00000100) #define MOTU_G3_RATE_MULTIPLIER_MASK (0x00000600) #define MOTU_G3_OPT_A_IN_ENABLE 0x00000001 #define MOTU_G3_OPT_B_IN_ENABLE 0x00000002 #define MOTU_G3_OPT_A_OUT_ENABLE 0x00000100 #define MOTU_G3_OPT_B_OUT_ENABLE 0x00000200 #define MOTU_G3_OPT_A_IN_TOSLINK 0x00010000 // If these mode bits are not #define MOTU_G3_OPT_A_OUT_TOSLINK 0x00040000 // set the mode is ADAT if #define MOTU_G3_OPT_B_IN_TOSLINK 0x00100000 // the port is enabled #define MOTU_G3_OPT_B_OUT_TOSLINK 0x00400000 #define MOTU_G3_OPT_A_IN_MASK (MOTU_G3_OPT_A_IN_ENABLE|MOTU_G3_OPT_A_IN_TOSLINK) #define MOTU_G3_OPT_A_OUT_MASK (MOTU_G3_OPT_A_OUT_ENABLE|MOTU_G3_OPT_A_OUT_TOSLINK) #define MOTU_G3_OPT_B_IN_MASK (MOTU_G3_OPT_B_IN_ENABLE|MOTU_G3_OPT_B_IN_TOSLINK) #define MOTU_G3_OPT_B_OUT_MASK (MOTU_G3_OPT_B_OUT_ENABLE|MOTU_G3_OPT_B_OUT_TOSLINK) /* The following values are used when defining configuration structures and * calling driver functions. They are generally not raw values written to * registers. */ /* Port Active Flags (ports declaration) */ #define MOTU_PA_RATE_1x 0x0001 /* 44k1 or 48k */ #define MOTU_PA_RATE_2x 0x0002 /* 88k2 or 96k */ #define MOTU_PA_RATE_4x 0x0004 /* 176k4 or 192k */ #define MOTU_PA_RATE_1x2x (MOTU_PA_RATE_1x|MOTU_PA_RATE_2x) #define MOTU_PA_RATE_2x4x (MOTU_PA_RATE_2x|MOTU_PA_RATE_4x) #define MOTU_PA_RATE_ANY (MOTU_PA_RATE_1x|MOTU_PA_RATE_2x|MOTU_PA_RATE_4x) #define MOTU_PA_RATE_MASK MOTU_PA_RATE_ANY #define MOTU_PA_OPTICAL_OFF 0x0010 /* Optical port off */ #define MOTU_PA_OPTICAL_ADAT 0x0020 /* Optical port in ADAT mode */ #define MOTU_PA_OPTICAL_TOSLINK 0x0040 /* Optical port in SPDIF/Toslink mode */ #define MOTU_PA_OPTICAL_ON (MOTU_PA_OPTICAL_ADAT|MOTU_PA_OPTICAL_TOSLINK) #define MOTU_PA_OPTICAL_ANY (MOTU_PA_OPTICAL_OFF|MOTU_PA_OPTICAL_ON) #define MOTU_PA_OPTICAL_MASK MOTU_PA_OPTICAL_ANY #define MOTU_PA_PADDING 0x0100 #define MOTU_PA_IN 0x0200 #define MOTU_PA_OUT 0x0400 #define MOTU_PA_INOUT (MOTU_PA_IN | MOTU_PA_OUT) /* The Mark3 devices have two optical ports */ #define MOTU_PA_MK3_OPT_A_OFF MOTU_PA_OPTICAL_OFF #define MOTU_PA_MK3_OPT_A_ADAT MOTU_PA_OPTICAL_ADAT #define MOTU_PA_MK3_OPT_A_TOSLINK MOTU_PA_OPTICAL_TOSLINK #define MOTU_PA_MK3_OPT_A_ON (MOTU_PA_MK3_OPT_A_ADAT|MOTU_PA_MK3_OPT_A_TOSLINK) #define MOTU_PA_MK3_OPT_A_ANY (MOTU_PA_MK3_OPT_A_OFF|MOTU_PA_MK3_OPT_A_ON) #define MOTU_PA_MK3_OPT_A_MASK MOTU_PA_MK3_OPT_A_ANY #define MOTU_PA_MK3_OPT_B_OFF 0x1000 #define MOTU_PA_MK3_OPT_B_ADAT 0x2000 #define MOTU_PA_MK3_OPT_B_TOSLINK 0x4000 #define MOTU_PA_MK3_OPT_B_ON (MOTU_PA_MK3_OPT_B_ADAT|MOTU_PA_MK3_OPT_B_TOSLINK) #define MOTU_PA_MK3_OPT_B_ANY (MOTU_PA_MK3_OPT_B_OFF|MOTU_PA_MK3_OPT_B_ON) #define MOTU_PA_MK3_OPT_B_MASK MOTU_PA_MK3_OPT_B_ANY #define MOTU_PA_MK3_OPT_ANY (MOTU_PA_MK3_OPT_A_ANY|MOTU_PA_MK3_OPT_B_ANY) /* Generic "direction" flags */ #define MOTU_DIR_IN 1 #define MOTU_DIR_OUT 2 #define MOTU_DIR_INOUT (MOTU_DIR_IN | MOTU_DIR_OUT) /* Optical port mode settings/flags */ #define MOTU_OPTICAL_MODE_OFF 0x0000 #define MOTU_OPTICAL_MODE_ADAT 0x0001 #define MOTU_OPTICAL_MODE_TOSLINK 0x0002 #define MOTU_OPTICAL_MODE_KEEP 0xffff #define MOTU_OPTICAL_MODE_NONE 0xffffffff /* Clock source settings/flags */ #define MOTU_CLKSRC_INTERNAL 0 #define MOTU_CLKSRC_ADAT_OPTICAL 1 #define MOTU_CLKSRC_SPDIF_TOSLINK 2 #define MOTU_CLKSRC_SMPTE 3 #define MOTU_CLKSRC_WORDCLOCK 4 #define MOTU_CLKSRC_ADAT_9PIN 5 #define MOTU_CLKSRC_AES_EBU 6 #define MOTU_CLKSRC_OPTICAL_A 7 #define MOTU_CLKSRC_OPTICAL_B 8 #define MOTU_CLKSRC_LAST 8 #define MOTU_CLKSRC_NONE 0xffff #define MOTU_CLKSRC_UNCHANGED MOTU_CLKSRC_NONE /* Device generation identifiers */ #define MOTU_DEVICE_G1 0x0001 #define MOTU_DEVICE_G2 0x0002 #define MOTU_DEVICE_G3 0x0003 class ConfigRom; class Ieee1394Service; namespace Util { class Configuration; } namespace Motu { enum EMotuModel { MOTU_MODEL_NONE = 0x0000, MOTU_MODEL_828mkII = 0x0001, MOTU_MODEL_TRAVELER = 0x0002, MOTU_MODEL_ULTRALITE = 0x0003, MOTU_MODEL_8PRE = 0x0004, MOTU_MODEL_828MkI = 0x0005, MOTU_MODEL_896HD = 0x0006, MOTU_MODEL_828mk3 = 0x0007, MOTU_MODEL_ULTRALITEmk3 = 0x0008, MOTU_MODEL_ULTRALITEmk3_HYB = 0x0009, MOTU_MODEL_TRAVELERmk3 = 0x000a, // Placeholder only at present MOTU_MODEL_896mk3 = 0x000b, // Placeholder only at present MOTU_MODEL_4PRE = 0x000c, }; struct VendorModelEntry { unsigned int vendor_id; unsigned int model_id; unsigned int unit_version; unsigned int unit_specifier_id; enum EMotuModel model; const char *vendor_name; const char *model_name; }; struct PortGroupEntry { const char *group_name_format; signed int n_channels; unsigned int flags; signed int port_order; signed int port_num_offset; signed int group_pkt_offset[2]; }; // Structures used for pre-Mark3 device mixer definitions struct MixerCtrl { const char *name, *label, *desc; unsigned int type; unsigned int dev_register; }; struct MatrixMixBus { const char *name; unsigned int address; }; struct MatrixMixChannel { const char *name; unsigned int flags; unsigned int addr_ofs; }; struct MotuMixer { const MixerCtrl *mixer_ctrl; unsigned int n_mixer_ctrls; const MatrixMixBus *mixer_buses; unsigned int n_mixer_buses; const MatrixMixChannel *mixer_channels; unsigned int n_mixer_channels; }; // Structures used for devices which use the "Mark3" mixer protocol struct MotuMark3Mixer { }; struct DevicePropertyEntry { PortGroupEntry *portgroup_entry; unsigned int n_portgroup_entries; signed int MaxSampleRate; // A device will set at most one of the *mixer fields const struct MotuMixer *mixer; const struct MotuMark3Mixer *m3mixer; // Others features can be added here like MIDI port presence. }; extern const DevicePropertyEntry DevicesProperty[]; /* Macro to calculate the size of an array */ #define N_ELEMENTS(_array) (sizeof(_array) / sizeof((_array)[0])) /* Macro to define a MotuMixer structure succintly */ #define MOTUMIXER(_ctrls, _buses, _channels) \ { _ctrls, N_ELEMENTS(_ctrls), _buses, N_ELEMENTS(_buses), _channels, N_ELEMENTS(_channels), } class MotuDevice : public FFADODevice { public: MotuDevice( DeviceManager& d, ffado_smartptr( configRom ) ); virtual ~MotuDevice(); virtual bool buildMixer(); virtual bool destroyMixer(); static bool probe( Util::Configuration&, ConfigRom& configRom, bool generic = false ); static FFADODevice * createDevice( DeviceManager& d, ffado_smartptr( configRom )); static int getConfigurationId( ); virtual bool discover(); virtual void showDevice(); unsigned int getHwClockSource(); bool setClockCtrlRegister(signed int samplingFrequency, unsigned int clock_source); virtual bool setSamplingFrequency( int samplingFrequency ); virtual int getSamplingFrequency( ); virtual std::vector getSupportedSamplingFrequencies(); FFADODevice::ClockSource clockIdToClockSource(unsigned int id); virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); virtual int getStreamCount(); virtual Streaming::StreamProcessor *getStreamProcessorByIndex(int i); virtual enum FFADODevice::eStreamingState getStreamingState(); virtual bool prepare(); virtual bool lock(); virtual bool unlock(); virtual bool startStreamByIndex(int i); virtual bool stopStreamByIndex(int i); signed int getDeviceGeneration(void); signed int getIsoRecvChannel(void); signed int getIsoSendChannel(void); unsigned int getOpticalMode(unsigned int dir, unsigned int *port_a_mode, unsigned int *port_b_mode); signed int setOpticalMode(unsigned int dir, unsigned int port_a_mode, unsigned int port_b_mode); signed int getEventSize(unsigned int dir); signed int m_motu_model; protected: struct VendorModelEntry * m_model; signed int m_iso_recv_channel, m_iso_send_channel; signed int m_rx_bandwidth, m_tx_bandwidth; signed int m_rx_event_size, m_tx_event_size; Streaming::MotuReceiveStreamProcessor *m_receiveProcessor; Streaming::MotuTransmitStreamProcessor *m_transmitProcessor; private: bool buildMixerAudioControls(void); bool buildMark3MixerAudioControls(void); 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_a_mode, unsigned int optical_b_mode); bool initDirPortGroups( enum Streaming::Port::E_Direction direction, unsigned int sample_rate, unsigned int optical_a_mode, unsigned int optical_b_mode); bool addDirPortGroups( enum Streaming::Port::E_Direction direction, unsigned int sample_rate, unsigned int optical_a_mode, unsigned int optical_b_mode); public: unsigned int ReadRegister(fb_nodeaddr_t reg); signed int readBlock(fb_nodeaddr_t reg, quadlet_t *buf, signed int n_quads); signed int WriteRegister(fb_nodeaddr_t reg, quadlet_t data); signed int writeBlock(fb_nodeaddr_t reg, quadlet_t *data, signed int n_quads); private: Control::Container *m_MixerContainer; Control::Container *m_ControlContainer; }; } #endif libffado-2.4.5/src/motu/motu_controls.cpp0000644000175000001440000006764514206145246020074 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2008-2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // This also includes motu_controls.h #include "motu_avdevice.h" namespace Motu { MotuDiscreteCtrl::MotuDiscreteCtrl(MotuDevice &parent, unsigned int dev_reg) : Control::Discrete(&parent) , m_parent(parent) , m_register(dev_reg) { } MotuDiscreteCtrl::MotuDiscreteCtrl(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr) : Control::Discrete(&parent) , m_parent(parent) , m_register(dev_reg) { setName(name); setLabel(label); setDescription(descr); } MotuBinarySwitch::MotuBinarySwitch(MotuDevice &parent, unsigned int dev_reg, unsigned int val_mask, unsigned int setenable_mask) : MotuDiscreteCtrl(parent, dev_reg) { m_value_mask = val_mask; /* If no "write enable" is implemented for a given switch it's safe to * pass zero in to setenable_mask. */ m_setenable_mask = setenable_mask; } MotuBinarySwitch::MotuBinarySwitch(MotuDevice &parent, unsigned int dev_reg, unsigned int val_mask, unsigned int setenable_mask, std::string name, std::string label, std::string descr) : MotuDiscreteCtrl(parent, dev_reg, name, label, descr) { m_value_mask = val_mask; /* If no "write enable" is implemented for a given switch it's safe to * pass zero in to setenable_mask. */ m_setenable_mask = setenable_mask; } bool MotuBinarySwitch::setValue(int v) { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for switch %s (0x%04x) to %d\n", getName().c_str(), m_register, v); if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return true; } // Set the value if (m_setenable_mask) { val = (v==0)?0:m_value_mask; // Set the "write enable" bit for the value being set val |= m_setenable_mask; } else { // It would be good to utilise the cached value from the receive // processor (if running) later on. For now we'll just fetch the // current register value directly when needed. val = m_parent.ReadRegister(m_register); if (v==0) val &= ~m_value_mask; else val |= m_value_mask; } m_parent.WriteRegister(m_register, val); return true; } int MotuBinarySwitch::getValue() { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for switch %s (0x%04x)\n", getName().c_str(), m_register); if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return 0; } val = m_parent.ReadRegister(m_register); return (val & m_value_mask) != 0; } ChannelFader::ChannelFader(MotuDevice &parent, unsigned int dev_reg) : MotuDiscreteCtrl(parent, dev_reg) { } ChannelFader::ChannelFader(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr) : MotuDiscreteCtrl(parent, dev_reg, name, label, descr) { } bool ChannelFader::setValue(int v) { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for channel fader 0x%04x to %d\n", m_register, v); if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return true; } val = v<0?0:v; if (val > 0x80) val = 0x80; // Bit 30 indicates that the channel fader is being set val |= 0x40000000; m_parent.WriteRegister(m_register, val); return true; } int ChannelFader::getValue() { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for channel fader 0x%04x\n", m_register); // Silently swallow attempts to read non-existent controls for now if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return 0; } val = m_parent.ReadRegister(m_register); return val & 0xff; } ChannelPan::ChannelPan(MotuDevice &parent, unsigned int dev_reg) : MotuDiscreteCtrl(parent, dev_reg) { } ChannelPan::ChannelPan(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr) : MotuDiscreteCtrl(parent, dev_reg, name, label, descr) { } bool ChannelPan::setValue(int v) { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for channel pan 0x%04x to %d\n", m_register, v); if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return true; } val = ((v<-64?-64:v)+64) & 0xff; if (val > 0x80) val = 0x80; // Bit 31 indicates that pan is being set val = (val << 8) | 0x80000000; m_parent.WriteRegister(m_register, val); return true; } int ChannelPan::getValue() { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for channel pan 0x%04x\n", m_register); // Silently swallow attempts to read non-existent controls for now if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return 0; } val = m_parent.ReadRegister(m_register); return ((val >> 8) & 0xff) - 0x40; } MotuMatrixMixer::MotuMatrixMixer(MotuDevice &parent) : Control::MatrixMixer(&parent, "MatrixMixer") , m_parent(parent) { } MotuMatrixMixer::MotuMatrixMixer(MotuDevice &parent, std::string name) : Control::MatrixMixer(&parent, name) , m_parent(parent) { } void MotuMatrixMixer::addRowInfo(std::string name, unsigned int flags, unsigned int address) { struct sSignalInfo s; s.name = name; s.flags = flags; s.address = address; m_RowInfo.push_back(s); } void MotuMatrixMixer::addColInfo(std::string name, unsigned int flags, unsigned int address) { struct sSignalInfo s; s.name = name; s.flags = flags; s.address = address; m_ColInfo.push_back(s); } uint32_t MotuMatrixMixer::getCellRegister(const unsigned int row, const unsigned int col) { if (m_RowInfo.at(row).address==MOTU_CTRL_NONE || m_ColInfo.at(col).address==MOTU_CTRL_NONE) return MOTU_CTRL_NONE; return m_RowInfo.at(row).address + m_ColInfo.at(col).address; } void MotuMatrixMixer::show() { debugOutput(DEBUG_LEVEL_NORMAL, "MOTU matrix mixer\n"); } std::string MotuMatrixMixer::getRowName(const int row) { return m_RowInfo.at(row).name; } std::string MotuMatrixMixer::getColName(const int col) { return m_ColInfo.at(col).name; } int MotuMatrixMixer::getRowCount() { return m_RowInfo.size(); } int MotuMatrixMixer::getColCount() { return m_ColInfo.size(); } ChannelFaderMatrixMixer::ChannelFaderMatrixMixer(MotuDevice &parent) : MotuMatrixMixer(parent, "ChannelFaderMatrixMixer") { } ChannelFaderMatrixMixer::ChannelFaderMatrixMixer(MotuDevice &parent, std::string name) : MotuMatrixMixer(parent, name) { } double ChannelFaderMatrixMixer::setValue(const int row, const int col, const double val) { uint32_t v, reg; v = val<0?0:(uint32_t)val; if (v > 0x80) v = 0x80; debugOutput(DEBUG_LEVEL_VERBOSE, "ChannelFader setValue for row %d col %d to %lf (%d)\n", row, col, val, v); reg = getCellRegister(row,col); // Silently swallow attempts to set non-existent controls for now if (reg == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_VERBOSE, "ignoring control marked as non-existent\n"); return true; } // Bit 30 indicates that the channel fader is being set v |= 0x40000000; m_parent.WriteRegister(reg, v); return true; } double ChannelFaderMatrixMixer::getValue(const int row, const int col) { uint32_t val, reg; reg = getCellRegister(row,col); // Silently swallow attempts to read non-existent controls for now if (reg == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_VERBOSE, "ignoring control marked as non-existent\n"); return 0; } val = m_parent.ReadRegister(reg) & 0xff; debugOutput(DEBUG_LEVEL_VERBOSE, "ChannelFader getValue for row %d col %d = %u\n", row, col, val); return val; } ChannelPanMatrixMixer::ChannelPanMatrixMixer(MotuDevice &parent) : MotuMatrixMixer(parent, "ChannelPanMatrixMixer") { } ChannelPanMatrixMixer::ChannelPanMatrixMixer(MotuDevice &parent, std::string name) : MotuMatrixMixer(parent, name) { } double ChannelPanMatrixMixer::setValue(const int row, const int col, const double val) { uint32_t v, reg; v = ((val<-64?-64:(int32_t)val)+64) & 0xff; if (v > 0x80) v = 0x80; debugOutput(DEBUG_LEVEL_VERBOSE, "ChannelPan setValue for row %d col %d to %lf (%d)\n", row, col, val, v); reg = getCellRegister(row,col); // Silently swallow attempts to set non-existent controls for now if (reg == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_VERBOSE, "ignoring control marked as non-existent\n"); return true; } // Bit 31 indicates that pan is being set v = (v << 8) | 0x80000000; m_parent.WriteRegister(reg, v); return true; } double ChannelPanMatrixMixer::getValue(const int row, const int col) { int32_t val; uint32_t reg; reg = getCellRegister(row,col); // Silently swallow attempts to read non-existent controls for now if (reg == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_VERBOSE, "ignoring control marked as non-existent\n"); return 0; } val = m_parent.ReadRegister(reg); val = ((val >> 8) & 0xff) - 0x40; debugOutput(DEBUG_LEVEL_VERBOSE, "ChannelPan getValue for row %d col %d = %u\n", row, col, val); return val; } ChannelBinSwMatrixMixer::ChannelBinSwMatrixMixer(MotuDevice &parent) : MotuMatrixMixer(parent, "ChannelPanMatrixMixer") , m_value_mask(0) , m_setenable_mask(0) { } /* If no "write enable" is implemented for a given switch it's safe to * pass zero in to setenable_mask. */ ChannelBinSwMatrixMixer::ChannelBinSwMatrixMixer(MotuDevice &parent, std::string name, unsigned int val_mask, unsigned int setenable_mask) : MotuMatrixMixer(parent, name) , m_value_mask(val_mask) , m_setenable_mask(setenable_mask) { } double ChannelBinSwMatrixMixer::setValue(const int row, const int col, const double val) { uint32_t v, reg; debugOutput(DEBUG_LEVEL_VERBOSE, "BinSw setValue for row %d col %d to %lf (%d)\n", row, col, val, val==0?0:1); reg = getCellRegister(row,col); // Silently swallow attempts to set non-existent controls for now if (reg == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_VERBOSE, "ignoring control marked as non-existent\n"); return true; } // Set the value if (m_setenable_mask) { v = (val==0)?0:m_value_mask; // Set the "write enable" bit for the value being set v |= m_setenable_mask; } else { // It would be good to utilise the cached value from the receive // processor (if running) later on. For now we'll just fetch the // current register value directly when needed. v = m_parent.ReadRegister(reg); if (v==0) v &= ~m_value_mask; else v |= m_value_mask; } m_parent.WriteRegister(reg, v); return true; } double ChannelBinSwMatrixMixer::getValue(const int row, const int col) { uint32_t val, reg; reg = getCellRegister(row,col); // Silently swallow attempts to read non-existent controls for now if (reg == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_VERBOSE, "ignoring control marked as non-existent\n"); return 0; } val = m_parent.ReadRegister(reg); val = (val & m_value_mask) != 0; debugOutput(DEBUG_LEVEL_VERBOSE, "BinSw getValue for row %d col %d = %u\n", row, col, val); return val; } MixFader::MixFader(MotuDevice &parent, unsigned int dev_reg) : MotuDiscreteCtrl(parent, dev_reg) { } MixFader::MixFader(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr) : MotuDiscreteCtrl(parent, dev_reg, name, label, descr) { } bool MixFader::setValue(int v) { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for mix fader 0x%04x to %d\n", m_register, v); // Silently swallow attempts to set non-existent controls for now if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return true; } val = v<0?0:v; if (val > 0x80) val = 0x80; // Bit 24 indicates that the mix fader is being set val |= 0x01000000; m_parent.WriteRegister(m_register, val); return true; } int MixFader::getValue() { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for mix fader 0x%04x\n", m_register); // Silently swallow attempts to read non-existent controls for now if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return 0; } val = m_parent.ReadRegister(m_register); return val & 0xff; } MixMute::MixMute(MotuDevice &parent, unsigned int dev_reg) : MotuDiscreteCtrl(parent, dev_reg) { } MixMute::MixMute(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr) : MotuDiscreteCtrl(parent, dev_reg, name, label, descr) { } bool MixMute::setValue(int v) { unsigned int val, dest; debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for mix mute 0x%04x to %d\n", m_register, v); // Silently swallow attempts to set non-existent controls for now if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return true; } // Need to read current destination so we can preserve that when setting // mute status (mute and destination are always set together). dest = m_parent.ReadRegister(m_register) & 0x00000f00; // Mute status is bit 12 val = (v==0)?0:0x00001000; // Bit 25 indicates that mute and destination are being set. Also // preserve the current destination. val |= 0x02000000 | dest; m_parent.WriteRegister(m_register, val); return true; } int MixMute::getValue() { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for mix mute 0x%04x\n", m_register); // Silently swallow attempts to read non-existent controls for now if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return 0; } val = m_parent.ReadRegister(m_register); return (val & 0x00001000) != 0; } MixDest::MixDest(MotuDevice &parent, unsigned int dev_reg) : MotuDiscreteCtrl(parent, dev_reg) { } MixDest::MixDest(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr) : MotuDiscreteCtrl(parent, dev_reg, name, label, descr) { } bool MixDest::setValue(int v) { unsigned int val, mute; debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for mix destination 0x%04x to %d\n", m_register, v); // Silently swallow attempts to set non-existent controls for now if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return true; } // Need to get current mute status so we can preserve it mute = m_parent.ReadRegister(m_register) & 0x00001000; val = v; /* Currently destination values between 0 and 0x0b are accepted. * Ultimately this will be device (and device configuration) dependent. */ if (val<0 || val>0x0b) val = 0; /* Destination is given by bits 11-8. Add in the current mute status so * it can be preserved (it's set concurrently with the destination). */ val = (val << 8) | mute; // Bit 25 indicates that mute and destination are being set val |= 0x02000000; m_parent.WriteRegister(m_register, val); return true; } int MixDest::getValue() { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for mix destination 0x%04x\n", m_register); // Silently swallow attempts to read non-existent controls for now if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return true; } val = m_parent.ReadRegister(m_register); return (val >> 8) & 0x0f; } PhonesSrc::PhonesSrc(MotuDevice &parent) : MotuDiscreteCtrl(parent, 0) { } PhonesSrc::PhonesSrc(MotuDevice &parent, std::string name, std::string label, std::string descr) : MotuDiscreteCtrl(parent, 0, name, label, descr) { } bool PhonesSrc::setValue(int v) { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for phones destination to %d\n", v); /* Currently destination values between 0 and 0x0b are accepted. * Ultimately this will be device (and device configuration) dependent. */ val = v; if (val<0 || val>0x0b) val = 0; // Destination is given by bits 3-0. // Bit 24 indicates that the phones source is being set. val |= 0x01000000; m_parent.WriteRegister(MOTU_REG_ROUTE_PORT_CONF, val); return true; } int PhonesSrc::getValue() { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for phones destination\n"); val = m_parent.ReadRegister(MOTU_REG_ROUTE_PORT_CONF); return val & 0x0f; } OpticalMode::OpticalMode(MotuDevice &parent, unsigned int dev_reg) : MotuDiscreteCtrl(parent, dev_reg) { } OpticalMode::OpticalMode(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr) : MotuDiscreteCtrl(parent, dev_reg, name, label, descr) { } bool OpticalMode::setValue(int v) { unsigned int val, dir; debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for optical mode %d to %d\n", m_register, v); /* Assume v is 0 for "off", 1 for "ADAT" and 2 for "Toslink" */ switch (v) { case 0: val = MOTU_OPTICAL_MODE_OFF; break; case 1: val = MOTU_OPTICAL_MODE_ADAT; break; case 2: val = MOTU_OPTICAL_MODE_TOSLINK; break; default: return true; } dir = (m_register==MOTU_CTRL_DIR_IN)?MOTU_DIR_IN:MOTU_DIR_OUT; m_parent.setOpticalMode(dir, val, MOTU_OPTICAL_MODE_KEEP); return true; } int OpticalMode::getValue() { unsigned int dir, omode_a; debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for optical mode %d\n", m_register); dir = (m_register==MOTU_CTRL_DIR_IN)?MOTU_DIR_IN:MOTU_DIR_OUT; m_parent.getOpticalMode(dir, &omode_a, NULL); switch (omode_a) { case MOTU_OPTICAL_MODE_OFF: return 0; case MOTU_OPTICAL_MODE_ADAT: return 1; case MOTU_OPTICAL_MODE_TOSLINK: return 2; default: return 0; } return 0; } InputGainPadInv::InputGainPadInv(MotuDevice &parent, unsigned int channel, unsigned int mode) : MotuDiscreteCtrl(parent, channel) { m_mode = mode; validate(); } InputGainPadInv::InputGainPadInv(MotuDevice &parent, unsigned int channel, unsigned int mode, std::string name, std::string label, std::string descr) : MotuDiscreteCtrl(parent, channel, name, label, descr) { m_mode = mode; validate(); } void InputGainPadInv::validate(void) { if ((m_mode==MOTU_CTRL_MODE_PAD || m_mode==MOTU_CTRL_MODE_TRIMGAIN) && m_register>MOTU_CTRL_TRIMGAINPAD_MAX_CHANNEL) { debugOutput(DEBUG_LEVEL_VERBOSE, "Invalid channel %d: max supported is %d, assuming 0\n", m_register, MOTU_CTRL_TRIMGAINPAD_MAX_CHANNEL); m_register = 0; } if ((m_mode==MOTU_CTRL_MODE_UL_GAIN || m_mode==MOTU_CTRL_MODE_PHASE_INV) && m_register>MOTU_CTRL_GAINPHASEINV_MAX_CHANNEL) { debugOutput(DEBUG_LEVEL_VERBOSE, "Invalid ultralite channel %d: max supported is %d, assuming 0\n", m_register, MOTU_CTRL_GAINPHASEINV_MAX_CHANNEL); m_register = 0; } if (m_mode!=MOTU_CTRL_MODE_PAD && m_mode!=MOTU_CTRL_MODE_TRIMGAIN && m_mode!=MOTU_CTRL_MODE_UL_GAIN && m_mode!=MOTU_CTRL_MODE_PHASE_INV) { debugOutput(DEBUG_LEVEL_VERBOSE, "Invalid mode %d, assuming %d\n", m_mode, MOTU_CTRL_MODE_PAD); m_mode = MOTU_CTRL_MODE_PAD; } } unsigned int InputGainPadInv::dev_register(void) { /* Work out the device register to use for the associated channel */ /* Registers for gain/phase inversion controls on the Ultralite differ from those * of other devices. */ if (m_mode==MOTU_CTRL_MODE_PAD || m_mode==MOTU_CTRL_MODE_TRIMGAIN) { if (m_register>=0 && m_register<=3) { return MOTU_REG_INPUT_GAIN_PAD_0; } else { debugOutput(DEBUG_LEVEL_VERBOSE, "unsupported channel %d\n", m_register); } } else { if (m_register>=0 && m_register<=3) return MOTU_REG_INPUT_GAIN_PHINV0; else if (m_register>=4 && m_register<=7) return MOTU_REG_INPUT_GAIN_PHINV1; else if (m_register>=8 && m_register<=11) return MOTU_REG_INPUT_GAIN_PHINV2; else { debugOutput(DEBUG_LEVEL_VERBOSE, "unsupported ultralite channel %d\n", m_register); } } return 0; } bool InputGainPadInv::setValue(int v) { unsigned int val; unsigned int reg, reg_shift; debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for mode %d input pad/trim %d to %d\n", m_mode, m_register, v); if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return true; } reg = dev_register(); if (reg == 0) return false; reg_shift = (m_register & 0x03) * 8; // Need to get current gain trim / pad value so we can preserve one // while setting the other. The pad status is in bit 6 of the channel's // respective byte with the trim in bits 0-5. Bit 7 is the write enable // bit for the channel. val = m_parent.ReadRegister(reg) & (0xff << reg_shift); switch (m_mode) { case MOTU_CTRL_MODE_PAD: case MOTU_CTRL_MODE_PHASE_INV: // Set pad/phase inversion bit (bit 6 of relevant channel's byte) if (v == 0) { val &= ~(0x40 << reg_shift); } else { val |= (0x40 << reg_shift); } break; case MOTU_CTRL_MODE_TRIMGAIN: case MOTU_CTRL_MODE_UL_GAIN: // Set the gain trim (bits 0-5 of the channel's byte). Maximum // gain is 53 dB for trimgain on non-ultralite devices. For // ultralites, mic inputs max out at 0x18, line inputs at 0x12 // and spdif inputs at 0x0c. We just clip at 0x18 for now. if (m_mode==MOTU_CTRL_MODE_TRIMGAIN) { if (v > 0x35) v = 0x35; } else { if (v > 0x18) v = 0x18; } val = (val & ~(0x3f << reg_shift)) | (v << reg_shift); break; default: debugOutput(DEBUG_LEVEL_VERBOSE, "unsupported mode %d\n", m_mode); return false; } // Set the channel's write enable bit val |= (0x80 << reg_shift); m_parent.WriteRegister(reg, val); return true; } int InputGainPadInv::getValue() { unsigned int val; unsigned int reg, reg_shift; debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for mode %d input pad/trim %d\n", m_mode, m_register); if (m_register == MOTU_CTRL_NONE) { debugOutput(DEBUG_LEVEL_WARNING, "use of MOTU_CTRL_NONE in non-matrix control\n"); return 0; } reg = dev_register(); if (reg == 0) return false; reg_shift = (m_register & 0x03) * 8; // The pad/phase inversion status is in bit 6 of the channel's // respective byte with the trim in bits 0-5. Bit 7 is the write enable // bit for the channel. val = m_parent.ReadRegister(reg); switch (m_mode) { case MOTU_CTRL_MODE_PAD: case MOTU_CTRL_MODE_PHASE_INV: val = ((val >> reg_shift) & 0x40) != 0; break; case MOTU_CTRL_MODE_TRIMGAIN: case MOTU_CTRL_MODE_UL_GAIN: val = ((val >> reg_shift) & 0x3f); break; default: debugOutput(DEBUG_LEVEL_VERBOSE, "unsupported mode %d\n", m_mode); return 0; } return val; } MeterControl::MeterControl(MotuDevice &parent, unsigned int ctrl_mask, unsigned int ctrl_shift) : MotuDiscreteCtrl(parent, ctrl_mask) { m_shift = ctrl_shift; validate(); } MeterControl::MeterControl(MotuDevice &parent, unsigned int ctrl_mask, unsigned int ctrl_shift, std::string name, std::string label, std::string descr) : MotuDiscreteCtrl(parent, ctrl_mask, name, label, descr) { m_shift = ctrl_shift; validate(); } void MeterControl::validate(void) { if ((m_register & (1<< m_shift)) == 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "Inconsistent mask/shift: 0x%08x/%d\n", m_register, m_shift); } } bool MeterControl::setValue(int v) { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for meter control 0x%08x/%d: %d\n", m_register, m_shift, v); // Need to get current register setting so we can preserve the parts not // being controlled by this object. m_register holds the mask for the // parts we're changing. val = m_parent.ReadRegister(MOTU_REG_896HD_METER_CONF) & ~m_register; val |= (v << m_shift) & m_register; m_parent.WriteRegister(MOTU_REG_896HD_METER_CONF, val); // Drivers under other OSes set MOTU_REG_896HD_METER_REG (0x0b1c) to // 0x0400 whenever MOTU_REG_896HD_METER_CONF (0x0b24) is changed. // There's no obvious reason why they do this, but since it's no hassle // we might as well do the same. m_parent.WriteRegister(MOTU_REG_896HD_METER_REG, 0x0400); return true; } int MeterControl::getValue() { unsigned int val; debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for meter control 0x%08x/%d\n", m_register, m_shift); // m_register holds the mask of the part of interest val = (m_parent.ReadRegister(MOTU_REG_896HD_METER_CONF) & m_register) >> m_shift; return val; } InfoElement::InfoElement(MotuDevice &parent, unsigned infotype) : MotuDiscreteCtrl(parent, infotype) { } InfoElement::InfoElement(MotuDevice &parent, unsigned infotype, std::string name, std::string label, std::string descr) : MotuDiscreteCtrl(parent, infotype, name, label, descr) { } bool InfoElement::setValue(int v) { /* This is a read-only field, so any call to setValue() is technically * an error. */ debugOutput(DEBUG_LEVEL_VERBOSE, "InfoElement (%d) is read-only\n", m_register); return false; } int InfoElement::getValue() { unsigned int val; signed int res = 0; switch (m_register) { case MOTU_INFO_MODEL: res = m_parent.m_motu_model; debugOutput(DEBUG_LEVEL_VERBOSE, "Model: %d\n", res); break; case MOTU_INFO_IS_STREAMING: val = m_parent.ReadRegister(MOTU_REG_ISOCTRL); /* Streaming is active if either bit 22 (Motu->PC streaming * enable) or bit 30 (PC->Motu streaming enable) is set. */ res = (val & 0x40400000) != 0; debugOutput(DEBUG_LEVEL_VERBOSE, "IsStreaming: %d (reg=%08x)\n", res, val); break; case MOTU_INFO_SAMPLE_RATE: res = m_parent.getSamplingFrequency(); debugOutput(DEBUG_LEVEL_VERBOSE, "SampleRate: %d\n", res); break; } return res; } } libffado-2.4.5/src/motu/motu_controls.h0000644000175000001440000002466614206145246017535 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2008-2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* DBus controls used with devices which utilise a pre-Mark3 mixer interface */ #include "debugmodule/debugmodule.h" #include "libcontrol/BasicElements.h" #include "libcontrol/MatrixMixer.h" namespace Motu { class MotuDevice; #define MOTU_CTRL_CHANNEL_FADER 0x00000001 #define MOTU_CTRL_CHANNEL_PAN 0x00000002 #define MOTU_CTRL_CHANNEL_SOLO 0x00000004 #define MOTU_CTRL_CHANNEL_MUTE 0x00000008 #define MOTU_CTRL_MIX_FADER 0x00000100 #define MOTU_CTRL_MIX_MUTE 0x00000200 #define MOTU_CTRL_MIX_DEST 0x00000400 #define MOTU_CTRL_METER 0x00001000 #define MOTU_CTRL_INPUT_UL_GAIN 0x00400000 /* Gain on Ultralite channels */ #define MOTU_CTRL_INPUT_PHASE_INV 0x00800000 #define MOTU_CTRL_INPUT_TRIMGAIN 0x01000000 #define MOTU_CTRL_INPUT_PAD 0x02000000 #define MOTU_CTRL_INPUT_LEVEL 0x04000000 #define MOTU_CTRL_INPUT_BOOST 0x08000000 #define MOTU_CTRL_PHONES_SRC 0x10000000 #define MOTU_CTRL_OPTICAL_MODE 0x20000000 #define MOTU_CTRL_STD_CHANNEL \ (MOTU_CTRL_CHANNEL_FADER|MOTU_CTRL_CHANNEL_PAN|\ MOTU_CTRL_CHANNEL_SOLO|MOTU_CTRL_CHANNEL_MUTE) #define MOTU_CTRL_STD_MIX \ (MOTU_CTRL_MIX_FADER|MOTU_CTRL_MIX_MUTE|\ MOTU_CTRL_MIX_DEST) #define MOTU_CTRL_TRAVELER_MIC_INPUT_CTRLS \ (MOTU_CTRL_INPUT_TRIMGAIN|MOTU_CTRL_INPUT_PAD) #define MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS \ (MOTU_CTRL_INPUT_LEVEL|MOTU_CTRL_INPUT_BOOST) #define MOTU_CTRL_ULTRALITE_INPUT_CTRLS \ (MOTU_CTRL_INPUT_UL_GAIN|MOTU_CTRL_INPUT_PHASE_INV) #define MOTU_CTRL_MASK_MUTE_VALUE 0x00010000 #define MOTU_CTRL_MASK_MUTE_SETENABLE 0x01000000 #define MOTU_CTRL_MASK_SOLO_VALUE 0x00020000 #define MOTU_CTRL_MASK_SOLO_SETENABLE 0x02000000 #define MOTU_CTRL_MASK_ANA5_INPUT_LEVEL 0x00000010 #define MOTU_CTRL_MASK_ANA6_INPUT_LEVEL 0x00000020 #define MOTU_CTRL_MASK_ANA7_INPUT_LEVEL 0x00000040 #define MOTU_CTRL_MASK_ANA8_INPUT_LEVEL 0x00000080 #define MOTU_CTRL_MODE_PAD 0x00000000 #define MOTU_CTRL_MODE_TRIMGAIN 0x00000001 #define MOTU_CTRL_MODE_UL_GAIN 0x00000002 #define MOTU_CTRL_MODE_PHASE_INV 0x00000003 #define MOTU_CTRL_METER_PEAKHOLD 0x00000001 #define MOTU_CTRL_METER_CLIPHOLD 0x00000002 #define MOTU_CTRL_METER_AESEBU_SRC 0x00000004 #define MOTU_CTRL_METER_PROG_SRC 0x00000008 #define MOTU_CTRL_DIR_IN 0x00000001 #define MOTU_CTRL_DIR_OUT 0x00000002 #define MOTU_CTRL_DIR_INOUT (MOTU_CTRL_DIR_IN | MOTU_CTRL_DIR_OUT) #define MOTU_INFO_MODEL 0x00000001 #define MOTU_INFO_IS_STREAMING 0x00000002 #define MOTU_INFO_SAMPLE_RATE 0x00000003 #define MOTU_CTRL_TRIMGAINPAD_MAX_CHANNEL 3 #define MOTU_CTRL_GAINPHASEINV_MAX_CHANNEL 9 /* A "register" value used to signify that a particular control in a matrix * mixer is not available on the current interface. */ #define MOTU_CTRL_NONE 0xffffffff class MotuDiscreteCtrl : public Control::Discrete { public: MotuDiscreteCtrl(MotuDevice &parent, unsigned int dev_reg); MotuDiscreteCtrl(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr); virtual bool setValue(int v) = 0; virtual int getValue() = 0; // default implementations virtual bool setValue(int idx, int v) {return setValue(v);}; virtual int getValue(int idx) {return getValue();}; virtual int getMinimum() {return 0;}; virtual int getMaximum() {return 0;}; protected: MotuDevice &m_parent; unsigned int m_register; }; class MotuBinarySwitch : public MotuDiscreteCtrl { public: MotuBinarySwitch(MotuDevice &parent, unsigned int dev_reg, unsigned int val_mask, unsigned int setenable_mask); MotuBinarySwitch(MotuDevice &parent, unsigned int dev_reg, unsigned int val_mask, unsigned int setenable_mask, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); protected: unsigned int m_value_mask; unsigned int m_setenable_mask; }; class ChannelFader : public MotuDiscreteCtrl { public: ChannelFader(MotuDevice &parent, unsigned int dev_reg); ChannelFader(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); }; class ChannelPan : public MotuDiscreteCtrl { public: ChannelPan(MotuDevice &parent, unsigned int dev_reg); ChannelPan(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); }; class MotuMatrixMixer : public Control::MatrixMixer { public: MotuMatrixMixer(MotuDevice &parent); MotuMatrixMixer(MotuDevice &parent, std::string name); virtual ~MotuMatrixMixer() {}; void addRowInfo(std::string name, unsigned int flags, unsigned int address); void addColInfo(std::string name, unsigned int flags, unsigned int address); uint32_t getCellRegister(const unsigned int row, const unsigned int col); virtual void show(); bool hasNames() const { return true; } bool canConnect() const { return false; } virtual std::string getRowName(const int row); virtual std::string getColName(const int col); virtual int canWrite( const int, const int ) { return true; } virtual int getRowCount(); virtual int getColCount(); // full map updates are unsupported virtual bool getCoefficientMap(int &) {return false;}; virtual bool storeCoefficientMap(int &) {return false;}; protected: struct sSignalInfo { std::string name; unsigned int flags; unsigned int address; }; std::vector m_RowInfo; std::vector m_ColInfo; MotuDevice& m_parent; }; class ChannelFaderMatrixMixer : public MotuMatrixMixer { public: ChannelFaderMatrixMixer(MotuDevice &parent); ChannelFaderMatrixMixer(MotuDevice &parent, std::string name); virtual double setValue(const int row, const int col, const double val); virtual double getValue(const int row, const int col); }; class ChannelPanMatrixMixer : public MotuMatrixMixer { public: ChannelPanMatrixMixer(MotuDevice &parent); ChannelPanMatrixMixer(MotuDevice &parent, std::string name); virtual double setValue(const int row, const int col, const double val); virtual double getValue(const int row, const int col); }; class ChannelBinSwMatrixMixer : public MotuMatrixMixer { public: ChannelBinSwMatrixMixer(MotuDevice &parent); ChannelBinSwMatrixMixer(MotuDevice &parent, std::string name, unsigned int val_mask, unsigned int setenable_mask); virtual double setValue(const int row, const int col, const double val); virtual double getValue(const int row, const int col); protected: unsigned int m_value_mask; unsigned int m_setenable_mask; }; class MixFader : public MotuDiscreteCtrl { public: MixFader(MotuDevice &parent, unsigned int dev_reg); MixFader(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); }; class MixMute : public MotuDiscreteCtrl { public: MixMute(MotuDevice &parent, unsigned int dev_reg); MixMute(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); }; class MixDest : public MotuDiscreteCtrl { public: MixDest(MotuDevice &parent, unsigned int dev_reg); MixDest(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); }; class PhonesSrc : public MotuDiscreteCtrl { public: PhonesSrc(MotuDevice &parent); PhonesSrc(MotuDevice &parent, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); }; class OpticalMode : public MotuDiscreteCtrl { public: OpticalMode(MotuDevice &parent, unsigned int dev_reg); OpticalMode(MotuDevice &parent, unsigned int dev_reg, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); }; class InputGainPadInv : public MotuDiscreteCtrl { public: InputGainPadInv(MotuDevice &parent, unsigned int channel, unsigned int mode); InputGainPadInv(MotuDevice &parent, unsigned int channel, unsigned int mode, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); protected: void validate(); unsigned int dev_register(); unsigned int m_mode; }; class MeterControl : public MotuDiscreteCtrl { public: MeterControl(MotuDevice &parent, unsigned int ctrl_mask, unsigned int ctrl_shift); MeterControl(MotuDevice &parent, unsigned int ctrl_mask, unsigned int ctrl_shift, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); protected: void validate(); unsigned int m_shift; }; class InfoElement : public MotuDiscreteCtrl { public: InfoElement(MotuDevice &parent, unsigned infotype); InfoElement(MotuDevice &parent, unsigned infotype, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); }; } libffado-2.4.5/src/motu/motu_mark3_controls.cpp0000644000175000001440000000200714206145246021146 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2008-2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* DBus controls associated with Mark3 mixer controls */ // This also includes motu_mark3_controls.h #include "motu_avdevice.h" namespace Motu { } libffado-2.4.5/src/motu/motu_mark3_controls.h0000644000175000001440000000216014206145246020613 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2008-2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* DBus controls associated with Mark3 mixer controls */ #include "debugmodule/debugmodule.h" #include "libcontrol/BasicElements.h" #include "libcontrol/MatrixMixer.h" namespace Motu { class MotuDevice; } libffado-2.4.5/src/motu/motu_mark3_mixerdefs.cpp0000644000175000001440000000210014206145246021263 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* This module contains definitions of mixers on devices which utilise the * "Mark3" mixer control protocol. */ #include "motu/motu_avdevice.h" #include "motu/motu_mark3_mixerdefs.h" namespace Motu { } libffado-2.4.5/src/motu/motu_mark3_mixerdefs.h0000644000175000001440000000212614206145246020740 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* Provide access to mixer details for devices which utilise the "Mark3" * mixer control protocol. */ #ifndef MOTU_MARK3_MIXERDEFS_H #define MOTU_MARK3_MIXERDEFS_H #include "motu/motu_avdevice.h" namespace Motu { } #endif libffado-2.4.5/src/motu/motu_mixer.cpp0000644000175000001440000003777114206145246017352 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* This file collects together everything associated with the management * of mixer controls in the MOTU device object. */ #include "motu/motu_avdevice.h" #include "motu/motu_mixerdefs.h" #include "motu/motu_mark3_mixerdefs.h" namespace Motu { bool MotuDevice::buildMixerAudioControls(void) { bool result = true; MotuMatrixMixer *fader_mmixer = NULL; MotuMatrixMixer *pan_mmixer = NULL; MotuMatrixMixer *solo_mmixer = NULL; MotuMatrixMixer *mute_mmixer = NULL; const struct MatrixMixBus *buses = NULL; const struct MatrixMixChannel *channels = NULL; unsigned int bus, ch, i; if (DevicesProperty[m_motu_model-1].mixer == NULL) { debugOutput(DEBUG_LEVEL_INFO, "No pre-Mark3 mixer defined for model %d\n", m_motu_model); return true; } else { buses = DevicesProperty[m_motu_model-1].mixer->mixer_buses; result = false; if (buses == NULL) { debugOutput(DEBUG_LEVEL_INFO, "No mixer buses defined for model %d\n", m_motu_model); } else result = true; channels = DevicesProperty[m_motu_model-1].mixer->mixer_channels; if (channels == NULL) { debugOutput(DEBUG_LEVEL_INFO, "No mixer channels defined for model %d\n", m_motu_model); } else result = true; if (DevicesProperty[m_motu_model-1].mixer->mixer_ctrl == NULL) { debugOutput(DEBUG_LEVEL_INFO, "No mixer device controls defined for model %d\n", m_motu_model); } else result = true; } if (result == false) { return true; } /* Create the top-level matrix mixers */ fader_mmixer = new ChannelFaderMatrixMixer(*this, "fader"); result &= m_MixerContainer->addElement(fader_mmixer); pan_mmixer = new ChannelPanMatrixMixer(*this, "pan"); result &= m_MixerContainer->addElement(pan_mmixer); solo_mmixer = new ChannelBinSwMatrixMixer(*this, "solo", MOTU_CTRL_MASK_SOLO_VALUE, MOTU_CTRL_MASK_SOLO_SETENABLE); result &= m_MixerContainer->addElement(solo_mmixer); mute_mmixer = new ChannelBinSwMatrixMixer(*this, "mute", MOTU_CTRL_MASK_MUTE_VALUE, MOTU_CTRL_MASK_MUTE_SETENABLE); result &= m_MixerContainer->addElement(mute_mmixer); for (bus=0; busn_mixer_buses; bus++) { fader_mmixer->addRowInfo(buses[bus].name, 0, buses[bus].address); pan_mmixer->addRowInfo(buses[bus].name, 0, buses[bus].address); solo_mmixer->addRowInfo(buses[bus].name, 0, buses[bus].address); mute_mmixer->addRowInfo(buses[bus].name, 0, buses[bus].address); } for (ch=0; chn_mixer_channels; ch++) { uint32_t flags = channels[ch].flags; if (flags & MOTU_CTRL_CHANNEL_FADER) fader_mmixer->addColInfo(channels[ch].name, 0, channels[ch].addr_ofs); if (flags & MOTU_CTRL_CHANNEL_PAN) pan_mmixer->addColInfo(channels[ch].name, 0, channels[ch].addr_ofs); if (flags & MOTU_CTRL_CHANNEL_SOLO) solo_mmixer->addColInfo(channels[ch].name, 0, channels[ch].addr_ofs); if (flags & MOTU_CTRL_CHANNEL_MUTE) mute_mmixer->addColInfo(channels[ch].name, 0, channels[ch].addr_ofs); flags &= ~(MOTU_CTRL_CHANNEL_FADER|MOTU_CTRL_CHANNEL_PAN|MOTU_CTRL_CHANNEL_SOLO|MOTU_CTRL_CHANNEL_MUTE); if (flags) { debugOutput(DEBUG_LEVEL_WARNING, "Control %s: unknown flag bits 0x%08x\n", channels[ch].name, flags); } } // Single non-matrixed mixer controls get added here. Channel controls are supported // here, but usually these will be a part of a matrix mixer. for (i=0; in_mixer_ctrls; i++) { const struct MixerCtrl *ctrl = &DevicesProperty[m_motu_model-1].mixer->mixer_ctrl[i]; unsigned int type; char name[100]; char label[100]; if (ctrl == NULL) { debugOutput(DEBUG_LEVEL_WARNING, "NULL control at index %d for model %d\n", i, m_motu_model); continue; } type = ctrl->type; if (type & MOTU_CTRL_CHANNEL_FADER) { snprintf(name, 100, "%s%s", ctrl->name, "fader"); snprintf(label,100, "%s%s", ctrl->label,"fader"); result &= m_MixerContainer->addElement( new ChannelFader(*this, ctrl->dev_register, name, label, ctrl->desc)); type &= ~MOTU_CTRL_CHANNEL_FADER; } if (type & MOTU_CTRL_CHANNEL_PAN) { snprintf(name, 100, "%s%s", ctrl->name, "pan"); snprintf(label,100, "%s%s", ctrl->label,"pan"); result &= m_MixerContainer->addElement( new ChannelPan(*this, ctrl->dev_register, name, label, ctrl->desc)); type &= ~MOTU_CTRL_CHANNEL_PAN; } if (type & MOTU_CTRL_CHANNEL_MUTE) { snprintf(name, 100, "%s%s", ctrl->name, "mute"); snprintf(label,100, "%s%s", ctrl->label,"mute"); result &= m_MixerContainer->addElement( new MotuBinarySwitch(*this, ctrl->dev_register, MOTU_CTRL_MASK_MUTE_VALUE, MOTU_CTRL_MASK_MUTE_SETENABLE, name, label, ctrl->desc)); type &= ~MOTU_CTRL_CHANNEL_MUTE; } if (type & MOTU_CTRL_CHANNEL_SOLO) { snprintf(name, 100, "%s%s", ctrl->name, "solo"); snprintf(label,100, "%s%s", ctrl->label,"solo"); result &= m_MixerContainer->addElement( new MotuBinarySwitch(*this, ctrl->dev_register, MOTU_CTRL_MASK_SOLO_VALUE, MOTU_CTRL_MASK_SOLO_SETENABLE, name, label, ctrl->desc)); type &= ~MOTU_CTRL_CHANNEL_SOLO; } if (type & MOTU_CTRL_MIX_FADER) { snprintf(name, 100, "%s%s", ctrl->name, "fader"); snprintf(label,100, "%s%s", ctrl->label,"fader"); result &= m_MixerContainer->addElement( new MixFader(*this, ctrl->dev_register, name, label, ctrl->desc)); type &= ~MOTU_CTRL_MIX_FADER; } if (type & MOTU_CTRL_MIX_MUTE) { snprintf(name, 100, "%s%s", ctrl->name, "mute"); snprintf(label,100, "%s%s", ctrl->label,"mute"); result &= m_MixerContainer->addElement( new MixMute(*this, ctrl->dev_register, name, label, ctrl->desc)); type &= ~MOTU_CTRL_MIX_MUTE; } if (type & MOTU_CTRL_MIX_DEST) { snprintf(name, 100, "%s%s", ctrl->name, "dest"); snprintf(label,100, "%s%s", ctrl->label,"dest"); result &= m_MixerContainer->addElement( new MixDest(*this, ctrl->dev_register, name, label, ctrl->desc)); type &= ~MOTU_CTRL_MIX_DEST; } if (type & MOTU_CTRL_INPUT_UL_GAIN) { snprintf(name, 100, "%s%s", ctrl->name, "trimgain"); snprintf(label,100, "%s%s", ctrl->label,"trimgain"); result &= m_MixerContainer->addElement( new InputGainPadInv(*this, ctrl->dev_register, MOTU_CTRL_MODE_UL_GAIN, name, label, ctrl->desc)); type &= ~MOTU_CTRL_INPUT_UL_GAIN; } if (type & MOTU_CTRL_INPUT_PHASE_INV) { snprintf(name, 100, "%s%s", ctrl->name, "invert"); snprintf(label,100, "%s%s", ctrl->label,"invert"); result &= m_MixerContainer->addElement( new InputGainPadInv(*this, ctrl->dev_register, MOTU_CTRL_MODE_PHASE_INV, name, label, ctrl->desc)); type &= ~MOTU_CTRL_INPUT_PHASE_INV; } if (type & MOTU_CTRL_INPUT_TRIMGAIN) { snprintf(name, 100, "%s%s", ctrl->name, "trimgain"); snprintf(label,100, "%s%s", ctrl->label,"trimgain"); result &= m_MixerContainer->addElement( new InputGainPadInv(*this, ctrl->dev_register, MOTU_CTRL_MODE_TRIMGAIN, name, label, ctrl->desc)); type &= ~MOTU_CTRL_INPUT_TRIMGAIN; } if (type & MOTU_CTRL_INPUT_PAD) { snprintf(name, 100, "%s%s", ctrl->name, "pad"); snprintf(label,100, "%s%s", ctrl->label,"pad"); result &= m_MixerContainer->addElement( new InputGainPadInv(*this, ctrl->dev_register, MOTU_CTRL_MODE_PAD, name, label, ctrl->desc)); type &= ~MOTU_CTRL_INPUT_PAD; } if (type & MOTU_CTRL_INPUT_LEVEL) { snprintf(name, 100, "%s%s", ctrl->name, "level"); snprintf(label,100, "%s%s", ctrl->label,"level"); result &= m_MixerContainer->addElement( new MotuBinarySwitch(*this, MOTU_REG_INPUT_LEVEL, 1<dev_register, 0, name, label, ctrl->desc)); type &= ~MOTU_CTRL_INPUT_LEVEL; } if (type & MOTU_CTRL_INPUT_BOOST) { snprintf(name, 100, "%s%s", ctrl->name, "boost"); snprintf(label,100, "%s%s", ctrl->label,"boost"); result &= m_MixerContainer->addElement( new MotuBinarySwitch(*this, MOTU_REG_INPUT_BOOST, 1<dev_register, 0, name, label, ctrl->desc)); type &= ~MOTU_CTRL_INPUT_BOOST; } if (type & MOTU_CTRL_PHONES_SRC) { snprintf(name, 100, "%s%s", ctrl->name, "src"); snprintf(label,100, "%s%s", ctrl->label,"src"); result &= m_MixerContainer->addElement( new PhonesSrc(*this, name, label, ctrl->desc)); type &= ~MOTU_CTRL_PHONES_SRC; } if (type & MOTU_CTRL_OPTICAL_MODE) { result &= m_MixerContainer->addElement( new OpticalMode(*this, ctrl->dev_register, ctrl->name, ctrl->label, ctrl->desc)); type &= ~MOTU_CTRL_OPTICAL_MODE; } if (type & MOTU_CTRL_METER) { if (ctrl->dev_register & MOTU_CTRL_METER_PEAKHOLD) { snprintf(name, 100, "%s%s", ctrl->name, "peakhold_time"); snprintf(label,100, "%s%s", ctrl->label,"peakhold time"); result &= m_MixerContainer->addElement( new MeterControl(*this, MOTU_METER_PEAKHOLD_MASK, MOTU_METER_PEAKHOLD_SHIFT, name, label, ctrl->desc)); } if (ctrl->dev_register & MOTU_CTRL_METER_CLIPHOLD) { snprintf(name, 100, "%s%s", ctrl->name, "cliphold_time"); snprintf(label,100, "%s%s", ctrl->label,"cliphold time"); result &= m_MixerContainer->addElement( new MeterControl(*this, MOTU_METER_CLIPHOLD_MASK, MOTU_METER_CLIPHOLD_SHIFT, name, label, ctrl->desc)); } if (ctrl->dev_register & MOTU_CTRL_METER_AESEBU_SRC) { snprintf(name, 100, "%s%s", ctrl->name, "aesebu_src"); snprintf(label,100, "%s%s", ctrl->label,"AESEBU source"); result &= m_MixerContainer->addElement( new MeterControl(*this, MOTU_METER_AESEBU_SRC_MASK, MOTU_METER_AESEBU_SRC_SHIFT, name, label, ctrl->desc)); } if (ctrl->dev_register & MOTU_CTRL_METER_PROG_SRC) { snprintf(name, 100, "%s%s", ctrl->name, "src"); snprintf(label,100, "%s%s", ctrl->label,"source"); result &= m_MixerContainer->addElement( new MeterControl(*this, MOTU_METER_PROG_SRC_MASK, MOTU_METER_PROG_SRC_SHIFT, name, label, ctrl->desc)); } type &= ~MOTU_CTRL_METER; } if (type) { debugOutput(DEBUG_LEVEL_WARNING, "Unknown mixer control type flag bits 0x%08x\n", ctrl->type); } } return result; } bool MotuDevice::buildMark3MixerAudioControls(void) { bool result = true; if (DevicesProperty[m_motu_model-1].m3mixer == NULL) { debugOutput(DEBUG_LEVEL_INFO, "No Mark3 mixer controls defined for model %d\n", m_motu_model); return false; } // FIXME: Details to come result = false; return result; } bool MotuDevice::buildMixer() { bool result = true; debugOutput(DEBUG_LEVEL_VERBOSE, "Building a MOTU mixer...\n"); destroyMixer(); // create the mixer object container m_MixerContainer = new Control::Container(this, "Mixer"); if (!m_MixerContainer) { debugError("Could not create mixer container...\n"); return false; } if (DevicesProperty[m_motu_model-1].mixer != NULL && DevicesProperty[m_motu_model-1].m3mixer != NULL) { debugError("MOTU model %d has pre-Mark3 and Mark3 mixer descriptions\n", m_motu_model); return false; } // Create and populate the top-level matrix mixers result = buildMixerAudioControls() || buildMark3MixerAudioControls(); /* Now add some general device information controls. These may yet * become device-specific if it turns out to be easier that way. */ result &= m_MixerContainer->addElement( new InfoElement(*this, MOTU_INFO_MODEL, "Info/Model", "Model identifier", "")); result &= m_MixerContainer->addElement( new InfoElement(*this, MOTU_INFO_IS_STREAMING, "Info/IsStreaming", "Is device streaming", "")); result &= m_MixerContainer->addElement( new InfoElement(*this, MOTU_INFO_SAMPLE_RATE, "Info/SampleRate", "Device sample rate", "")); if (!addElement(m_MixerContainer)) { debugWarning("Could not register mixer to device\n"); // clean up destroyMixer(); return false; } // Special controls m_ControlContainer = new Control::Container(this, "Control"); if (!m_ControlContainer) { debugError("Could not create control container...\n"); return false; } // Special controls get added here if (!result) { debugWarning("One or more device control elements could not be created."); // clean up those that couldn't be created destroyMixer(); return false; } if (!addElement(m_ControlContainer)) { debugWarning("Could not register controls to device\n"); // clean up destroyMixer(); return false; } return true; } bool MotuDevice::destroyMixer() { debugOutput(DEBUG_LEVEL_VERBOSE, "destroy mixer...\n"); if (m_MixerContainer == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "no mixer to destroy...\n"); return true; } if (!deleteElement(m_MixerContainer)) { debugError("Mixer present but not registered to the avdevice\n"); return false; } // remove and delete (as in free) child control elements m_MixerContainer->clearElements(true); delete m_MixerContainer; m_MixerContainer = NULL; // remove control container if (m_ControlContainer == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "no controls to destroy...\n"); return true; } if (!deleteElement(m_ControlContainer)) { debugError("Controls present but not registered to the avdevice\n"); return false; } // remove and delete (as in free) child control elements m_ControlContainer->clearElements(true); delete m_ControlContainer; m_ControlContainer = NULL; return true; } } libffado-2.4.5/src/motu/motu_mixerdefs.cpp0000644000175000001440000003640514206145246020205 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* This module contains definitions of mixers on devices which utilise the * original "pre-Mark3" mixer control protocol. */ #include "motu/motu_avdevice.h" #include "motu/motu_mixerdefs.h" namespace Motu { // Mixer registers const MatrixMixBus MixerBuses_Traveler[] = { {"Mix 1", 0x4000, }, {"Mix 2", 0x4100, }, {"Mix 3", 0x4200, }, {"Mix 4", 0x4300, }, }; const MatrixMixChannel MixerChannels_Traveler[] = { {"Analog 1", MOTU_CTRL_STD_CHANNEL, 0x0000, }, {"Analog 2", MOTU_CTRL_STD_CHANNEL, 0x0004, }, {"Analog 3", MOTU_CTRL_STD_CHANNEL, 0x0008, }, {"Analog 4", MOTU_CTRL_STD_CHANNEL, 0x000c, }, {"Analog 5", MOTU_CTRL_STD_CHANNEL, 0x0010, }, {"Analog 6", MOTU_CTRL_STD_CHANNEL, 0x0014, }, {"Analog 7", MOTU_CTRL_STD_CHANNEL, 0x0018, }, {"Analog 8", MOTU_CTRL_STD_CHANNEL, 0x001c, }, {"AES/EBU 1", MOTU_CTRL_STD_CHANNEL, 0x0020, }, {"AES/EBU 2", MOTU_CTRL_STD_CHANNEL, 0x0024, }, {"SPDIF 1", MOTU_CTRL_STD_CHANNEL, 0x0028, }, {"SPDIF 2", MOTU_CTRL_STD_CHANNEL, 0x002c, }, {"ADAT 1", MOTU_CTRL_STD_CHANNEL, 0x0030, }, {"ADAT 2", MOTU_CTRL_STD_CHANNEL, 0x0034, }, {"ADAT 3", MOTU_CTRL_STD_CHANNEL, 0x0038, }, {"ADAT 4", MOTU_CTRL_STD_CHANNEL, 0x003c, }, {"ADAT 5", MOTU_CTRL_STD_CHANNEL, 0x0040, }, {"ADAT 6", MOTU_CTRL_STD_CHANNEL, 0x0044, }, {"ADAT 7", MOTU_CTRL_STD_CHANNEL, 0x0048, }, {"ADAT 8", MOTU_CTRL_STD_CHANNEL, 0x004c, }, }; const MixerCtrl MixerCtrls_Traveler[] = { {"Mix1/Mix_", "Mix 1 ", "", MOTU_CTRL_STD_MIX, 0x0c20, }, {"Mix2/Mix_", "Mix 2 ", "", MOTU_CTRL_STD_MIX, 0x0c24, }, {"Mix3/Mix_", "Mix 3 ", "", MOTU_CTRL_STD_MIX, 0x0c28, }, {"Mix4/Mix_", "Mix 4 ", "", MOTU_CTRL_STD_MIX, 0x0c2c, }, {"Mainout_", "MainOut ", "", MOTU_CTRL_MIX_FADER, 0x0c0c, }, {"Phones_", "Phones ", "", MOTU_CTRL_MIX_FADER, 0x0c10, }, /* For mic/line input controls, the "register" is the zero-based channel number */ {"Control/Ana1_", "Analog 1 input ", "", MOTU_CTRL_TRAVELER_MIC_INPUT_CTRLS, 0}, {"Control/Ana2_", "Analog 2 input ", "", MOTU_CTRL_TRAVELER_MIC_INPUT_CTRLS, 1}, {"Control/Ana3_", "Analog 3 input ", "", MOTU_CTRL_TRAVELER_MIC_INPUT_CTRLS, 2}, {"Control/Ana4_", "Analog 4 input ", "", MOTU_CTRL_TRAVELER_MIC_INPUT_CTRLS, 3}, {"Control/Ana5_", "Analog 5 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 4}, {"Control/Ana6_", "Analog 6 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 5}, {"Control/Ana7_", "Analog 7 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 6}, {"Control/Ana8_", "Analog 8 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 7}, /* For phones source control, "register" is currently unused */ {"Control/Phones_", "Phones source", "", MOTU_CTRL_PHONES_SRC, 0}, /* For optical mode controls, the "register" is used to indicate direction */ {"Control/OpticalIn_mode", "Optical input mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_CTRL_DIR_IN}, {"Control/OpticalOut_mode", "Optical output mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_CTRL_DIR_OUT}, }; const MatrixMixBus MixerBuses_Ultralite[] = { {"Mix 1", 0x4000, }, {"Mix 2", 0x4100, }, {"Mix 3", 0x4200, }, {"Mix 4", 0x4300, }, }; const MatrixMixChannel MixerChannels_Ultralite[] = { {"Analog 1", MOTU_CTRL_STD_CHANNEL, 0x0000, }, {"Analog 2", MOTU_CTRL_STD_CHANNEL, 0x0004, }, {"Analog 3", MOTU_CTRL_STD_CHANNEL, 0x0008, }, {"Analog 4", MOTU_CTRL_STD_CHANNEL, 0x000c, }, {"Analog 5", MOTU_CTRL_STD_CHANNEL, 0x0010, }, {"Analog 6", MOTU_CTRL_STD_CHANNEL, 0x0014, }, {"Analog 7", MOTU_CTRL_STD_CHANNEL, 0x0018, }, {"Analog 8", MOTU_CTRL_STD_CHANNEL, 0x001c, }, {"AES/EBU 1", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, {"AES/EBU 2", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, {"SPDIF 1", MOTU_CTRL_STD_CHANNEL, 0x0020, }, {"SPDIF 2", MOTU_CTRL_STD_CHANNEL, 0x0024, }, {"ADAT 1", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, {"ADAT 2", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, {"ADAT 3", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, {"ADAT 4", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, {"ADAT 5", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, {"ADAT 6", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, {"ADAT 7", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, {"ADAT 8", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, }; const MixerCtrl MixerCtrls_Ultralite[] = { {"Mix1/Mix_", "Mix 1 ", "", MOTU_CTRL_STD_MIX, 0x0c20, }, {"Mix2/Mix_", "Mix 2 ", "", MOTU_CTRL_STD_MIX, 0x0c24, }, {"Mix3/Mix_", "Mix 3 ", "", MOTU_CTRL_STD_MIX, 0x0c28, }, {"Mix4/Mix_", "Mix 4 ", "", MOTU_CTRL_STD_MIX, 0x0c2c, }, {"Mainout_", "MainOut ", "", MOTU_CTRL_MIX_FADER, 0x0c0c, }, {"Phones_", "Phones ", "", MOTU_CTRL_MIX_FADER, 0x0c10, }, /* For mic/line input controls, the "register" is the zero-based channel number */ {"Control/Ana1_", "Analog 1 input ", "", MOTU_CTRL_ULTRALITE_INPUT_CTRLS, 0}, {"Control/Ana2_", "Analog 2 input ", "", MOTU_CTRL_ULTRALITE_INPUT_CTRLS, 1}, {"Control/Ana3_", "Analog 3 input ", "", MOTU_CTRL_ULTRALITE_INPUT_CTRLS, 2}, {"Control/Ana4_", "Analog 4 input ", "", MOTU_CTRL_ULTRALITE_INPUT_CTRLS, 3}, {"Control/Ana5_", "Analog 5 input ", "", MOTU_CTRL_ULTRALITE_INPUT_CTRLS, 4}, {"Control/Ana6_", "Analog 6 input ", "", MOTU_CTRL_ULTRALITE_INPUT_CTRLS, 5}, {"Control/Ana7_", "Analog 7 input ", "", MOTU_CTRL_ULTRALITE_INPUT_CTRLS, 6}, {"Control/Ana8_", "Analog 8 input ", "", MOTU_CTRL_ULTRALITE_INPUT_CTRLS, 7}, {"Control/Spdif1_", "SPDIF 1 input ", "", MOTU_CTRL_ULTRALITE_INPUT_CTRLS, 6}, {"Control/Spdif2_", "SPDIF 2 input ", "", MOTU_CTRL_ULTRALITE_INPUT_CTRLS, 7}, /* For phones source control, "register" is currently unused */ {"Control/Phones_", "Phones source", "", MOTU_CTRL_PHONES_SRC, 0}, /* For optical mode controls, the "register" is used to indicate direction */ {"Control/OpticalIn_mode", "Optical input mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_CTRL_DIR_IN}, {"Control/OpticalOut_mode", "Optical output mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_CTRL_DIR_OUT}, }; const MatrixMixBus MixerBuses_896HD[] = { {"Mix 1", 0x4000, }, {"Mix 2", 0x4100, }, {"Mix 3", 0x4200, }, {"Mix 4", 0x4300, }, }; const MatrixMixChannel MixerChannels_896HD[] = { {"Analog 1", MOTU_CTRL_STD_CHANNEL, 0x0000, }, {"Analog 2", MOTU_CTRL_STD_CHANNEL, 0x0004, }, {"Analog 3", MOTU_CTRL_STD_CHANNEL, 0x0008, }, {"Analog 4", MOTU_CTRL_STD_CHANNEL, 0x000c, }, {"Analog 5", MOTU_CTRL_STD_CHANNEL, 0x0010, }, {"Analog 6", MOTU_CTRL_STD_CHANNEL, 0x0014, }, {"Analog 7", MOTU_CTRL_STD_CHANNEL, 0x0018, }, {"Analog 8", MOTU_CTRL_STD_CHANNEL, 0x001c, }, {"AES/EBU 1", MOTU_CTRL_STD_CHANNEL, 0x0020, }, {"AES/EBU 2", MOTU_CTRL_STD_CHANNEL, 0x0024, }, {"SPDIF 1", MOTU_CTRL_STD_CHANNEL, 0x0048, }, {"SPDIF 2", MOTU_CTRL_STD_CHANNEL, 0x004c, }, {"ADAT 1", MOTU_CTRL_STD_CHANNEL, 0x0028, }, {"ADAT 2", MOTU_CTRL_STD_CHANNEL, 0x002c, }, {"ADAT 3", MOTU_CTRL_STD_CHANNEL, 0x0030, }, {"ADAT 4", MOTU_CTRL_STD_CHANNEL, 0x0034, }, {"ADAT 5", MOTU_CTRL_STD_CHANNEL, 0x0038, }, {"ADAT 6", MOTU_CTRL_STD_CHANNEL, 0x003c, }, {"ADAT 7", MOTU_CTRL_STD_CHANNEL, 0x0040, }, {"ADAT 8", MOTU_CTRL_STD_CHANNEL, 0x0044, }, }; const MixerCtrl MixerCtrls_896HD[] = { {"Mix1/Mix_", "Mix 1 ", "", MOTU_CTRL_STD_MIX, 0x0c20, }, {"Mix2/Mix_", "Mix 2 ", "", MOTU_CTRL_STD_MIX, 0x0c24, }, {"Mix3/Mix_", "Mix 3 ", "", MOTU_CTRL_STD_MIX, 0x0c28, }, {"Mix4/Mix_", "Mix 4 ", "", MOTU_CTRL_STD_MIX, 0x0c2c, }, {"Mainout_", "MainOut ", "", MOTU_CTRL_MIX_FADER, 0x0c0c, }, {"Phones_", "Phones ", "", MOTU_CTRL_MIX_FADER, 0x0c10, }, /* For phones source control, "register" is currently unused */ {"Control/Phones_", "Phones source", "", MOTU_CTRL_PHONES_SRC, 0}, /* For optical mode controls, the "register" is used to indicate direction */ {"Control/OpticalIn_mode", "Optical input mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_CTRL_DIR_IN}, {"Control/OpticalOut_mode", "Optical output mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_CTRL_DIR_OUT}, /* For meter controls the "register" indicates which meter controls are available */ {"Control/Meter_", "Meter ", "", MOTU_CTRL_METER, MOTU_CTRL_METER_PEAKHOLD | MOTU_CTRL_METER_CLIPHOLD | MOTU_CTRL_METER_AESEBU_SRC | MOTU_CTRL_METER_PROG_SRC}, }; const MatrixMixBus MixerBuses_828Mk2[] = { {"Mix 1", 0x4000, }, {"Mix 2", 0x4100, }, {"Mix 3", 0x4200, }, {"Mix 4", 0x4300, }, }; const MatrixMixChannel MixerChannels_828Mk2[] = { {"Analog 1", MOTU_CTRL_STD_CHANNEL, 0x0000, }, {"Analog 2", MOTU_CTRL_STD_CHANNEL, 0x0004, }, {"Analog 3", MOTU_CTRL_STD_CHANNEL, 0x0008, }, {"Analog 4", MOTU_CTRL_STD_CHANNEL, 0x000c, }, {"Analog 5", MOTU_CTRL_STD_CHANNEL, 0x0010, }, {"Analog 6", MOTU_CTRL_STD_CHANNEL, 0x0014, }, {"Analog 7", MOTU_CTRL_STD_CHANNEL, 0x0018, }, {"Analog 8", MOTU_CTRL_STD_CHANNEL, 0x001c, }, {"Mic 1", MOTU_CTRL_STD_CHANNEL, 0x0020, }, {"Mic 2", MOTU_CTRL_STD_CHANNEL, 0x0024, }, {"SPDIF 1", MOTU_CTRL_STD_CHANNEL, 0x0028, }, {"SPDIF 2", MOTU_CTRL_STD_CHANNEL, 0x002c, }, {"ADAT 1", MOTU_CTRL_STD_CHANNEL, 0x0030, }, {"ADAT 2", MOTU_CTRL_STD_CHANNEL, 0x0034, }, {"ADAT 3", MOTU_CTRL_STD_CHANNEL, 0x0038, }, {"ADAT 4", MOTU_CTRL_STD_CHANNEL, 0x003c, }, {"ADAT 5", MOTU_CTRL_STD_CHANNEL, 0x0040, }, {"ADAT 6", MOTU_CTRL_STD_CHANNEL, 0x0044, }, {"ADAT 7", MOTU_CTRL_STD_CHANNEL, 0x0048, }, {"ADAT 8", MOTU_CTRL_STD_CHANNEL, 0x004c, }, }; const MixerCtrl MixerCtrls_828Mk2[] = { {"Mix1/Mix_", "Mix 1 ", "", MOTU_CTRL_STD_MIX, 0x0c20, }, {"Mix2/Mix_", "Mix 2 ", "", MOTU_CTRL_STD_MIX, 0x0c24, }, {"Mix3/Mix_", "Mix 3 ", "", MOTU_CTRL_STD_MIX, 0x0c28, }, {"Mix4/Mix_", "Mix 4 ", "", MOTU_CTRL_STD_MIX, 0x0c2c, }, {"Mainout_", "MainOut ", "", MOTU_CTRL_MIX_FADER, 0x0c0c, }, {"Phones_", "Phones ", "", MOTU_CTRL_MIX_FADER, 0x0c10, }, /* For mic/line input controls, the "register" is the zero-based channel number */ {"Control/Ana1_", "Analog 1 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 0}, {"Control/Ana2_", "Analog 2 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 1}, {"Control/Ana3_", "Analog 3 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 2}, {"Control/Ana4_", "Analog 4 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 3}, {"Control/Ana5_", "Analog 5 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 4}, {"Control/Ana6_", "Analog 6 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 5}, {"Control/Ana7_", "Analog 7 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 6}, {"Control/Ana8_", "Analog 8 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 7}, /* For phones source control, "register" is currently unused */ {"Control/Phones_", "Phones source", "", MOTU_CTRL_PHONES_SRC, 0}, /* For optical mode controls, the "register" is used to indicate direction */ {"Control/OpticalIn_mode", "Optical input mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_CTRL_DIR_IN}, {"Control/OpticalOut_mode", "Optical output mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_CTRL_DIR_OUT}, }; const MatrixMixBus MixerBuses_8pre[] = { {"Mix 1", 0x4000, }, {"Mix 2", 0x4100, }, {"Mix 3", 0x4200, }, {"Mix 4", 0x4300, }, }; const MatrixMixChannel MixerChannels_8pre[] = { {"Analog 1", MOTU_CTRL_STD_CHANNEL, 0x0000, }, {"Analog 2", MOTU_CTRL_STD_CHANNEL, 0x0004, }, {"Analog 3", MOTU_CTRL_STD_CHANNEL, 0x0008, }, {"Analog 4", MOTU_CTRL_STD_CHANNEL, 0x000c, }, {"Analog 5", MOTU_CTRL_STD_CHANNEL, 0x0010, }, {"Analog 6", MOTU_CTRL_STD_CHANNEL, 0x0014, }, {"Analog 7", MOTU_CTRL_STD_CHANNEL, 0x0018, }, {"Analog 8", MOTU_CTRL_STD_CHANNEL, 0x001c, }, // // The Ultralite doesn't include AES/EBU or SPDIF mixer controls, but // "pad" mixer entries are required so the index of the ADAT controls // within the various matrix mixers remain unchanged compared to other // interfaces. This in turn means the python ffado-mixer code doesn't // have to deal with differing layouts within the matrix mixer controls. {"AES/EBU 1", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, {"AES/EBU 2", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, {"SPDIF 1", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, {"SPDIF 2", MOTU_CTRL_STD_CHANNEL, MOTU_CTRL_NONE, }, // {"ADAT 1", MOTU_CTRL_STD_CHANNEL, 0x0020, }, {"ADAT 2", MOTU_CTRL_STD_CHANNEL, 0x0024, }, {"ADAT 3", MOTU_CTRL_STD_CHANNEL, 0x0028, }, {"ADAT 4", MOTU_CTRL_STD_CHANNEL, 0x002c, }, {"ADAT 5", MOTU_CTRL_STD_CHANNEL, 0x0030, }, {"ADAT 6", MOTU_CTRL_STD_CHANNEL, 0x0034, }, {"ADAT 7", MOTU_CTRL_STD_CHANNEL, 0x0038, }, {"ADAT 8", MOTU_CTRL_STD_CHANNEL, 0x003c, }, }; const MixerCtrl MixerCtrls_8pre[] = { {"Mix1/Mix_", "Mix 1 ", "", MOTU_CTRL_STD_MIX, 0x0c20, }, {"Mix2/Mix_", "Mix 2 ", "", MOTU_CTRL_STD_MIX, 0x0c24, }, {"Mix3/Mix_", "Mix 3 ", "", MOTU_CTRL_STD_MIX, 0x0c28, }, {"Mix4/Mix_", "Mix 4 ", "", MOTU_CTRL_STD_MIX, 0x0c2c, }, /* For phones source control, "register" is currently unused */ {"Control/Phones_", "Phones source", "", MOTU_CTRL_PHONES_SRC, 0}, /* For optical mode controls, the "register" is used to indicate direction */ {"Control/OpticalIn_mode", "Optical input mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_CTRL_DIR_IN}, {"Control/OpticalOut_mode", "Optical output mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_CTRL_DIR_OUT}, }; const MixerCtrl MixerCtrls_828Mk1[] = { /* For optical mode controls, the "register" is used to indicate direction */ {"Control/OpticalIn_mode", "Optical input mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_CTRL_DIR_IN}, {"Control/OpticalOut_mode", "Optical output mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_CTRL_DIR_OUT}, }; const MotuMixer Mixer_Traveler = MOTUMIXER( MixerCtrls_Traveler, MixerBuses_Traveler, MixerChannels_Traveler); const MotuMixer Mixer_Ultralite = MOTUMIXER( MixerCtrls_Ultralite, MixerBuses_Ultralite, MixerChannels_Ultralite); const MotuMixer Mixer_828Mk2 = MOTUMIXER( MixerCtrls_828Mk2, MixerBuses_828Mk2, MixerChannels_828Mk2); const MotuMixer Mixer_896HD = MOTUMIXER( MixerCtrls_896HD, MixerBuses_896HD, MixerChannels_896HD); const MotuMixer Mixer_8pre = MOTUMIXER( MixerCtrls_8pre, MixerBuses_8pre, MixerChannels_8pre); // Since we don't have channel or bus lists yet, the mixer definition for // the 828Mk1 must be done without the use of the MOTUMIXER() macro. const MotuMixer Mixer_828Mk1 = { MixerCtrls_828Mk1, N_ELEMENTS(MixerCtrls_828Mk1), NULL, 0, NULL, 0}; } libffado-2.4.5/src/motu/motu_mixerdefs.h0000644000175000001440000000247014206145246017645 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* Provide access to mixer details for devices which utilise the * original "pre-Mark3" mixer control protocol. */ #ifndef MOTU_MIXERDEFS_H #define MOTU_MIXERDEFS_H #include "motu/motu_avdevice.h" namespace Motu { extern const MotuMixer Mixer_Traveler; extern const MotuMixer Mixer_Ultralite; extern const MotuMixer Mixer_828Mk2; extern const MotuMixer Mixer_896HD; extern const MotuMixer Mixer_8pre; extern const MotuMixer Mixer_828Mk1; } #endif libffado-2.4.5/src/oxford/0000755000175000001440000000000014206145613014752 5ustar jwoitheuserslibffado-2.4.5/src/oxford/oxford_device.cpp0000644000175000001440000002356314206145246020311 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Oxford Semiconductor OXFW970 and OXFW971 support */ #include "devicemanager.h" #include "oxford_device.h" #include "libavc/general/avc_plug.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libstreaming/amdtp-oxford/AmdtpOxfordReceiveStreamProcessor.h" #include "libstreaming/amdtp/AmdtpTransmitStreamProcessor.h" #include "libstreaming/amdtp/AmdtpPort.h" #include "libstreaming/amdtp/AmdtpPortInfo.h" #include using namespace std; namespace Oxford { Device::Device(DeviceManager& d, ffado_smartptr( configRom )) : GenericAVC::Device( d, configRom) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Oxford::Device (NodeID %d)\n", getConfigRom().getNodeId() ); m_fixed_clocksource.type = FFADODevice::eCT_Internal; m_fixed_clocksource.valid = true; m_fixed_clocksource.locked = true; m_fixed_clocksource.id = 0; m_fixed_clocksource.slipping = false; m_fixed_clocksource.description = "Internal"; } Device::~Device() { } void Device::showDevice() { debugOutput(DEBUG_LEVEL_VERBOSE, "This is a Oxford::Device\n"); GenericAVC::Device::showDevice(); } bool Device::probe( Util::Configuration& c, ConfigRom& configRom, bool generic ) { if(generic) { // heuristic yet that can tell us whether it's oxford based return false; } else { unsigned int vendorId = configRom.getNodeVendorId(); unsigned int modelId = configRom.getModelId(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); return c.isValid(vme) && vme.driver == Util::Configuration::eD_Oxford; } } bool Device::discover() { Util::MutexLockHelper lock(m_DeviceMutex); unsigned int vendorId = getConfigRom().getNodeVendorId(); unsigned int modelId = getConfigRom().getModelId(); Util::Configuration &c = getDeviceManager().getConfiguration(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); if (c.isValid(vme) && vme.driver == Util::Configuration::eD_Oxford) { debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", vme.vendor_name.c_str(), vme.model_name.c_str()); } else { debugWarning("Using Oxford AV/C support for unsupported device '%s %s'\n", getConfigRom().getVendorName().c_str(), getConfigRom().getModelName().c_str()); } // do the actual discovery if ( !Unit::discover() ) { debugError( "Could not discover unit\n" ); return false; } // the FCA202 has only an audio subunit if((getAudioSubunit( 0 ) == NULL)) { debugError( "Unit doesn't have an Audio subunit.\n"); return false; } return true; } FFADODevice * Device::createDevice(DeviceManager& d, ffado_smartptr( configRom )) { unsigned int vendorId = configRom->getNodeVendorId(); switch(vendorId) { default: return new Device(d, configRom ); } } std::vector Device::getSupportedSamplingFrequencies() { std::vector frequencies; frequencies.push_back(44100); frequencies.push_back(48000); frequencies.push_back(96000); return frequencies; } FFADODevice::ClockSourceVector Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; r.push_back(m_fixed_clocksource); return r; } bool Device::setActiveClockSource(ClockSource s) { // can't change, hence only succeed when identical return s.id == m_fixed_clocksource.id; } FFADODevice::ClockSource Device::getActiveClockSource() { return m_fixed_clocksource; } bool Device::prepare() { bool snoopMode=false; Util::MutexLockHelper lock(m_DeviceMutex); if(!getOption("snoopMode", snoopMode)) { debugWarning("Could not retrieve snoopMode parameter, defauling to false\n"); } /////////// // get plugs AVC::Plug* inputPlug = getPlugById( m_pcrPlugs, AVC::Plug::eAPD_Input, 0 ); if ( !inputPlug ) { debugError( "setSampleRate: Could not retrieve iso input plug 0\n" ); return false; } AVC::Plug* outputPlug = getPlugById( m_pcrPlugs, AVC::Plug::eAPD_Output, 0 ); if ( !outputPlug ) { debugError( "setSampleRate: Could not retrieve iso output plug 0\n" ); return false; } // get the device specific and/or global SP configuration Util::Configuration &config = getDeviceManager().getConfiguration(); // base value is the config.h value float recv_sp_dll_bw = STREAMPROCESSOR_DLL_BW_HZ; float xmit_sp_dll_bw = STREAMPROCESSOR_DLL_BW_HZ; int xmit_max_cycles_early_transmit = AMDTP_MAX_CYCLES_TO_TRANSMIT_EARLY; int xmit_transfer_delay = AMDTP_TRANSMIT_TRANSFER_DELAY; int xmit_min_cycles_before_presentation = AMDTP_MIN_CYCLES_BEFORE_PRESENTATION; // we can override that globally config.getValueForSetting("streaming.common.recv_sp_dll_bw", recv_sp_dll_bw); config.getValueForSetting("streaming.common.xmit_sp_dll_bw", xmit_sp_dll_bw); config.getValueForSetting("streaming.amdtp.xmit_max_cycles_early_transmit", xmit_max_cycles_early_transmit); config.getValueForSetting("streaming.amdtp.xmit_transfer_delay", xmit_transfer_delay); config.getValueForSetting("streaming.amdtp.xmit_min_cycles_before_presentation", xmit_min_cycles_before_presentation); // or override in the device section uint32_t vendorid = getConfigRom().getNodeVendorId(); uint32_t modelid = getConfigRom().getModelId(); config.getValueForDeviceSetting(vendorid, modelid, "recv_sp_dll_bw", recv_sp_dll_bw); config.getValueForDeviceSetting(vendorid, modelid, "xmit_sp_dll_bw", xmit_sp_dll_bw); config.getValueForDeviceSetting(vendorid, modelid, "xmit_max_cycles_early_transmit", xmit_max_cycles_early_transmit); config.getValueForDeviceSetting(vendorid, modelid, "xmit_transfer_delay", xmit_transfer_delay); config.getValueForDeviceSetting(vendorid, modelid, "xmit_min_cycles_before_presentation", xmit_min_cycles_before_presentation); // initialize the SP's debugOutput( DEBUG_LEVEL_VERBOSE, "Initializing receive processor...\n"); // create & add streamprocessors Streaming::StreamProcessor *p; if ( outputPlug->getNrOfChannels() == 0 ) { debugError("Receive plug has no channels\n"); return false; } p = new Streaming::AmdtpOxfordReceiveStreamProcessor(*this, outputPlug->getNrOfChannels()); if(!p->init()) { debugFatal("Could not initialize receive processor!\n"); delete p; return false; } if (!addPlugToProcessor(*outputPlug, p, Streaming::Port::E_Capture)) { debugFatal("Could not add plug to processor!\n"); delete p; return false; } if(!p->setDllBandwidth(recv_sp_dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete p; return false; } m_receiveProcessors.push_back(p); // do the transmit processor debugOutput( DEBUG_LEVEL_VERBOSE, "Initializing transmit processor%s...\n", (snoopMode?" in snoop mode":"")); if (snoopMode) { // we are snooping, so this is receive too. p=new Streaming::AmdtpOxfordReceiveStreamProcessor(*this, inputPlug->getNrOfChannels()); } else { Streaming::AmdtpTransmitStreamProcessor * t; t=new Streaming::AmdtpTransmitStreamProcessor(*this, inputPlug->getNrOfChannels()); // NOTE: the oxford devices don't allow payload, otherwise they generate distorted sound // if this generates a compile error it means you try to compile something that won't work t->sendPayloadForNoDataPackets(false); // transmit control parameters t->setMaxCyclesToTransmitEarly(xmit_max_cycles_early_transmit); t->setTransferDelay(xmit_transfer_delay); t->setMinCyclesBeforePresentation(xmit_min_cycles_before_presentation); p=t; } if(!p->init()) { debugFatal("Could not initialize transmit processor %s!\n", (snoopMode?" in snoop mode":"")); delete p; return false; } if (snoopMode) { if (!addPlugToProcessor(*inputPlug, p, Streaming::Port::E_Capture)) { debugFatal("Could not add plug to processor!\n"); return false; } if(!p->setDllBandwidth(recv_sp_dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete p; return false; } } else { if (!addPlugToProcessor(*inputPlug, p, Streaming::Port::E_Playback)) { debugFatal("Could not add plug to processor!\n"); return false; } if(!p->setDllBandwidth(xmit_sp_dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete p; return false; } } // we put this SP into the transmit SP vector, // no matter if we are in snoop mode or not // this allows us to find out what direction // a certain stream should have. m_transmitProcessors.push_back(p); return true; } } // Oxford libffado-2.4.5/src/oxford/oxford_device.h0000644000175000001440000000341214206145246017745 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef OXFORD_DEVICE_H #define OXFORD_DEVICE_H #include "debugmodule/debugmodule.h" #include "genericavc/avc_avdevice.h" #include #include "libutil/Mutex.h" class ConfigRom; class Ieee1394Service; namespace Oxford { class Device : public GenericAVC::Device { public: Device( DeviceManager& d, ffado_smartptr( configRom ) ); virtual ~Device(); static bool probe( Util::Configuration&, ConfigRom& configRom, bool generic = false ); static FFADODevice * createDevice( DeviceManager& d, ffado_smartptr( configRom )); virtual bool discover(); virtual void showDevice(); virtual std::vector getSupportedSamplingFrequencies(); virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); virtual bool prepare(); private: ClockSource m_fixed_clocksource; }; } // namespace Oxford #endif libffado-2.4.5/src/rme/0000755000175000001440000000000014206145613014234 5ustar jwoitheuserslibffado-2.4.5/src/rme/fireface_def.h0000644000175000001440000010156714206145246017003 0ustar jwoitheusers/* * Copyright (C) 2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* This file contains definitions relating to the RME Fireface interfaces * (Fireface 400 and Fireface 800). Naming convention: * RME_FF_ identifier applies to both FF400 and FF800 * RME_FF400_ identifier specific to the FF400 * RME_FF800_ identifier specific to the FF800 */ #ifndef _FIREFACE_DEF #define _FIREFACE_DEF /* The maximum number of channels supported by each device */ #define RME_FF400_MAX_CHANNELS 18 #define RME_FF800_MAX_CHANNELS 28 /* Boundaries between the speed multipliers */ #define MIN_SPEED 30000 #define MIN_DOUBLE_SPEED 56000 #define MIN_QUAD_SPEED 112000 #define MAX_SPEED 210000 // A flag used to indicate the use of a 800 Mbps bus speed to various // streaming registers of the FF800. #define RME_FF800_STREAMING_SPEED_800 0x800 /* The Command Buffer Address (CBA) is different for the two interfaces */ #define RME_FF400_CMD_BUFFER 0x80100500 #define RME_FF800_CMD_BUFFER 0xfc88f000 // Offsets for registers at fixed offsets from the device's command buffer // address #define RME_FF_DDS_SRATE_OFS (0*4) #define RME_FF_CONF1_OFS (5*4) #define RME_FF_CONF2_OFS (6*4) #define RME_FF_CONF3_OFS (7*4) #define RME_FF400_FLASH_CMD_OFS (8*4) // Write only #define RME_FF400_FLASH_STAT_OFS (8*4) // Read only /* General register definitions */ #define RME_FF400_CONF_REG (RME_FF400_CMD_BUFFER + RME_FF_CONF1_OFS) #define RME_FF800_CONF_REG (RME_FF800_CMD_BUFFER + RME_FF_CONF1_OFS) #define RME_FF400_STREAM_INIT_REG (RME_FF400_CMD_BUFFER) // 5 quadlets wide #define RME_FF400_STREAM_INIT_SIZE 5 // Size in quadlets #define RME_FF400_STREAM_SRATE (RME_FF400_CMD_BUFFER) #define RME_FF400_STREAM_CONF0 (RME_FF400_CMD_BUFFER+4) #define RME_FF400_STREAM_CONF1 (RME_FF400_CMD_BUFFER+8) #define RME_FF800_STREAM_INIT_REG 0x20000001cLL // 3 quadlets wide #define RME_FF800_STREAM_INIT_SIZE 3 // Size in quadlets #define RME_FF800_STREAM_SRATE 0x20000001cLL #define RME_FF800_STREAM_CONF0 (0x20000001cLL+4) #define RME_FF800_STREAM_CONF1 (0x20000001cLL+8) #define RME_FF400_STREAM_START_REG (RME_FF400_CMD_BUFFER + 0x000c) // 1 quadlet #define RME_FF800_STREAM_START_REG 0x200000028LL // 1 quadlet #define RME_FF400_STREAM_END_REG (RME_FF400_CMD_BUFFER + 0x0004) // 4 quadlets wide #define RME_FF400_STREAM_END_SIZE 4 // Size in quadlets #define RME_FF800_STREAM_END_REG 0x200000034LL // 3 quadlets wide #define RME_FF800_STREAM_END_SIZE 3 // Size in quadlets #define RME_FF800_HOST_LED_REG 0x200000324LL #define RME_FF800_REVISION_REG 0x200000100LL #define RME_FF_CHANNEL_MUTE_MASK 0x801c0000 // Write only #define RME_FF_STATUS_REG0 0x801c0000 // Read only #define RME_FF_STATUS_REG1 0x801c0004 // Read only #define RME_FF_STATUS_REG2 0x801c0008 #define RME_FF_STATUS_REG3 0x801c001c #define RME_FF_OUTPUT_REC_MASK 0x801c0080 // Write only #define RME_FF_MIXER_RAM 0x80080000 #define RME_FF_TCO_READ_REG 0x801f0000 // FF800 only #define RME_FF_TCO_WRITE_REG 0x801f0020 // FF800 only #define RME_FF400_GAIN_REG 0x801c0180 #define RME_FF400_MIDI_HIGH_ADDR 0x801003f4 /* Types of controls in the matrix mixer */ #define RME_FF_MM_INPUT 0x0000 #define RME_FF_MM_PLAYBACK 0x0001 #define RME_FF_MM_OUTPUT 0x0002 /* Addresses of various blocks in memory-mapped flash */ #define RME_FF400_FLASH_SETTINGS_ADDR 0x00060000 #define RME_FF400_FLASH_MIXER_VOLUME_ADDR 0x00070000 #define RME_FF400_FLASH_MIXER_PAN_ADDR 0x00070800 #define RME_FF400_FLASH_MIXER_HW_ADDR 0x00071000 /* Hardware volume settings, MIDI enable, submix */ #define RME_FF800_FLASH_MIXER_SHADOW_ADDR 0x3000e0000LL #define RME_FF800_FLASH_MIXER_SHADOW_SIZE 0x00002000 #define RME_FF800_FLASH_MIXER_VOLUME_ADDR 0x3000e2000LL #define RME_FF800_FLASH_MIXER_PAN_ADDR 0x3000e2800LL #define RME_FF800_FLASH_MIXER_HW_ADDR 0x3000e3000LL /* H/w volume settings, MIDI enable, submix */ #define RME_FF800_FLASH_SETTINGS_ADDR 0x3000f0000LL #define RME_FF_FLASH_MIXER_ARRAY_SIZE 0x00000800 // In bytes /* Flash control registers */ #define RME_FF400_FLASH_BLOCK_ADDR_REG 0x80100288 #define RME_FF400_FLASH_BLOCK_SIZE_REG 0x8010028c #define RME_FF400_FLASH_CMD_REG (RME_FF400_CMD_BUFFER + RME_FF400_FLASH_CMD_OFS) #define RME_FF400_FLASH_STAT_REG (RME_FF400_CMD_BUFFER + RME_FF400_FLASH_STAT_OFS) #define RME_FF400_FLASH_WRITE_BUFFER 0x80100290 #define RME_FF400_FLASH_READ_BUFFER 0x80100290 /* Flash erase control registers on the FF800 */ #define RME_FF800_FLASH_ERASE_VOLUME_REG 0x3fffffff4LL #define RME_FF800_FLASH_ERASE_SETTINGS_REG 0x3fffffff0LL #define RME_FF800_FLASH_ERASE_FIRMWARE_REG 0x3fffffff8LL #define RME_FF800_FLASH_ERASE_CONFIG_REG 0x3fffffffcLL /* Flash erase command values for the FF400 */ #define RME_FF400_FLASH_CMD_WRITE 0x00000001 #define RME_FF400_FLASH_CMD_READ 0x00000002 #define RME_FF400_FLASH_CMD_ERASE_VOLUME 0x0000000e #define RME_FF400_FLASH_CMD_ERASE_SETTINGS 0x0000000d #define RME_FF400_FLASH_CMD_ERASE_CONFIG 0x0000000c #define RME_FF400_FLASH_CMD_GET_REVISION 0x0000000f /* Flags for use with erase_flash() */ #define RME_FF_FLASH_ERASE_VOLUME 1 #define RME_FF_FLASH_ERASE_SETTINGS 2 #define RME_FF_FLASH_ERASE_CONFIG 3 #define RME_FF_FLASH_SECTOR_SIZE 256 // In bytes #define RME_FF_FLASH_SECTOR_SIZE_QUADS (RME_FF_FLASH_SECTOR_SIZE/4) #define RME_FF_FLASH_0DB_VOL_VALUE 0x323 /* Defines for components of the control registers */ // Configuration register 0 #define CR0_PHANTOM_MIC0 0x00000001 #define CR0_PHANTOM_MIC2 0x00000002 #define CR0_FILTER_FPGA 0x00000004 #define CR0_BIT01 0x00000002 // Use depends on model - see below #define CR0_BIT02 0x00000004 // Use depends on model - see below #define CR0_ILEVEL_FPGA_CTRL0 0x00000008 #define CR0_ILEVEL_FPGA_CTRL1 0x00000010 #define CR0_ILEVEL_FPGA_CTRL2 0x00000020 #define CR0_ZEROBIT06 0x00000040 #define CR0_PHANTOM_MIC1 0x00000080 #define CR0_BIT08 0x00000100 // Use depends on model - see below #define CR0_BIT09 0x00000200 // Use depends on model - see below #define CRO_OLEVEL_FPGA_CTRL_0 0x00000400 #define CRO_OLEVEL_FPGA_CTRL_1 0x00000800 #define CRO_OLEVEL_FPGA_CTRL_2 0x00001000 #define CR0_ZEROBIT13 0x00002000 #define CRO_ZEROBIT14 0x00004000 #define CRO_ZEROBIT15 0x00008000 #define CRO_PHLEVEL_CTRL0 0x00010000 #define CRO_PHLEVEL_CTRL1 0x00020000 #define CR0_FF400_PHANTOM_MIC0 CR0_PHANTOM_MIC0 #define CR0_FF400_PHANTOM_MIC1 CR0_PHANTOM_MIC1 #define CR0_FF400_CH3_PAD CR0_BIT08 #define CR0_FF400_CH3_INSTR CR0_BIT09 #define CR0_FF400_CH4_PAD CR0_BIT01 #define CR0_FF400_CH4_INSTR CR0_BIT02 #define CR0_FF800_PHANTOM_MIC7 CR0_PHANTOM_MIC0 #define CR0_FF800_PHANTOM_MIC8 CR0_PHANTOM_MIC1 #define CR0_FF800_PHANTOM_MIC9 CR0_BIT01 #define CR0_FF800_PHANTOM_MIC10 CR0_BIT08 #define CR0_FF800_FILTER_FPGA CR0_BIT02 #define CR0_FF800_DRIVE_FPGA CR0_BIT09 #define CR0_ILEVEL_FPGA_LOGAIN CR0_ILEVEL_FPGA_CTRL0 #define CR0_ILEVEL_FPGA_4dBU CR0_ILEVEL_FPGA_CTRL1 #define CR0_ILEVEL_FPGA_m10dBV CR0_ILEVEL_FPGA_CTRL2 #define CR0_OLEVEL_FPGA_HIGAIN CRO_OLEVEL_FPGA_CTRL_0 #define CR0_OLEVEL_FPGA_4dBU CRO_OLEVEL_FPGA_CTRL_1 #define CR0_OLEVEL_FPGA_m10dBV CRO_OLEVEL_FPGA_CTRL_2 #define CR0_PHLEVEL_4dBU 0 #define CRO_PHLEVEL_m10dBV CRO_PHLEVEL_CTRL0 #define CRO_PHLEVEL_HIGAIN CRO_PHLEVEL_CTRL1 // Configuration register 1 #define CR1_ILEVEL_CPLD_CTRL0 0x00000001 #define CR1_ILEVEL_CPLD_CTRL1 0x00000002 #define CR1_INPUT_OPT0_B 0x00000004 // Input optionset 0, option B #define CR1_OLEVEL_CPLD_CTRL0 0x00000008 #define CR1_OLEVEL_CPLD_CTRL1 0x00000010 #define CR1_INPUT_OPT1_A 0x00000020 // Input optionset 1, option A #define CR1_INPUT_OPT1_B 0x00000040 // Input optionset 1, option B #define CR1_INPUT_OPT2_A 0x00000080 // Input optionset 2, option A #define CR1_INPUT_OPT2_B 0x00000100 // Input optionset 2, option B #define CR1_INSTR_DRIVE 0x00000200 #define CR1_INPUT_OPT0_A1 0x00000400 // Input optionset 0, option A bit 1 #define CR1_INPUT_OPT0_A0 0x00000800 // Input optionset 0, option A bit 0 #define CR1_ILEVEL_CPLD_LOGAIN 0 #define CR1_ILEVEL_CPLD_4dBU CR1_ILEVEL_CPLD_CTRL1 #define CR1_ILEVEL_CPLD_m10dBV (CR1_ILEVEL_CPLD_CTRL0 | CR1_ILEVEL_CPLD_CTRL1) #define CR1_OLEVEL_CPLD_m10dBV CR1_OLEVEL_CPLD_CTRL0 #define CR1_OLEVEL_CPLD_HIGAIN CR1_OLEVEL_CPLD_CTRL1 #define CR1_OLEVEL_CPLD_4dBU (CR1_OLEVEL_CPLD_CTRL0 | CR1_OLEVEL_CPLD_CTRL1) #define CR1_FF800_INPUT7_FRONT CR1_INPUT_OPT1_A #define CR1_FF800_INPUT7_REAR CR1_INPUT_OPT1_B #define CR1_FF800_INPUT8_FRONT CR1_INPUT_OPT2_A #define CR1_FF800_INPUT8_REAR CR1_INPUT_OPT2_B #define CR1_FF400_INPUT3_INSTR CR1_INPUT_OPT1_B // To be confirmed #define CR1_FF400_INPUT3_PAD CR1_INPUT_OPT1_A // To be confirmed #define CR1_FF400_INPUT4_INSTR CR1_INPUT_OPT2_B // To be confirmed #define CR1_FF400_INPUT4_PAD CR1_INPUT_OPT2_A // To be confirmed // The input 1 "front" option is strange on the FF800 in that it is // indicated using two bits. The actual bit set depends, curiously enough, // on the "speaker emulation" (aka "filter") setting. How odd. #define CR1_FF800_INPUT1_FRONT CR1_INPUT_OPT0_A0 #define CR1_FF800_INPUT1_FRONT_WITH_FILTER CR1_INPUT_OPT0_A1 #define CR1_FF800_INPUT1_REAR CR1_INPUT_OPT0_B // Configuration register 2 #define CR2_CLOCKMODE_AUTOSYNC 0x00000000 #define CR2_CLOCKMODE_MASTER 0x00000001 #define CR2_FREQ0 0x00000002 #define CR2_FREQ1 0x00000004 #define CR2_DSPEED 0x00000008 #define CR2_QSSPEED 0x00000010 #define CR2_SPDIF_OUT_PRO 0x00000020 #define CR2_SPDIF_OUT_EMP 0x00000040 #define CR2_SPDIF_OUT_NONAUDIO 0x00000080 #define CR2_SPDIF_OUT_ADAT2 0x00000100 // Optical SPDIF on ADAT2 port #define CR2_SPDIF_IN_COAX 0x00000000 #define CR2_SPDIF_IN_ADAT2 0x00000200 // Optical SPDIF on ADAT2 port #define CR2_SYNC_REF0 0x00000400 #define CR2_SYNC_REF1 0x00000800 #define CR2_SYNC_REF2 0x00001000 #define CR2_WORD_CLOCK_1x 0x00002000 #define CR2_TOGGLE_TCO 0x00004000 // Normally set to 0 #define CR2_P12DB_AN0 0x00010000 // Disable soft-limiter. Normally set to 0 #define CR2_FF400_DISABLE_MIDI_TX_MASK 0x03000000 // Either or all bits will disable #define CR2_FF400_SELECT_MIDI_TX_ADDR_1 0x04000000 // FF400 tx to 0x'....'....'0000'0000'0000'0000 #define CR2_FF400_SELECT_MIDI_TX_ADDR_2 0x08000000 // FF400 tx to 0x'....'....'0000'0000'0000'0080 #define CR2_FF400_SELECT_MIDI_TX_ADDR_3 0x10000000 // FF400 tx to 0x'....'....'0000'0000'0000'0100 #define CR2_FF400_SELECT_MIDI_TX_ADDR_4 0x20000000 // FF400 tx to 0x'....'....'0000'0000'0000'0180 // '....'....' = content of RME_FF400_MIDI_HIGH_ADDR #define CR2_TMS 0x40000000 // Unit option, normally 0 #define CR2_DROP_AND_STOP 0x80000000 // Normally set to 1 #define CR2_SYNC_ADAT1 0x0 #define CR2_SYNC_ADAT2 (CR2_SYNC_REF0) #define CR2_SYNC_SPDIF (CR2_SYNC_REF0 | CR2_SYNC_REF1) #define CR2_SYNC_WORDCLOCK (CR2_SYNC_REF2) #define CR2_SYNC_TCO (CR2_SYNC_REF0 | CR2_SYNC_REF2) #define CR2_DISABLE_LIMITER CR2_P12DB_AN0 /* Defines for the status registers */ // Status register 0 #define SR0_ADAT1_LOCK 0x00000400 #define SR0_ADAT2_LOCK 0x00000800 #define SR0_ADAT1_SYNC 0x00001000 #define SR0_ADAT2_SYNC 0x00002000 #define SR0_SPDIF_F0 0x00004000 #define SR0_SPDIF_F1 0x00008000 #define SR0_SPDIF_F2 0x00010000 #define SR0_SPDIF_F3 0x00020000 #define SR0_SPDIF_SYNC 0x00040000 #define SR0_OVER 0x00080000 #define SR0_SPDIF_LOCK 0x00100000 #define SR0_SEL_SYNC_REF0 0x00400000 #define SR0_SEL_SYNC_REF1 0x00800000 #define SR0_SEL_SYNC_REF2 0x01000000 #define SR0_INP_FREQ0 0x02000000 #define SR0_INP_FREQ1 0x04000000 #define SR0_INP_FREQ2 0x08000000 #define SR0_INP_FREQ3 0x10000000 #define SR0_WCLK_SYNC 0x20000000 #define SR0_WCLK_LOCK 0x40000000 // The lowest 10 bits of SR0 represent sample_rate/250 if locked to an // external clock source. #define SR0_STREAMING_FREQ_MASK 0x000003ff #define SR0_ADAT1_STATUS_MASK (SR0_ADAT1_LOCK|SR0_ADAT1_SYNC) #define SR0_ADAT1_STATUS_NOLOCK 0 #define SR0_ADAT1_STATUS_LOCK SR0_ADAT1_LOCK #define SR0_ADAT1_STATUS_SYNC (SR0_ADAT1_LOCK|SR0_ADAT1_SYNC) #define SR0_ADAT2_STATUS_MASK (SR0_ADAT2_LOCK|SR0_ADAT2_SYNC) #define SR0_ADAT2_STATUS_NOLOCK 0 #define SR0_ADAT2_STATUS_LOCK SR0_ADAT2_LOCK #define SR0_ADAT2_STATUS_SYNC (SR0_ADAT2_LOCK|SR0_ADAT2_SYNC) #define SR0_SPDIF_STATUS_MASK (SR0_SPDIF_LOCK|SR0_SPDIF_SYNC) #define SR0_SPDIF_STATUS_NOLOCK 0 #define SR0_SPDIF_STATUS_LOCK SR0_SPDIF_LOCK #define SR0_SPDIF_STATUS_SYNC (SR0_SPDIF_LOCK|SR0_SPDIF_SYNC) #define SR0_WCLK_STATUS_MASK (SR0_WCLK_LOCK|SR0_WCLK_SYNC) #define SR0_WCLK_STATUS_NOLOCK 0 #define SR0_WCLK_STATUS_LOCK SR0_WCLK_LOCK #define SR0_WCLK_STATUS_SYNC (SR0_WCLK_LOCK|SR0_WCLK_SYNC) #define SR0_SPDIF_FREQ_MASK (SR0_SPDIF_F0|SR0_SPDIF_F1|SR0_SPDIF_F2|SR0_SPDIF_F3) #define SR0_SPDIF_FREQ_32k SR0_SPDIF_F0 #define SR0_SPDIF_FREQ_44k1 SR0_SPDIF_F1 #define SR0_SPDIF_FREQ_48k (SR0_SPDIF_F0|SR0_SPDIF_F1) #define SR0_SPDIF_FREQ_64k SR0_SPDIF_F2 #define SR0_SPDIF_FREQ_88k2 (SR0_SPDIF_F0|SR0_SPDIF_F2) #define SR0_SPDIF_FREQ_96k (SR0_SPDIF_F1|SR0_SPDIF_F2) #define SR0_SPDIF_FREQ_128k (SR0_SPDIF_F0|SR0_SPDIF_F1|SR0_SPDIF_F2) #define SR0_SPDIF_FREQ_176k4 SR0_SPDIF_F3 #define SR0_SPDIF_FREQ_192k (SR0_SPDIF_F0|SR0_SPDIF_F3) #define SR0_AUTOSYNC_SRC_MASK (SR0_SEL_SYNC_REF0|SR0_SEL_SYNC_REF1|SR0_SEL_SYNC_REF2) #define SR0_AUTOSYNC_SRC_ADAT1 0 #define SR0_AUTOSYNC_SRC_ADAT2 SR0_SEL_SYNC_REF0 #define SR0_AUTOSYNC_SRC_SPDIF (SR0_SEL_SYNC_REF0|SR0_SEL_SYNC_REF1) #define SR0_AUTOSYNC_SRC_WCLK SR0_SEL_SYNC_REF2 #define SR0_AUTOSYNC_SRC_TCO (SR0_SEL_SYNC_REF0|SR0_SEL_SYNC_REF2) #define SR0_AUTOSYNC_SRC_NONE (SR0_SEL_SYNC_REF1|SR0_SEL_SYNC_REF2) #define SR0_AUTOSYNC_FREQ_MASK (SR0_INP_FREQ0|SR0_INP_FREQ1|SR0_INP_FREQ2|SR0_INP_FREQ3) #define SR0_AUTOSYNC_FREQ_32k SR0_INP_FREQ0 #define SR0_AUTOSYNC_FREQ_44k1 SR0_INP_FREQ1 #define SR0_AUTOSYNC_FREQ_48k (SR0_INP_FREQ0|SR0_INP_FREQ1) #define SR0_AUTOSYNC_FREQ_64k SR0_INP_FREQ2 #define SR0_AUTOSYNC_FREQ_88k2 (SR0_INP_FREQ0|SR0_INP_FREQ2) #define SR0_AUTOSYNC_FREQ_96k (SR0_INP_FREQ1|SR0_INP_FREQ2) #define SR0_AUTOSYNC_FREQ_128k (SR0_INP_FREQ0|SR0_INP_FREQ1|SR0_INP_FREQ2) #define SR0_AUTOSYNC_FREQ_176k4 SR0_INP_FREQ3 #define SR0_AUTOSYNC_FREQ_192k (SR0_INP_FREQ0|SR0_INP_FREQ3) #define SR0_AUTOSYNC_FREQ_NONE 0 // Status register 1 #define SR1_CLOCK_MODE_MASTER 0x00000001 #define SR1_TCO_SYNC 0x00400000 #define SR1_TCO_LOCK 0x00800000 #define SR1_TCO_STATUS_MASK (SR1_TCO_LOCK|SR1_TCO_SYNC) #define SR1_TCO_STATUS_NOLOCK 0 #define SR1_TCO_STATUS_LOCK SR1_TCO_LOCK #define SR1_TCO_STATUS_SYNC (SR1_TCO_LOCK|SR1_TCO_SYNC) /* Structure used to store device settings in the device flash RAM. This * structure mirrors the layout in the Fireface's flash, so it cannot be * altered. Fields named as unused_* are not utilised at present. */ typedef struct { uint32_t unused_device_id; uint32_t unused_device_rev; uint32_t unused_asio_latency; uint32_t unused_samples_per_frame; uint32_t spdif_input_mode; uint32_t spdif_output_emphasis; uint32_t spdif_output_pro; uint32_t clock_mode; uint32_t spdif_output_nonaudio; uint32_t sync_ref; uint32_t spdif_output_mode; uint32_t unused_check_input; uint32_t unused_status; uint32_t unused_register[4]; uint32_t unused_iso_rx_channel; uint32_t unused_iso_tx_channel; uint32_t unused_timecode; uint32_t unused_device_type; uint32_t unused_number_of_devices; uint32_t tms; uint32_t unused_speed; uint32_t unused_channels_avail_hi; uint32_t unused_channels_avail_lo; uint32_t limit_bandwidth; uint32_t unused_bandwidth_allocated; uint32_t stop_on_dropout; uint32_t input_level; uint32_t output_level; uint32_t mic_plug_select[2]; // Front/rear select for FF800 ch 7/8 // [0] = phones level on FF400 uint32_t mic_phantom[4]; uint32_t instrument_plug_select; // Front/rear select for FF800 ch 1 uint32_t filter; uint32_t fuzz; uint32_t unused_sync_align; uint32_t unused_device_index; uint32_t unused_advanced_dialog; uint32_t sample_rate; uint32_t unused_interleaved; uint32_t unused_sn; uint32_t word_clock_single_speed; uint32_t unused_num_channels; uint32_t unused_dropped_samples; uint32_t p12db_an[10]; } FF_device_flash_settings_t; // Defines used to interpret device flash settings. The driver can read // these and use them to infer the appropriate values for the configuration // registers. The flash settings are also used directly by the device // at power up to define its initial state. Therefore it's important that // these settings correspond to the values expected by the device. #define FF_DEV_FLASH_INVALID 0xffffffff #define FF_DEV_FLASH_SPDIF_INPUT_COAX 0x00000001 #define FF_DEV_FLASH_SPDIF_INPUT_OPTICAL 0x00000000 #define FF_DEV_FLASH_SPDIF_OUTPUT_COAX 0x00000000 #define FF_DEV_FLASH_SPDIF_OUTPUT_OPTICAL 0x00000001 #define FF_DEV_FLASH_SPDIF_OUTPUT_EMPHASIS_ON 0x00000001 #define FF_DEV_FLASH_SPDIF_OUTPUT_PRO_ON 0x00000001 #define FF_DEV_FLASH_SPDIF_OUTPUT_NONAUDIO_ON 0x00000001 #define FF_DEV_FLASH_BWLIMIT_SEND_ALL_CHANNELS 0x00000000 #define FF_DEV_FLASH_BWLIMIT_NO_ADAT2 0x00000001 // FF800 only #define FF_DEV_FLASH_BWLIMIT_ANALOG_SPDIF_ONLY 0x00000002 #define FF_DEV_FLASH_BWLIMIT_ANALOG_ONLY 0x00000003 #define FF_DEV_FLASH_CLOCK_MODE_MASTER 0x00000000 #define FF_DEV_FLASH_CLOCK_MODE_AUTOSYNC 0x00000001 #define FF_DEV_FLASH_CLOCK_MODE_SLAVE 0x00000001 #define FF_DEV_FLASH_SYNCREF_WORDCLOCK 0x00000003 #define FF_DEV_FLASH_SYNCREF_ADAT1 0x00000000 #define FF_DEV_FLASH_SYNCREF_ADAT2 0x00000001 #define FF_DEV_FLASH_SYNCREF_SPDIF 0x00000002 #define FF_DEV_FLASH_SYNCREC_TCO 0x00000004 // To be confirmed #define FF_DEV_FLASH_ILEVEL_LOGAIN 0x00000000 #define FF_DEV_FLASH_ILEVEL_4dBU 0x00000002 #define FF_DEV_FLASH_ILEVEL_m10dBV 0x00000001 #define FF_DEV_FLASH_OLEVEL_HIGAIN 0x00000002 #define FF_DEV_FLASH_OLEVEL_4dBU 0x00000001 #define FF_DEV_FLASH_OLEVEL_m10dBV 0x00000000 #define FF_DEV_FLASH_MIC_PHANTOM_ON 0x00000001 #define FF_DEV_FLASH_SRATE_DDS_INACTIVE 0x00000000 #define FF_DEV_FLASH_WORD_CLOCK_1x 0x00000001 #define FF_DEV_FLASH_PLUG_SELECT_FRONT_REAR 0x00000002 #define FF_DEV_FLASH_PLUG_SELECT_FRONT 0x00000001 #define FF_DEV_FLASH_PLUG_SELECT_REAR 0x00000000 #define FF_MATRIXMIXER_SIZE (RME_FF800_MAX_CHANNELS*RME_FF800_MAX_CHANNELS) // Structure used by FFADO to keep track of the device status. This is // decoupled from any structures used directly by the device, so it can be // added to and ordered freely. When making changes to the device the // configuration registers must be all written to, so any function changing // a parameter must have access to the complete device status. typedef struct { uint32_t mic_phantom[4]; uint32_t spdif_input_mode; uint32_t spdif_output_emphasis; uint32_t spdif_output_pro; uint32_t spdif_output_nonaudio; uint32_t spdif_output_mode; uint32_t clock_mode; uint32_t sync_ref; uint32_t tms; uint32_t limit_bandwidth; uint32_t stop_on_dropout; uint32_t input_level; uint32_t output_level; uint32_t filter; uint32_t fuzz; uint32_t limiter; uint32_t sample_rate; uint32_t word_clock_single_speed; uint32_t ff400_input_pad[2]; // Channels 3/4, FF400 only uint32_t ff400_instr_input[2]; // Channels 3/4, FF400 only uint32_t phones_level; // Derived from fields in device flash uint32_t input_opt[3]; // Derived from fields in device flash // Other "settings" fields which are not necessarily stored in device flash int32_t amp_gains[22]; // FF400: gains of input/output amps int32_t input_faders[FF_MATRIXMIXER_SIZE]; int32_t playback_faders[FF_MATRIXMIXER_SIZE]; int32_t output_faders[RME_FF800_MAX_CHANNELS]; unsigned char input_mixerflags[FF_MATRIXMIXER_SIZE]; unsigned char playback_mixerflags[FF_MATRIXMIXER_SIZE]; unsigned char output_mixerflags[FF_MATRIXMIXER_SIZE]; } FF_software_settings_t; // Defines used to interpret the software settings structure. For now we // use the same values as used by the device flash settings to remove the // need for translation between reading the flash and copying it to the // software settings structure, but in principle different values could be // used given translation code. #define FF_SWPARAM_INVALID FF_DEV_FLASH_INVALID #define FF_SWPARAM_SPDIF_INPUT_COAX FF_DEV_FLASH_SPDIF_INPUT_COAX #define FF_SWPARAM_SPDIF_INPUT_OPTICAL FF_DEV_FLASH_SPDIF_INPUT_OPTICAL #define FF_SWPARAM_SPDIF_OUTPUT_COAX FF_DEV_FLASH_SPDIF_OUTPUT_COAX #define FF_SWPARAM_SPDIF_OUTPUT_OPTICAL FF_DEV_FLASH_SPDIF_OUTPUT_OPTICAL #define FF_SWPARAM_SPDIF_OUTPUT_EMPHASIS_ON FF_DEV_FLASH_SPDIF_OUTPUT_EMPHASIS_ON #define FF_SWPARAM_SPDIF_OUTPUT_PRO_ON FF_DEV_FLASH_SPDIF_OUTPUT_PRO_ON #define FF_SWPARAM_SPDIF_OUTPUT_NONAUDIO_ON FF_DEV_FLASH_SPDIF_OUTPUT_NONAUDIO_ON #define FF_SWPARAM_BWLIMIT_SEND_ALL_CHANNELS FF_DEV_FLASH_BWLIMIT_SEND_ALL_CHANNELS #define FF_SWPARAM_BWLIMIT_NO_ADAT2 FF_DEV_FLASH_BWLIMIT_NO_ADAT2 #define FF_SWPARAM_BWLIMIT_ANALOG_SPDIF_ONLY FF_DEV_FLASH_BWLIMIT_ANALOG_SPDIF_ONLY #define FF_SWPARAM_BWLIMIT_ANALOG_ONLY FF_DEV_FLASH_BWLIMIT_ANALOG_ONLY #define FF_SWPARAM_CLOCK_MODE_MASTER FF_DEV_FLASH_CLOCK_MODE_MASTER #define FF_SWPARAM_CLOCK_MODE_AUTOSYNC FF_DEV_FLASH_CLOCK_MODE_AUTOSYNC #define FF_SWPARAM_CLOCK_MODE_SLAVE FF_DEV_FLASH_CLOCK_MODE_SLAVE #define FF_SWPARAM_SYNCREF_WORDCLOCK FF_DEV_FLASH_SYNCREF_WORDCLOCK #define FF_SWPARAM_SYNCREF_ADAT1 FF_DEV_FLASH_SYNCREF_ADAT1 #define FF_SWPARAM_SYNCREF_ADAT2 FF_DEV_FLASH_SYNCREF_ADAT2 #define FF_SWPARAM_SYNCREF_SPDIF FF_DEV_FLASH_SYNCREF_SPDIF #define FF_SWPARAM_SYNCREC_TCO FF_DEV_FLASH_SYNCREC_TCO #define FF_SWPARAM_ILEVEL_LOGAIN FF_DEV_FLASH_ILEVEL_LOGAIN #define FF_SWPARAM_ILEVEL_4dBU FF_DEV_FLASH_ILEVEL_4dBU #define FF_SWPARAM_ILEVEL_m10dBV FF_DEV_FLASH_ILEVEL_m10dBV #define FF_SWPARAM_OLEVEL_HIGAIN FF_DEV_FLASH_OLEVEL_HIGAIN #define FF_SWPARAM_OLEVEL_4dBU FF_DEV_FLASH_OLEVEL_4dBU #define FF_SWPARAM_OLEVEL_m10dBV FF_DEV_FLASH_OLEVEL_m10dBV #define FF_SWPARAM_MIC_PHANTOM_ON FF_DEV_FLASH_MIC_PHANTOM_ON #define FF_SWPARAM_WORD_CLOCK_1x FF_DEV_FLASH_WORD_CLOCK_1x #define FF_SWPARAM_SRATE_DDS_INACTIVE FF_DEV_FLASH_SRATE_DDS_INACTIVE // // The following defines refer to fields in the software parameter record // which are derived from one or more fields in device flash. #define FF_SWPARAM_PHONESLEVEL_HIGAIN 0x00000000 #define FF_SWPARAM_PHONESLEVEL_4dBU 0x00000001 #define FF_SWPARAM_PHONESLEVEL_m10dBV 0x00000002 #define FF_SWPARAM_INPUT_OPT_B 0x00000001 #define FF_SWPARAM_INPUT_OPT_A 0x00000002 #define FF_SWPARAM_FF800_INPUT_OPT_FRONT FF_SWPARAM_INPUT_OPT_A #define FF_SWPARAM_FF800_INPUT_OPT_REAR FF_SWPARAM_INPUT_OPT_B // Flags for the "status" parameter of setInputInstrOpt() #define FF400_INSTR_OPT_ACTIVE 0x01 #define FF800_INSTR_OPT_FILTER 0x02 #define FF800_INSTR_OPT_FUZZ 0x04 #define FF800_INSTR_OPT_LIMITER 0x08 // Flags for the *_mixerflags fields #define FF_SWPARAM_MF_NORMAL 0x00 #define FF_SWPARAM_MF_MUTED 0x01 #define FF_SWPARAM_MF_INVERTED 0x02 // Inputs/playbacks only #define FF_SWPARAM_MF_REC 0x04 // Outputs only // Indices into the amp_gains array #define FF400_AMPGAIN_MIC1 0 #define FF400_AMPGAIN_MIC2 1 #define FF400_AMPGAIN_INPUT3 2 #define FF400_AMPGAIN_INPUT4 3 #define FF400_AMPGAIN_OUTPUT1 4 #define FF400_AMPGAIN_OUTPUT2 5 #define FF400_AMPGAIN_OUTPUT3 6 #define FF400_AMPGAIN_OUTPUT4 7 #define FF400_AMPGAIN_OUTPUT5 8 #define FF400_AMPGAIN_OUTPUT6 9 #define FF400_AMPGAIN_PHONES_L 10 #define FF400_AMPGAIN_PHONES_R 11 #define FF400_AMPGAIN_SPDIF1 12 #define FF400_AMPGAIN_SPDIF2 13 #define FF400_AMPGAIN_ADAT1_1 14 #define FF400_AMPGAIN_ADAT1_2 15 #define FF400_AMPGAIN_ADAT1_3 16 #define FF400_AMPGAIN_ADAT1_4 17 #define FF400_AMPGAIN_ADAT1_5 18 #define FF400_AMPGAIN_ADAT1_6 19 #define FF400_AMPGAIN_ADAT1_7 20 #define FF400_AMPGAIN_ADAT1_8 21 #define FF400_AMPGAIN_NUM 22 // The general Fireface state typedef struct { uint32_t is_streaming; uint32_t clock_mode; uint32_t autosync_source; uint32_t autosync_freq; uint32_t spdif_freq; uint32_t adat1_sync_status, adat2_sync_status; uint32_t spdif_sync_status; uint32_t wclk_sync_status, tco_sync_status; } FF_state_t; #define FF_STATE_CLOCKMODE_MASTER 0 #define FF_STATE_CLOCKMODE_AUTOSYNC 1 #define FF_STATE_AUTOSYNC_SRC_NOLOCK 0 #define FF_STATE_AUTOSYNC_SRC_ADAT1 1 #define FF_STATE_AUTOSYNC_SRC_ADAT2 2 #define FF_STATE_AUTOSYNC_SRC_SPDIF 3 #define FF_STATE_AUTOSYNC_SRC_WCLK 4 #define FF_STATE_AUTOSYNC_SRC_TCO 5 #define FF_STATE_SYNC_NOLOCK 0 #define FF_STATE_SYNC_LOCKED 1 #define FF_STATE_SYNC_SYNCED 2 // Data structure for the TCO (Time Code Option) state typedef struct { uint32_t input; uint32_t frame_rate; uint32_t word_clock; uint32_t sample_rate; uint32_t pull; uint32_t termination; uint32_t MTC; } FF_TCO_settings_t; // Defines used to configure selected quadlets of the TCO read space. Some // of these are also used when configuring the TCO. The byte indices // referenced in the define names are 0-based. // TCO quadlet 0 #define FF_TCO0_MTC 0x80000000 // TCO quadlet 1 #define FF_TCO1_TCO_lock 0x00000001 #define FF_TCO1_WORD_CLOCK_INPUT_RATE0 0x00000002 #define FF_TCO1_WORD_CLOCK_INPUT_RATE1 0x00000004 #define FF_TCO1_LTC_INPUT_VALID 0x00000008 #define FF_TCO1_WORD_CLOCK_INPUT_VALID 0x00000010 #define FF_TCO1_VIDEO_INPUT_NTSC 0x00000020 #define FF_TCO1_VIDEO_INPUT_PAL 0x00000040 #define FF_TCO1_SET_TC 0x00000100 #define FF_TCO1_SET_DROPFRAME 0x00000200 #define FF_TCO1_LTC_FORMAT0 0x00000400 #define FF_TCO1_LTC_FORMAT1 0x00000800 #define FF_TCO1_WORD_CLOCK_INPUT_1x 0 #define FF_TCO1_WORD_CLOCK_INPUT_2x FF_TCO1_WORD_CLOCK_INPUT_RATE0 #define FF_TCO1_WORD_CLOCK_INPUT_4x FF_TCO1_WORD_CLOCK_INPUT_RATE1 #define FF_TCO1_WORD_CLOCK_INPUT_MASK (FF_TCO1_WORD_CLOCK_INPUT_RATE0|FF_TCO1_WORD_CLOCK_INPUT_RATE1) #define FF_TCO1_VIDEO_INPUT_MASK (FF_TCO1_VIDEO_INPUT_NTSC|FF_TCO1_VIDEO_INPUT_PAL) #define FF_TC01_LTC_FORMAT_24fps 0 #define FF_TCO1_LTC_FORMAT_25fps FF_TCO1_LTC_FORMAT0 #define FF_TC01_LTC_FORMAT_29_97fps FF_TCO1_LTC_FORMAT1 #define FF_TCO1_LTC_FORMAT_29_97dpfs (FF_TCO1_LTC_FORMAT1|FF_TCO1_SET_DROPFRAME) #define FF_TCO1_LTC_FORMAT_30fps (FF_TCO1_LTC_FORMAT0|FF_TCO1_LTC_FORMAT1) #define FF_TCO1_LTC_FORMAT_30dfps (FF_TCO1_LTC_FORMAT0|FF_TCO1_LTC_FORMAT1|FF_TCO1_SET_DROPFRAME) #define FF_TCO1_LTC_FORMAT_MASK (FF_TCO1_LTC_FORMAT0|FF_TCO1_LTC_FORMAT1) // TCO quadlet 2 #define FF_TCO2_TC_RUN 0x00010000 #define FF_TCO2_WORD_CLOCK_CONV0 0x00020000 #define FF_TCO2_WORD_CLOCK_CONV1 0x00040000 #define FF_TCO2_NUM_DROPFRAMES0 0x00080000 // Unused #define FF_TCO2_NUM_DROPFRAMES1 0x00100000 // Unused #define FF_TCO2_SET_JAM_SYNC 0x00200000 #define FF_TCO2_SET_FLYWHEEL 0x00400000 #define FF_TCO2_SET_01_4 0x01000000 #define FF_TCO2_SET_PULLDOWN 0x02000000 #define FF_TCO2_SET_PULLUP 0x04000000 #define FF_TCO2_SET_FREQ 0x08000000 #define FF_TCO2_SET_TERMINATION 0x10000000 #define FF_TCO2_SET_INPUT0 0x20000000 #define FF_TCO2_SET_INPUT1 0x40000000 #define FF_TCO2_SET_FREQ_FROM_APP 0x80000000 #define FF_TCO2_WORD_CLOCK_CONV_1_1 0 #define FF_TCO2_WORD_CLOCK_CONV_44_48 FF_TCO2_WORD_CLOCK_CONV0 #define FF_TCO2_WORD_CLOCK_CONV_48_44 FF_TCO2_WORD_CLOCK_CONV1 #define FF_TCO2_PULL_0 0 #define FF_TCO2_PULL_UP_01 FF_TCO2_SET_PULLUP #define FF_TCO2_PULL_DOWN_01 FF_TCO2_SET_PULLDOWN #define FF_TCO2_PULL_UP_40 (FF_TCO2_SET_PULLUP|FF_TCO2_SET_01_4) #define FF_TCO2_PULL_DOWN_40 (FF_TCO2_SET_PULLDOWN|FF_TCO2_SET_01_4) #define FF_TCO2_INPUT_LTC FF_TCO2_SET_INPUT1 #define FF_TCO2_INPUT_VIDEO FF_TCO2_SET_INPUT0 #define FF_TCO2_INPUT_WORD_CLOCK 0 #define FF_TCO2_SRATE_44_1 0 #define FF_TCO2_SRATE_48 FF_TCO2_SET_FREQ #define FF_TCO2_SRATE_FROM_APP FF_TCO2_SET_FREQ_FROM_APP // Interpretation of the TCO software settings fields #define FF_TCOPARAM_INPUT_LTC 1 #define FF_TCOPARAM_INPUT_VIDEO 2 #define FF_TCOPARAM_INPUT_WCK 3 #define FF_TCOPARAM_FRAMERATE_24fps 1 #define FF_TCOPARAM_FRAMERATE_25fps 2 #define FF_TCOPARAM_FRAMERATE_29_97fps 3 #define FF_TCOPARAM_FRAMERATE_29_97dfps 4 #define FF_TCOPARAM_FRAMERATE_30fps 5 #define FF_TCOPARAM_FRAMERATE_30dfps 6 #define FF_TCOPARAM_WORD_CLOCK_CONV_1_1 1 // 1:1 #define FF_TCOPARAM_WORD_CLOCK_CONV_44_48 2 // 44.1 kHz-> 48 kHz #define FF_TCOPARAM_WORD_CLOCK_CONV_48_44 3 // 48 kHz -> 44.1 kHz #define FF_TCOPARAM_SRATE_44_1 1 // Rate is 44.1 kHz #define FF_TCOPARAM_SRATE_48 2 // Rate is 48 kHz #define FF_TCOPARAM_SRATE_FROM_APP 3 #define FF_TCOPARAM_PULL_NONE 1 #define FF_TCOPARAM_PULL_UP_01 2 // +0.1% #define FF_TCOPARAM_PULL_DOWN_01 3 // -0.1% #define FF_TCOPARAM_PULL_UP_40 4 // +4.0% #define FF_TCOPARAM_PULL_DOWN_40 5 // -4.0% #define FF_TCOPARAM_TERMINATION_ON 1 // The state of the TCO typedef struct { unsigned int locked, ltc_valid; unsigned int hours, minutes, seconds, frames; unsigned int frame_rate; unsigned int drop_frame; unsigned int video_input; unsigned int word_clock_state; float sample_rate; } FF_TCO_state_t; // TCO state field defines #define FF_TCOSTATE_FRAMERATE_24fps 1 #define FF_TCOSTATE_FRAMERATE_25fps 2 #define FF_TCOSTATE_FRAMERATE_29_97fps 3 #define FF_TCOSTATE_FRAMERATE_30fps 4 #define FF_TCOSTATE_VIDEO_NONE 0 #define FF_TCOSTATE_VIDEO_PAL 1 #define FF_TCOSTATE_VIDEO_NTSC 2 #define FF_TCOSTATE_WORDCLOCK_NONE 0 #define FF_TCOSTATE_WORDCLOCK_1x 1 #define FF_TCOSTATE_WORDCLOCK_2x 2 #define FF_TCOSTATE_WORDCLOCK_4x 3 #endif libffado-2.4.5/src/rme/fireface_flash.cpp0000644000175000001440000007056714206145246017702 0ustar jwoitheusers/* * Copyright (C) 2009-2013 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* This file implements the flash memory methods of the Device object */ #include #include #include "rme/rme_avdevice.h" #include "rme/fireface_def.h" #include "debugmodule/debugmodule.h" #define MAX_FLASH_BUSY_RETRIES 25 namespace Rme { signed int Device::wait_while_busy(unsigned int init_delay_ms) { signed int i; quadlet_t status; // Wait for the device to become available for a new command. A delay // of init_delay_ms is executed prior to each test of the device status. for (i=0; i RME_FF_FLASH_SECTOR_SIZE_QUADS)?RME_FF_FLASH_SECTOR_SIZE_QUADS:n_quads; err |= readBlock(addr, buf, xfer_size); n_quads -= xfer_size; buf += xfer_size; addr += xfer_size*sizeof(quadlet_t); } while (n_quads>0 && !err); } else { // FF400 case follows do { xfer_size = (n_quads > 32)?32:n_quads; block_desc[0] = ff400_addr; block_desc[1] = xfer_size * sizeof(quadlet_t); // Program the read address and size err |= writeBlock(RME_FF400_FLASH_BLOCK_ADDR_REG, block_desc, 2); // Execute the read and wait for its completion err |= writeRegister(RME_FF400_FLASH_CMD_REG, RME_FF400_FLASH_CMD_READ); if (!err) wait_while_busy(2); // Read from bounce buffer into final destination err |= readBlock(RME_FF400_FLASH_READ_BUFFER, buf, xfer_size); n_quads -= xfer_size; ff400_addr += xfer_size*sizeof(quadlet_t); buf += xfer_size; } while (n_quads>0 && !err); } return err?-1:0; } signed int Device::erase_flash(unsigned int flags) { // Erase the requested flash block. "flags" should be one of the // RME_FF_FLASH_ERASE_* flags. Return 0 on success, -1 on error. fb_nodeaddr_t addr; quadlet_t data; unsigned int err = 0; if (m_rme_model == RME_MODEL_FIREFACE800) { switch (flags) { case RME_FF_FLASH_ERASE_VOLUME: addr = RME_FF800_FLASH_ERASE_VOLUME_REG; break; case RME_FF_FLASH_ERASE_SETTINGS: addr = RME_FF800_FLASH_ERASE_SETTINGS_REG; break; case RME_FF_FLASH_ERASE_CONFIG: addr = RME_FF800_FLASH_ERASE_CONFIG_REG; break; default: debugOutput(DEBUG_LEVEL_WARNING, "unknown flag %d\n", flags); return -1; } data = 0; } else if (m_rme_model == RME_MODEL_FIREFACE400) { addr = RME_FF400_FLASH_CMD_REG; switch (flags) { case RME_FF_FLASH_ERASE_VOLUME: data = RME_FF400_FLASH_CMD_ERASE_VOLUME; break; case RME_FF_FLASH_ERASE_SETTINGS: data = RME_FF400_FLASH_CMD_ERASE_SETTINGS; break; case RME_FF_FLASH_ERASE_CONFIG: data = RME_FF400_FLASH_CMD_ERASE_CONFIG; break; default: debugOutput(DEBUG_LEVEL_WARNING, "unknown flag %d\n", flags); return -1; } } else { debugOutput(DEBUG_LEVEL_ERROR, "unimplemented model %d\n", m_rme_model); return -1; } err |= writeRegister(addr, data); if (!err) { wait_while_busy(500); // After the device is ready, wait a further 20 milliseconds. The purpose // of this is unclear. Drivers from other OSes do it, so we should too. usleep(20000); } return err?-1:0; } signed int Device::write_flash(fb_nodeaddr_t addr, quadlet_t *buf, unsigned int n_quads) { // Write "n_quads" quadlets to the Fireface Flash starting at address // addr. Return 0 on success, -1 on error. The caller must ensure the // supplied address is appropriate for the device in use. unsigned int xfer_size; unsigned int err = 0; quadlet_t block_desc[2]; quadlet_t ff400_addr = (addr & 0xffffffff); if (m_rme_model == RME_MODEL_FIREFACE800) { do { xfer_size = (n_quads > RME_FF_FLASH_SECTOR_SIZE_QUADS)?RME_FF_FLASH_SECTOR_SIZE_QUADS:n_quads; err |= writeBlock(addr, buf, xfer_size); if (!err) { err = wait_while_busy(5) != 0; if (err) debugOutput(DEBUG_LEVEL_WARNING, "device still busy after flash write\n"); } else debugOutput(DEBUG_LEVEL_WARNING, "flash writeBlock() failed\n"); n_quads -= xfer_size; buf += xfer_size; addr += xfer_size*sizeof(quadlet_t); } while (n_quads>0 && !err); return err?-1:0; } // FF400 case follows do { xfer_size = (n_quads > 32)?32:n_quads; // Send data to flash buffer err |= writeBlock(RME_FF400_FLASH_WRITE_BUFFER, buf, xfer_size); // Program the destination address and size block_desc[0] = ff400_addr; block_desc[1] = xfer_size * sizeof(quadlet_t); err |= writeBlock(RME_FF400_FLASH_BLOCK_ADDR_REG, block_desc, 2); // Execute the write and wait for its completion err |= writeRegister(RME_FF400_FLASH_CMD_REG, RME_FF400_FLASH_CMD_WRITE); if (!err) wait_while_busy(2); n_quads -= xfer_size; ff400_addr += xfer_size*sizeof(quadlet_t); buf += xfer_size; } while (n_quads>0 && !err); return err?-1:0; } signed int Device::read_device_flash_settings(FF_software_settings_t *dsettings) { // Note: this function does NOT copy the newly read settings into // the hardware registers even if reading into the device's master // settings structure (ie: when dsettings is NULL). If the settings // read from flash are to be made active the caller must take // care of this (by calling set_hardware_params() for instance). if (dsettings == NULL) dsettings = settings; // Read the device's configuration flash RAM and use this to set up // the given settings structure. FF_device_flash_settings_t hw_settings; signed int i, err = 0; unsigned int rev; long long int addr; quadlet_t status_buf[4]; i = get_revision(&rev); if (i != 0) { debugOutput(DEBUG_LEVEL_WARNING, "Error reading hardware revision: %d\n", i); } else { debugOutput(DEBUG_LEVEL_VERBOSE, "Hardware revision: 0x%08x\n", rev); } // Read settings flash ram block if (m_rme_model == RME_MODEL_FIREFACE800) addr = RME_FF800_FLASH_SETTINGS_ADDR; else if (m_rme_model == RME_MODEL_FIREFACE400) addr = RME_FF400_FLASH_SETTINGS_ADDR; else { debugOutput(DEBUG_LEVEL_ERROR, "unimplemented model %d\n", m_rme_model); return -1; } err = read_flash(addr, (quadlet_t *)&hw_settings, sizeof(hw_settings)/sizeof(uint32_t)); if (err != 0) { debugOutput(DEBUG_LEVEL_WARNING, "Error reading device flash settings: %d\n", i); return -1; } debugOutput(DEBUG_LEVEL_VERBOSE, "Device flash settings:\n"); if (hw_settings.clock_mode == FF_DEV_FLASH_INVALID) { debugOutput(DEBUG_LEVEL_VERBOSE, " Clock mode: not set in device flash\n"); } else { debugOutput(DEBUG_LEVEL_VERBOSE, " Clock mode: %s\n", hw_settings.clock_mode==FF_DEV_FLASH_CLOCK_MODE_MASTER?"Master":"Slave"); } if (hw_settings.sample_rate == FF_DEV_FLASH_INVALID) { debugOutput(DEBUG_LEVEL_VERBOSE, " Sample rate: not set in device flash\n"); } else if (hw_settings.sample_rate == FF_DEV_FLASH_SRATE_DDS_INACTIVE) { debugOutput(DEBUG_LEVEL_VERBOSE, " Sample rate: DDS not active\n"); } else { debugOutput(DEBUG_LEVEL_VERBOSE, " Sample rate: %d Hz (DDS active)\n", hw_settings.sample_rate); } // Sanity check the "limit_bandwidth" setting since it has been observed // to take on bogus values for some users. The reason behind the // unexpected values is currently unknown. Note that limit_bandwidth is // unsigned, so there's no need to check for values less than 0. if (hw_settings.limit_bandwidth > FF_DEV_FLASH_BWLIMIT_ANALOG_ONLY) { debugOutput(DEBUG_LEVEL_WARNING, "bogus FireWire bandwidth limit flag 0x%08x reset to 0 (send all channels)\n", hw_settings.limit_bandwidth); hw_settings.limit_bandwidth = FF_DEV_FLASH_BWLIMIT_SEND_ALL_CHANNELS; } if (dsettings != NULL) { memset(dsettings, 0, sizeof(*dsettings)); // Copy hardware details to the software settings structure as // appropriate. for (i=0; i<2; i++) dsettings->mic_phantom[i] = hw_settings.mic_phantom[i]; if (m_rme_model == RME_MODEL_FIREFACE800) { for (i=2; i<4; i++) dsettings->mic_phantom[i] = hw_settings.mic_phantom[i]; } else if (m_rme_model == RME_MODEL_FIREFACE400) { // TODO: confirm this is true for (i=2; i<4; i++) dsettings->ff400_input_pad[i-2] = hw_settings.mic_phantom[i]; } else { debugOutput(DEBUG_LEVEL_ERROR, "unimplemented model %d\n", m_rme_model); return -1; } dsettings->spdif_input_mode = hw_settings.spdif_input_mode; dsettings->spdif_output_emphasis = hw_settings.spdif_output_emphasis; dsettings->spdif_output_pro = hw_settings.spdif_output_pro; dsettings->spdif_output_nonaudio = hw_settings.spdif_output_nonaudio; dsettings->spdif_output_mode = hw_settings.spdif_output_mode; dsettings->clock_mode = hw_settings.clock_mode; dsettings->sync_ref = hw_settings.sync_ref; dsettings->tms = hw_settings.tms; dsettings->limit_bandwidth = hw_settings.limit_bandwidth; dsettings->stop_on_dropout = hw_settings.stop_on_dropout; dsettings->input_level = hw_settings.input_level; dsettings->output_level = hw_settings.output_level; if (m_rme_model == RME_MODEL_FIREFACE800) { dsettings->filter = hw_settings.filter; dsettings->fuzz = hw_settings.fuzz; } else if (m_rme_model == RME_MODEL_FIREFACE400) { // TODO: confirm this is true dsettings->ff400_instr_input[0] = hw_settings.fuzz; dsettings->ff400_instr_input[1] = hw_settings.filter; } dsettings->limiter = (hw_settings.p12db_an[0] == 0)?1:0; dsettings->sample_rate = hw_settings.sample_rate; dsettings->word_clock_single_speed = hw_settings.word_clock_single_speed; // The FF800 has front/rear selectors for the "instrument" input // (aka channel 1) and the two "mic" channels (aka channels 7 and 8). // The FF400 does not. The FF400 borrows the mic0 selector field // in the flash configuration structure to use for the "phones" // level which the FF800 doesn't have. if (m_rme_model == RME_MODEL_FIREFACE400) dsettings->phones_level = hw_settings.mic_plug_select[0]; else if (m_rme_model == RME_MODEL_FIREFACE800) { // The value for the front/rear selectors coming from the flash // is an indexed value: 0=rear, 1=front, 2=front and rear. By // adding one to this we can treat input_opt as a bitmask with // bit 0 being "rear" and bit 1 being "front". This follows the // approach used in drivers for other operating systems and // simplifies certain logic expressions within the driver. dsettings->input_opt[0] = hw_settings.instrument_plug_select + 1; dsettings->input_opt[1] = hw_settings.mic_plug_select[0] + 1; dsettings->input_opt[2] = hw_settings.mic_plug_select[1] + 1; } /* If debug is enabled, show what's been read from the flash */ debugOutput(DEBUG_LEVEL_VERBOSE, "Settings acquired from flash:\n"); if (m_rme_model == RME_MODEL_FIREFACE800) { debugOutput(DEBUG_LEVEL_VERBOSE, " Phantom: %d %d %d %d\n", dsettings->mic_phantom[0], dsettings->mic_phantom[1], dsettings->mic_phantom[2], dsettings->mic_phantom[2]); } else if (m_rme_model == RME_MODEL_FIREFACE400) { debugOutput(DEBUG_LEVEL_VERBOSE, " Phantom: %d %d\n", dsettings->mic_phantom[0], dsettings->mic_phantom[1]); debugOutput(DEBUG_LEVEL_VERBOSE, " Input pad: %d %d\n", dsettings->ff400_input_pad[0], dsettings->ff400_input_pad[1]); } debugOutput(DEBUG_LEVEL_VERBOSE, " spdif input mode: %d\n", dsettings->spdif_input_mode); debugOutput(DEBUG_LEVEL_VERBOSE, " spdif output emphasis: %d\n", dsettings->spdif_output_emphasis); debugOutput(DEBUG_LEVEL_VERBOSE, " spdif output pro: %d\n", dsettings->spdif_output_pro); debugOutput(DEBUG_LEVEL_VERBOSE, " spdif output nonaudio: %d\n", dsettings->spdif_output_nonaudio); debugOutput(DEBUG_LEVEL_VERBOSE, " spdif output mode: %d\n", dsettings->spdif_output_mode); debugOutput(DEBUG_LEVEL_VERBOSE, " clock mode: %d\n", dsettings->clock_mode); debugOutput(DEBUG_LEVEL_VERBOSE, " sync ref: %d\n", dsettings->sync_ref); debugOutput(DEBUG_LEVEL_VERBOSE, " tms: %d\n", dsettings->tms); debugOutput(DEBUG_LEVEL_VERBOSE, " limit FireWire bandwidth: %d\n", dsettings->limit_bandwidth); debugOutput(DEBUG_LEVEL_VERBOSE, " stop on dropout: %d\n", dsettings->stop_on_dropout); debugOutput(DEBUG_LEVEL_VERBOSE, " input level: %d\n", dsettings->input_level); debugOutput(DEBUG_LEVEL_VERBOSE, " output level: %d\n", dsettings->output_level); if (m_rme_model == RME_MODEL_FIREFACE800) { debugOutput(DEBUG_LEVEL_VERBOSE, " filter: %d\n", dsettings->filter); debugOutput(DEBUG_LEVEL_VERBOSE, " fuzz: %d\n", dsettings->fuzz); } else if (m_rme_model == RME_MODEL_FIREFACE400) { debugOutput(DEBUG_LEVEL_VERBOSE, " instr input 0: %d\n", dsettings->ff400_instr_input[0]); debugOutput(DEBUG_LEVEL_VERBOSE, " instr input 1: %d\n", dsettings->ff400_instr_input[1]); } debugOutput(DEBUG_LEVEL_VERBOSE, " limiter: %d\n", dsettings->limiter); debugOutput(DEBUG_LEVEL_VERBOSE, " sample rate: %d\n", dsettings->sample_rate); debugOutput(DEBUG_LEVEL_VERBOSE, " word clock single speed: %d\n", dsettings->word_clock_single_speed); if (m_rme_model == RME_MODEL_FIREFACE400) { debugOutput(DEBUG_LEVEL_VERBOSE, " phones level: %d\n", dsettings->phones_level); } else if (m_rme_model == RME_MODEL_FIREFACE800) { debugOutput(DEBUG_LEVEL_VERBOSE, " input opts: %d %d %d\n", dsettings->input_opt[0], dsettings->input_opt[1], dsettings->input_opt[2]); } } i = readBlock(RME_FF_STATUS_REG0, status_buf, 4); debugOutput(DEBUG_LEVEL_VERBOSE, "Status read: %d: 0x%08x 0x%08x 0x%08x 0x%08x\n", i, status_buf[0], status_buf[1], status_buf[2], status_buf[3]); return err!=0?-1:0; } signed int Device::write_device_flash_settings(FF_software_settings_t *dsettings) { if (dsettings == NULL) dsettings = settings; // Write the given device settings to the device's configuration flash. FF_device_flash_settings_t hw_settings; signed int i, err = 0; if (dsettings == NULL) { debugOutput(DEBUG_LEVEL_WARNING, "NULL settings parameter\n"); return -1; } memset(&hw_settings, 0, sizeof(hw_settings)); // Copy software settings to the hardware structure as appropriate. for (i=0; i<4; i++) hw_settings.mic_phantom[i] = dsettings->mic_phantom[i]; hw_settings.spdif_input_mode = dsettings->spdif_input_mode; hw_settings.spdif_output_emphasis = dsettings->spdif_output_emphasis; hw_settings.spdif_output_pro = dsettings->spdif_output_pro; hw_settings.spdif_output_nonaudio = dsettings->spdif_output_nonaudio; hw_settings.spdif_output_mode = dsettings->spdif_output_mode; hw_settings.clock_mode = dsettings->clock_mode; hw_settings.sync_ref = dsettings->sync_ref; hw_settings.tms = dsettings->tms; hw_settings.limit_bandwidth = dsettings->limit_bandwidth; hw_settings.stop_on_dropout = dsettings->stop_on_dropout; hw_settings.input_level = dsettings->input_level; hw_settings.output_level = dsettings->output_level; hw_settings.filter = dsettings->filter; hw_settings.fuzz = dsettings->fuzz; // The limiter can only be disabled if channel 1 uses the "front" input. // Note that p12db_an (as passed to the flash) seems to be a "limiter // disabled" flag. if (m_rme_model==RME_MODEL_FIREFACE800 && dsettings->limiter==0 && dsettings->input_opt[0]==FF_SWPARAM_FF800_INPUT_OPT_FRONT) hw_settings.p12db_an[0] = 1; else hw_settings.p12db_an[0] = 0; hw_settings.sample_rate = dsettings->sample_rate; hw_settings.word_clock_single_speed = dsettings->word_clock_single_speed; // The FF800 has front/rear selectors for the "instrument" input // (aka channel 1) and the two "mic" channels (aka channels 7 and 8). // The FF400 does not. The FF400 borrows the mic0 selector field // in the flash configuration structure to use for the "phones" // level which the FF800 doesn't have. if (m_rme_model == RME_MODEL_FIREFACE400) hw_settings.mic_plug_select[0] = dsettings->phones_level; else if (m_rme_model == RME_MODEL_FIREFACE800) { // The offset of 1 follows the convention used internally in drivers // for other operating systems. It permits input_opt to be treated // as a bitmask with bit 0 being "rear" and bit 1 being "front". // In the flash, the corresponding value is the index of the active // option in the list rear, front, front and rear. See also the // related section of read_device_flash_settings(). hw_settings.instrument_plug_select = dsettings->input_opt[0] - 1; hw_settings.mic_plug_select[0] = dsettings->input_opt[1] - 1; hw_settings.mic_plug_select[1] = dsettings->input_opt[2] - 1; } // The configuration flash block must be erased before we can write to it err = erase_flash(RME_FF_FLASH_ERASE_SETTINGS) != 0; if (err != 0) debugOutput(DEBUG_LEVEL_WARNING, "Error erasing settings flash block: %d\n", i); else { long long int addr; if (m_rme_model == RME_MODEL_FIREFACE800) addr = RME_FF800_FLASH_SETTINGS_ADDR; else if (m_rme_model == RME_MODEL_FIREFACE400) addr = RME_FF400_FLASH_SETTINGS_ADDR; else { debugOutput(DEBUG_LEVEL_ERROR, "unimplemented model %d\n", m_rme_model); return -1; } err = write_flash(addr, (quadlet_t *)&hw_settings, sizeof(hw_settings)/sizeof(uint32_t)); if (err != 0) debugOutput(DEBUG_LEVEL_WARNING, "Error writing device flash settings: %d\n", i); } return err!=0?-1:0; } static float fader2flashvol(signed int fader) { return (1023.0/3) * log(fader*(exp(3.0)-1.0)/0x10000 + 1); } static float flashvol2fader(signed int flash_vol) { // Map the 0 dB flash volume value explicitly to the corresponding // fader value to avoid round-off effects. if (flash_vol == RME_FF_FLASH_0DB_VOL_VALUE) return 0x8000; return 0x10000 * (exp(3.0*flash_vol/1023.0)-1) / (exp(3)-1.0); } static void faders2flash(signed int fader0, signed int fader1, unsigned short int *flash_vol, unsigned short int *flash_pan) { signed int v = fader0 + fader1; *flash_pan = 256.0 * fader1 / v; *flash_vol = fader2flashvol(v); } static void flash2faders(signed int flash_vol, signed int flash_pan, signed int *fader0, signed int *fader1) { float v = flashvol2fader(flash_vol); *fader0 = v * (1 - flash_pan/256.0); *fader1 = v * (flash_pan/256.0); } signed int Device::read_device_mixer_settings(FF_software_settings_t *dsettings) { // Note: this function does NOT send the mixer configuration read from // flash to the mixer control registers. If the newly read state is // to become active, the caller must arrange for this to happen (perhaps // by calling set_hardware_mixergain(), or relying on "changed" widget // callbacks like the rme.py ffado-mixer module does). unsigned short int vbuf[RME_FF_FLASH_MIXER_ARRAY_SIZE/2]; unsigned short int pbuf[RME_FF_FLASH_MIXER_ARRAY_SIZE/2]; unsigned short int obuf[RME_FF_FLASH_SECTOR_SIZE/2]; fb_nodeaddr_t addr = 0; signed int i, in, out; signed int nch = 0; signed int flash_row_size = 0; if (dsettings == NULL) dsettings = settings; if (m_rme_model == RME_MODEL_FIREFACE400) { addr = RME_FF400_FLASH_MIXER_VOLUME_ADDR; nch = RME_FF400_MAX_CHANNELS; flash_row_size = 18; } else if (m_rme_model == RME_MODEL_FIREFACE800) { addr = RME_FF800_FLASH_MIXER_VOLUME_ADDR; nch = RME_FF800_MAX_CHANNELS; flash_row_size = 32; } if (addr == 0) return -1; i = read_flash(addr, (quadlet_t *)(vbuf), RME_FF_FLASH_MIXER_ARRAY_SIZE/4); debugOutput(DEBUG_LEVEL_VERBOSE, "read_flash(%" PRId64 ") returned %d\n", addr, i); addr += RME_FF_FLASH_MIXER_ARRAY_SIZE; i = read_flash(addr, (quadlet_t *)(pbuf), RME_FF_FLASH_MIXER_ARRAY_SIZE/4); debugOutput(DEBUG_LEVEL_VERBOSE, "read_flash(%" PRId64 ") returned %d\n", addr, i); addr += RME_FF_FLASH_MIXER_ARRAY_SIZE; i = read_flash(addr, (quadlet_t *)obuf, RME_FF_FLASH_SECTOR_SIZE_QUADS); debugOutput(DEBUG_LEVEL_VERBOSE, "read_flash(%" PRId64 ") returned %d\n", addr, i); for (out=0; outinput_faders[getMixerGainIndex(in,out*2)], &dsettings->input_faders[getMixerGainIndex(in,out*2+1)]); } } for (out=0; outplayback_faders[getMixerGainIndex(in,out*2)], &dsettings->playback_faders[getMixerGainIndex(in,out*2+1)]); } } // Elements 30 and 31 of obuf[] are not output fader values: [30] // indicates MIDI control is active while [31] is a submix number. // It's suspected that neither of these are used by the device directly, // and that these elements are just a convenient place for computer // control applications to store things. FFADO does not make use // of these. nch is assumed to be <= RME_FF800_MAX_CHANNELS (28), // the size of the output_faders[] array. for (out=0; outoutput_faders[out] = flashvol2fader(obuf[out]); } return 0; } signed int Device::write_device_mixer_settings(FF_software_settings_t *dsettings) { quadlet_t shadow[RME_FF800_FLASH_MIXER_SHADOW_SIZE/4]; unsigned short int vbuf[RME_FF_FLASH_MIXER_ARRAY_SIZE/2]; unsigned short int pbuf[RME_FF_FLASH_MIXER_ARRAY_SIZE/2]; unsigned short int obuf[RME_FF_FLASH_SECTOR_SIZE/2]; fb_nodeaddr_t addr = 0; signed int i, in, out; signed int nch = 0; signed int flash_row_size = 0; if (dsettings == NULL) dsettings = settings; if (m_rme_model == RME_MODEL_FIREFACE400) { addr = RME_FF400_FLASH_MIXER_VOLUME_ADDR; nch = RME_FF400_MAX_CHANNELS; flash_row_size = 18; } else if (m_rme_model == RME_MODEL_FIREFACE800) { addr = RME_FF800_FLASH_MIXER_SHADOW_ADDR; nch = RME_FF800_MAX_CHANNELS; flash_row_size = 32; } if (addr == 0) return -1; // The mixer flash block must be erased before we can write to it i = erase_flash(RME_FF_FLASH_ERASE_VOLUME) != 0; if (i) { debugOutput(DEBUG_LEVEL_VERBOSE, "erase_flash() failed\n"); return -1; } /* Write the shadow mixer array if the device is a ff800 */ if (m_rme_model == RME_MODEL_FIREFACE800) { memset(shadow, 0, sizeof(shadow)); for (out=0; outinput_faders[getMixerGainIndex(in,out)]; shadow[in+out*0x40+0x20] = dsettings->playback_faders[getMixerGainIndex(in,out)]; } } for (out=0; outoutput_faders[out]; } i = write_flash(addr, shadow, RME_FF800_FLASH_MIXER_SHADOW_SIZE/4); debugOutput(DEBUG_LEVEL_VERBOSE, "write_flash(%" PRId64 ") returned %d\n", addr, i); addr = RME_FF800_FLASH_MIXER_VOLUME_ADDR; } memset(vbuf, 0, sizeof(vbuf)); memset(pbuf, 0, sizeof(pbuf)); for (out=0; outinput_faders[getMixerGainIndex(in,out*2)], dsettings->input_faders[getMixerGainIndex(in,out*2+1)], &vbuf[in+out*2*flash_row_size], &pbuf[in+out*2*flash_row_size]); } } for (out=0; outplayback_faders[getMixerGainIndex(in,out*2)], dsettings->playback_faders[getMixerGainIndex(in,out*2+1)], &vbuf[in+flash_row_size*(out*2+1)], &pbuf[in+flash_row_size*(out*2+1)]); } } // Elements 30 and 31 of obuf[] are not output fader values. See // comments in read_device_mixer_settings(). memset(obuf, 0, sizeof(obuf)); for (out=0; outoutput_faders[out]); } i = write_flash(addr, (quadlet_t *)(vbuf), RME_FF_FLASH_MIXER_ARRAY_SIZE/4); debugOutput(DEBUG_LEVEL_VERBOSE, "write_flash(%" PRId64 ") returned %d\n", addr, i); addr += RME_FF_FLASH_MIXER_ARRAY_SIZE; i = write_flash(addr, (quadlet_t *)(pbuf), RME_FF_FLASH_MIXER_ARRAY_SIZE/4); debugOutput(DEBUG_LEVEL_VERBOSE, "write_flash(%" PRId64 ") returned %d\n", addr, i); addr += RME_FF_FLASH_MIXER_ARRAY_SIZE; i = write_flash(addr, (quadlet_t *)obuf, RME_FF_FLASH_SECTOR_SIZE_QUADS); debugOutput(DEBUG_LEVEL_VERBOSE, "write_flash(%" PRId64 ") returned %d\n", addr, i); return 0; } } libffado-2.4.5/src/rme/fireface_hw.cpp0000644000175000001440000012162014206145246017206 0ustar jwoitheusers/* * Copyright (C) 2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* This file implements miscellaneous lower-level hardware functions for the Fireface */ #include #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "rme/rme_avdevice.h" #include "rme/fireface_def.h" #include "debugmodule/debugmodule.h" namespace Rme { unsigned int Device::multiplier_of_freq(unsigned int freq) { if (freq > MIN_QUAD_SPEED) return 4; if (freq > MIN_DOUBLE_SPEED) return 2; return 1; } void Device::config_lock(void) { rme_shm_lock(dev_config); } void Device::config_unlock(void) { rme_shm_unlock(dev_config); } signed int Device::init_hardware(void) { signed int ret = 0; signed int src, dest; signed int n_channels; signed int have_mixer_settings = 0; switch (m_rme_model) { case RME_MODEL_FIREFACE400: n_channels = RME_FF400_MAX_CHANNELS; break; case RME_MODEL_FIREFACE800: n_channels = RME_FF800_MAX_CHANNELS; break; default: debugOutput(DEBUG_LEVEL_ERROR, "unknown model %d\n", m_rme_model); return -1; } // Initialises the device's settings structure to a known state and then // sets the hardware to reflect this state. config_lock(); // If the software state is not yet valid, attempt to obtain settings // from the device flash. If that fails for some reason, initialise // with a static setup. if (dev_config->settings_valid == 0) { dev_config->settings_valid = read_device_flash_settings(settings) == 0; // If valid settings were read, write them to the device so we can // be sure that this mirrors how the device is currently configured. // This is also needed so the "host" LED is extinguished on first // use after power up. Also use the stored sample rate as the // operational rate. if (dev_config->settings_valid) { dev_config->software_freq = settings->sample_rate; // dds_freq can be used to run the audio clock at a slightly // different frequency to what the software requests (to allow // for drop-frame rates for example). For the moment FFADO // provides no explicit support for this, so dds_freq should // always be zero. // // If user access to dds_freq is implemented in future, it may // or may not be desireable to set dds_freq from // settings->sample_rate. This will probably be determined by // the nature of the user interface to dds_freq. // // See also comments in getSamplingFrequency() and // setDDSFrequency(). dev_config->dds_freq = 0; set_hardware_params(settings); } } // If no valid flash settings, configure with a static setup. if (dev_config->settings_valid == 0) { debugOutput(DEBUG_LEVEL_VERBOSE, "flash settings unavailable or invalid; using defaults\n"); memset(settings, 0, sizeof(*settings)); settings->spdif_input_mode = FF_SWPARAM_SPDIF_INPUT_COAX; settings->spdif_output_mode = FF_SWPARAM_SPDIF_OUTPUT_COAX; settings->clock_mode = FF_SWPARAM_CLOCK_MODE_MASTER; settings->sync_ref = FF_SWPARAM_SYNCREF_WORDCLOCK; settings->input_level = FF_SWPARAM_ILEVEL_LOGAIN; settings->output_level = FF_SWPARAM_OLEVEL_HIGAIN; settings->phones_level = FF_SWPARAM_PHONESLEVEL_HIGAIN; settings->limit_bandwidth = FF_SWPARAM_BWLIMIT_SEND_ALL_CHANNELS; // A default sampling rate. An explicit DDS frequency is not enabled // by default (FFADO doesn't currently make explicit use of this - see // above). dev_config->software_freq = 44100; dev_config->dds_freq = 0; settings->sample_rate = dev_config->software_freq; // TODO: set input amplifier gains to a value other than 0? // TODO: store and manipulate channel mute/rec flags // The FF800 needs the input source set via the input options. // The device's default has the limiter enabled so we'll follow // that convention. if (m_rme_model == RME_MODEL_FIREFACE800) { settings->input_opt[0] = settings->input_opt[1] = settings->input_opt[2] = FF_SWPARAM_FF800_INPUT_OPT_FRONT; settings->limiter = 1; } // Configure the hardware to match the current software status. // This is only done if the settings valid flag is 0; if it is 1 it // indicates that something has already set the device up to match // the software settings so there's no need to do it again. if (set_hardware_params(settings) != 0) ret = -1; if (ret==0) { signed freq = dev_config->software_freq; if (dev_config->dds_freq > 0) freq = dev_config->dds_freq; if (set_hardware_dds_freq(freq) != 0) ret = -1; } if (m_rme_model == RME_MODEL_FIREFACE400) { signed int i; for (i=FF400_AMPGAIN_MIC1; i<=FF400_AMPGAIN_INPUT4; i++) { set_hardware_ampgain(i, settings->amp_gains[i]); } } dev_config->settings_valid = 1; } have_mixer_settings = read_device_mixer_settings(settings) == 0; // Matrix mixer settings for (dest=0; destinput_faders[getMixerGainIndex(src, dest)] = 0; set_hardware_mixergain(RME_FF_MM_INPUT, src, dest, settings->input_faders[getMixerGainIndex(src, dest)]); } for (src=0; srcplayback_faders[getMixerGainIndex(src, dest)] = src==dest?0x8000:0; set_hardware_mixergain(RME_FF_MM_PLAYBACK, src, dest, settings->playback_faders[getMixerGainIndex(src, dest)]); } } for (src=0; srcoutput_faders[src] = 0x8000; set_hardware_mixergain(RME_FF_MM_OUTPUT, src, 0, settings->output_faders[src]); } set_hardware_output_rec(0); if (ret==0 && m_rme_model==RME_MODEL_FIREFACE400 && provide_midi) { // Precisely mirror the method used under other operating systems to // set the high quadlet of the MIDI ARM address, even though it is a // little inflexible. We can refine this later if need be. This is // only done if FFADO is providing MIDI functionality. If not, the // RME_FF400_MIDI_HIGH_ADDR is left alone for other drivers (such as // snd-fireface in Linux kernel >= 4.12) to configure if desired. unsigned int node_id = getConfigRom().getNodeId(); unsigned int midi_hi_addr; midi_hi_addr = 0x01; if (writeRegister(RME_FF400_MIDI_HIGH_ADDR, (node_id<<16) | midi_hi_addr) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to write MIDI high address register\n"); ret = -1; } } // Also configure the TCO (Time Code Option) settings for those devices // which have a TCO. if (ret==0 && dev_config->tco_settings_valid==0) { if (dev_config->tco_present) { FF_TCO_state_t tco_state; memset(tco_settings, 0, sizeof(*tco_settings)); if (read_tco_state(&tco_state) == 0) { if (tco_state.ltc_valid) { tco_settings->input = FF_TCOPARAM_INPUT_LTC; switch (tco_state.frame_rate) { case FF_TCOSTATE_FRAMERATE_24fps: tco_settings->frame_rate = FF_TCOPARAM_FRAMERATE_24fps; break; case FF_TCOSTATE_FRAMERATE_25fps: tco_settings->frame_rate = FF_TCOPARAM_FRAMERATE_25fps; break; case FF_TCOSTATE_FRAMERATE_29_97fps: tco_settings->frame_rate = FF_TCOPARAM_FRAMERATE_29_97fps; break; case FF_TCOSTATE_FRAMERATE_30fps: tco_settings->frame_rate = FF_TCOPARAM_FRAMERATE_30fps; default: tco_settings->frame_rate = FF_TCOPARAM_FRAMERATE_25fps; } if (tco_state.drop_frame) { tco_settings->frame_rate += 1; } } else { tco_settings->input = FF_TCOPARAM_INPUT_VIDEO; tco_settings->frame_rate = FF_TCOPARAM_FRAMERATE_25fps; } tco_settings->word_clock = FF_TCOPARAM_WORD_CLOCK_CONV_1_1; tco_settings->sample_rate = ((settings->sample_rate % 48000)==0) ? FF_TCOPARAM_SRATE_48 : FF_TCOPARAM_SRATE_44_1; tco_settings->pull = FF_TCOPARAM_PULL_NONE; tco_settings->termination = 0; tco_settings->MTC = 0; } else { debugOutput(DEBUG_LEVEL_ERROR, "failed to read TCO state\n"); } if (write_tco_settings(tco_settings) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to write TCO settings\n"); } } dev_config->tco_settings_valid = 1; } config_unlock(); return ret; } signed int Device::get_hardware_status(unsigned int *stat0, unsigned int *stat1) { unsigned int buf[2]; if (readBlock(RME_FF_STATUS_REG0, buf, 2) != 0) return -1; *stat0 = buf[0]; *stat1 = buf[1]; return 0; } signed int Device::get_hardware_streaming_status(unsigned int *stat, unsigned int n) { // Get the hardware status as it applies to the streaming system. This // involves a request of 4 quadlets from the status register. It // appears that the first register's definition is slightly different in // this situation compared to when only 2 quadlets are requested as is // done in get_hardware_status(). // // "n" is the size of the passed-in stat array. It must be >= 4. if (n < 4) return -1; if (readBlock(RME_FF_STATUS_REG0, stat, 4) != 0) return -1; return 0; } signed int Device::get_hardware_state(FF_state_t *state) { // Retrieve the hardware status and deduce the device state. Return // -1 on error, 0 on success. The given state structure will be // cleared by this call. static signed int call_count = 0; unsigned int stat0, stat1; memset(state, 0, sizeof(*state)); if (get_hardware_status(&stat0, &stat1) != 0) return -1; state->is_streaming = dev_config->is_streaming; state->clock_mode = (settings->clock_mode == FF_SWPARAM_CLOCK_MODE_MASTER)?FF_STATE_CLOCKMODE_MASTER:FF_STATE_CLOCKMODE_AUTOSYNC; switch (stat0 & SR0_AUTOSYNC_SRC_MASK) { case SR0_AUTOSYNC_SRC_ADAT1: state->autosync_source = FF_STATE_AUTOSYNC_SRC_ADAT1; break; case SR0_AUTOSYNC_SRC_ADAT2: state->autosync_source = FF_STATE_AUTOSYNC_SRC_ADAT2; break; case SR0_AUTOSYNC_SRC_SPDIF: state->autosync_source = FF_STATE_AUTOSYNC_SRC_SPDIF; break; case SR0_AUTOSYNC_SRC_WCLK: state->autosync_source = FF_STATE_AUTOSYNC_SRC_WCLK; break; case SR0_AUTOSYNC_SRC_TCO: state->autosync_source = FF_STATE_AUTOSYNC_SRC_TCO; break; default: state->autosync_source = FF_STATE_AUTOSYNC_SRC_NOLOCK; } switch (stat0 & SR0_AUTOSYNC_FREQ_MASK) { case SR0_AUTOSYNC_FREQ_32k: state->autosync_freq = 32000; break; case SR0_AUTOSYNC_FREQ_44k1: state->autosync_freq = 44100; break; case SR0_AUTOSYNC_FREQ_48k: state->autosync_freq = 48000; break; case SR0_AUTOSYNC_FREQ_64k: state->autosync_freq = 64000; break; case SR0_AUTOSYNC_FREQ_88k2: state->autosync_freq = 88200; break; case SR0_AUTOSYNC_FREQ_96k: state->autosync_freq = 96000; break; case SR0_AUTOSYNC_FREQ_128k: state->autosync_freq = 128000; break; case SR0_AUTOSYNC_FREQ_176k4:state->autosync_freq = 176400; break; case SR0_AUTOSYNC_FREQ_192k: state->autosync_freq = 192000; break; } switch (stat0 & SR0_SPDIF_FREQ_MASK) { case SR0_SPDIF_FREQ_32k: state->spdif_freq = 32000; break; case SR0_SPDIF_FREQ_44k1: state->spdif_freq = 41000; break; case SR0_SPDIF_FREQ_48k: state->spdif_freq = 48000; break; case SR0_SPDIF_FREQ_64k: state->spdif_freq = 64000; break; case SR0_SPDIF_FREQ_88k2: state->spdif_freq = 88200; break; case SR0_SPDIF_FREQ_96k: state->spdif_freq = 96000; break; case SR0_SPDIF_FREQ_128k: state->spdif_freq = 128000; break; case SR0_SPDIF_FREQ_176k4:state->spdif_freq = 176400; break; case SR0_SPDIF_FREQ_192k: state->spdif_freq = 192000; break; } switch (stat0 & SR0_ADAT1_STATUS_MASK) { case SR0_ADAT1_STATUS_NOLOCK: state->adat1_sync_status = FF_STATE_SYNC_NOLOCK; break; case SR0_ADAT1_STATUS_LOCK: state->adat1_sync_status = FF_STATE_SYNC_LOCKED; break; case SR0_ADAT1_STATUS_SYNC: state->adat1_sync_status = FF_STATE_SYNC_SYNCED; break; } switch (stat0 & SR0_ADAT2_STATUS_MASK) { case SR0_ADAT2_STATUS_NOLOCK: state->adat2_sync_status = FF_STATE_SYNC_NOLOCK; break; case SR0_ADAT2_STATUS_LOCK: state->adat2_sync_status = FF_STATE_SYNC_LOCKED; break; case SR0_ADAT2_STATUS_SYNC: state->adat2_sync_status = FF_STATE_SYNC_SYNCED; break; } switch (stat0 & SR0_SPDIF_STATUS_MASK) { case SR0_SPDIF_STATUS_NOLOCK: state->spdif_sync_status = FF_STATE_SYNC_NOLOCK; break; case SR0_SPDIF_STATUS_LOCK: state->spdif_sync_status = FF_STATE_SYNC_LOCKED; break; case SR0_SPDIF_STATUS_SYNC: state->spdif_sync_status = FF_STATE_SYNC_SYNCED; break; } switch (stat0 & SR0_WCLK_STATUS_MASK) { case SR0_WCLK_STATUS_NOLOCK: state->wclk_sync_status = FF_STATE_SYNC_NOLOCK; break; case SR0_WCLK_STATUS_LOCK: state->wclk_sync_status = FF_STATE_SYNC_LOCKED; break; case SR0_WCLK_STATUS_SYNC: state->wclk_sync_status = FF_STATE_SYNC_SYNCED; break; } switch (stat1 & SR1_TCO_STATUS_MASK) { case SR1_TCO_STATUS_NOLOCK: state->tco_sync_status = FF_STATE_SYNC_NOLOCK; break; case SR1_TCO_STATUS_LOCK: state->tco_sync_status = FF_STATE_SYNC_LOCKED; break; case SR1_TCO_STATUS_SYNC: state->tco_sync_status = FF_STATE_SYNC_SYNCED; break; } // Report the state reported by the hardware if debug output is active. // Only do this for the first few calls. if (call_count < 2) { debugOutput(DEBUG_LEVEL_VERBOSE, "State reported by hardware:\n"); debugOutput(DEBUG_LEVEL_VERBOSE, " is_streaming: %d\n", state->is_streaming); debugOutput(DEBUG_LEVEL_VERBOSE, " clock_mode: %s\n", state->clock_mode==FF_STATE_CLOCKMODE_MASTER?"master":"autosync/slave"); debugOutput(DEBUG_LEVEL_VERBOSE, " autosync source: %d\n", state->autosync_source); debugOutput(DEBUG_LEVEL_VERBOSE, " autosync freq: %d\n", state->autosync_freq); debugOutput(DEBUG_LEVEL_VERBOSE, " spdif freq: %d\n", state->spdif_freq); debugOutput(DEBUG_LEVEL_VERBOSE, " ADAT 1/2 status: %x, %x\n", state->adat1_sync_status, state->adat2_sync_status); debugOutput(DEBUG_LEVEL_VERBOSE, " SDPIF status: %x\n", state->spdif_sync_status); debugOutput(DEBUG_LEVEL_VERBOSE, " Wclk/tco status: %x, %x\n", state->wclk_sync_status, state->tco_sync_status); call_count++; } return 0; } signed int Device::set_hardware_params(FF_software_settings_t *use_settings) { // Initialises the hardware to the state defined by the supplied // software settings structure (which will usually be the device's // "settings" structure). This has the side effect of extinguishing the // "Host" LED on the FF400 when done for the first time after the // interface has been powered up. // // If use_settings is NULL, the device's current settings structure will // be used to source the configuration information. FF_software_settings_t *sw_settings; quadlet_t data[3] = {0, 0, 0}; unsigned int conf_reg; if (use_settings == NULL) sw_settings = settings; else sw_settings = use_settings; if (sw_settings->mic_phantom[0]) data[0] |= CR0_PHANTOM_MIC0; if (sw_settings->mic_phantom[1]) data[0] |= CR0_PHANTOM_MIC1; switch (m_rme_model) { case RME_MODEL_FIREFACE800: if (sw_settings->mic_phantom[2]) data[0] |= CR0_FF800_PHANTOM_MIC9; if (sw_settings->mic_phantom[3]) data[0] |= CR0_FF800_PHANTOM_MIC10; break; case RME_MODEL_FIREFACE400: if (sw_settings->ff400_input_pad[0]) data[0] |= CR0_FF400_CH3_PAD; if (sw_settings->ff400_input_pad[1]) data[0] |= CR0_FF400_CH4_PAD; break; default: break; } /* Phones level */ if (m_rme_model == RME_MODEL_FIREFACE400) { switch (sw_settings->phones_level) { case FF_SWPARAM_PHONESLEVEL_HIGAIN: data[0] |= CRO_PHLEVEL_HIGAIN; break; case FF_SWPARAM_PHONESLEVEL_4dBU: data[0] |= CR0_PHLEVEL_4dBU; break; case FF_SWPARAM_PHONESLEVEL_m10dBV: data[0] |= CRO_PHLEVEL_m10dBV; break; } } /* Input level */ switch (sw_settings->input_level) { case FF_SWPARAM_ILEVEL_LOGAIN: // Low gain data[1] |= CR1_ILEVEL_CPLD_LOGAIN; // CPLD data[0] |= CR0_ILEVEL_FPGA_LOGAIN; // LED control (used on FF800 only) break; case FF_SWPARAM_ILEVEL_4dBU: // +4 dBu data[1] |= CR1_ILEVEL_CPLD_4dBU; data[0] |= CR0_ILEVEL_FPGA_4dBU; break; case FF_SWPARAM_ILEVEL_m10dBV: // -10 dBV data[1] |= CR1_ILEVEL_CPLD_m10dBV; data[0] |= CR0_ILEVEL_FPGA_m10dBV; break; } /* Output level */ switch (sw_settings->output_level) { case FF_SWPARAM_OLEVEL_HIGAIN: // High gain data[1] |= CR1_OLEVEL_CPLD_HIGAIN; // CPLD data[0] |= CR0_OLEVEL_FPGA_HIGAIN; // LED control (used on FF800 only) break; case FF_SWPARAM_OLEVEL_4dBU: // +4 dBu data[1] |= CR1_OLEVEL_CPLD_4dBU; data[0] |= CR0_OLEVEL_FPGA_4dBU; break; case FF_SWPARAM_OLEVEL_m10dBV: // -10 dBV data[1] |= CR1_OLEVEL_CPLD_m10dBV; data[0] |= CR0_OLEVEL_FPGA_m10dBV; break; } /* Set input options. The meaning of the options differs between * devices, so we use the generic identifiers here. */ data[1] |= (sw_settings->input_opt[1] & FF_SWPARAM_INPUT_OPT_A) ? CR1_INPUT_OPT1_A : 0; data[1] |= (sw_settings->input_opt[1] & FF_SWPARAM_INPUT_OPT_B) ? CR1_INPUT_OPT1_B : 0; data[1] |= (sw_settings->input_opt[2] & FF_SWPARAM_INPUT_OPT_A) ? CR1_INPUT_OPT2_A : 0; data[1] |= (sw_settings->input_opt[2] & FF_SWPARAM_INPUT_OPT_B) ? CR1_INPUT_OPT2_B : 0; // Drive the speaker emulation / filter LED via FPGA in FF800. In FF400 // the same bit controls the channel 4 "instrument" option. if (m_rme_model == RME_MODEL_FIREFACE800) { data[0] |= (sw_settings->filter) ? CR0_FF800_FILTER_FPGA : 0; } else if (m_rme_model == RME_MODEL_FIREFACE400) { data[0] |= (sw_settings->ff400_instr_input[1]) ? CR0_FF400_CH4_INSTR : 0; } // Set the "rear" option for input 0 if selected data[1] |= (sw_settings->input_opt[0] & FF_SWPARAM_FF800_INPUT_OPT_REAR) ? CR1_FF800_INPUT1_REAR : 0; // The input 0 "front" option is activated using one of two bits // depending on whether the filter (aka "speaker emulation") setting is // active. if (sw_settings->input_opt[0] & FF_SWPARAM_FF800_INPUT_OPT_FRONT) { data[1] |= (sw_settings->filter) ? CR1_FF800_INPUT1_FRONT_WITH_FILTER : CR1_FF800_INPUT1_FRONT; } data[2] |= (sw_settings->spdif_output_emphasis==FF_SWPARAM_SPDIF_OUTPUT_EMPHASIS_ON) ? CR2_SPDIF_OUT_EMP : 0; data[2] |= (sw_settings->spdif_output_pro==FF_SWPARAM_SPDIF_OUTPUT_PRO_ON) ? CR2_SPDIF_OUT_PRO : 0; data[2] |= (sw_settings->spdif_output_nonaudio==FF_SWPARAM_SPDIF_OUTPUT_NONAUDIO_ON) ? CR2_SPDIF_OUT_NONAUDIO : 0; data[2] |= (sw_settings->spdif_output_mode==FF_SWPARAM_SPDIF_OUTPUT_OPTICAL) ? CR2_SPDIF_OUT_ADAT2 : 0; data[2] |= (sw_settings->clock_mode==FF_SWPARAM_CLOCK_MODE_AUTOSYNC) ? CR2_CLOCKMODE_AUTOSYNC : CR2_CLOCKMODE_MASTER; data[2] |= (sw_settings->spdif_input_mode==FF_SWPARAM_SPDIF_INPUT_COAX) ? CR2_SPDIF_IN_COAX : CR2_SPDIF_IN_ADAT2; data[2] |= (sw_settings->word_clock_single_speed=FF_SWPARAM_WORD_CLOCK_1x) ? CR2_WORD_CLOCK_1x : 0; /* TMS / TCO toggle bits in CR2 are not set by other drivers */ /* Drive / fuzz in FF800. In FF400, the CR0 bit used by "Drive" controls * the channel 3 "instrument" option. */ if (m_rme_model == RME_MODEL_FIREFACE800) { if (sw_settings->fuzz) data[0] |= CR0_FF800_DRIVE_FPGA; // FPGA LED control else data[1] |= CR1_INSTR_DRIVE; // CPLD } else if (m_rme_model == RME_MODEL_FIREFACE400) { data[0] |= (sw_settings->ff400_instr_input[0]) ? CR0_FF400_CH3_INSTR : 0; } /* Drop-and-stop is hardwired on in other drivers */ data[2] |= CR2_DROP_AND_STOP; if (m_rme_model==RME_MODEL_FIREFACE400 && !provide_midi) { // If libffado is not providing MIDI, configure the register to // allow snd-fireface (Linux kernel >= 4.12) - or any other driver // for the FF400 which might appear in future - to do so if desired. // The choice of tx address 1 matches that which is coded in // snd-fireface as of kernel 4.12. data[2] &= ~CR2_FF400_DISABLE_MIDI_TX_MASK; data[2] |= CR2_FF400_SELECT_MIDI_TX_ADDR_1; } switch (sw_settings->sync_ref) { case FF_SWPARAM_SYNCREF_WORDCLOCK: data[2] |= CR2_SYNC_WORDCLOCK; break; case FF_SWPARAM_SYNCREF_ADAT1: data[2] |= CR2_SYNC_ADAT1; break; case FF_SWPARAM_SYNCREF_ADAT2: data[2] |= CR2_SYNC_ADAT2; break; case FF_SWPARAM_SYNCREF_SPDIF: data[2] |= CR2_SYNC_SPDIF; break; case FF_SWPARAM_SYNCREC_TCO: data[2] |= CR2_SYNC_TCO; break; } // This is hardwired in other drivers data[2] |= (CR2_FREQ0 + CR2_FREQ1 + CR2_DSPEED + CR2_QSSPEED); // The FF800 limiter can only be disabled if the front panel instrument // input is in use, so it only makes sense that it is disabled when that // input is in use. data[2] |= (sw_settings->limiter==0 && (sw_settings->input_opt[0]==FF_SWPARAM_FF800_INPUT_OPT_FRONT)) ? CR2_DISABLE_LIMITER : 0; //This is just for testing - it's a known consistent configuration //data[0] = 0x00020810; // Phantom off //data[0] = 0x00020811; // Phantom on //data[1] = 0x0000031e; //data[2] = 0xc400101f; debugOutput(DEBUG_LEVEL_VERBOSE, "set hardware registers: 0x%08x 0x%08x 0x%08x\n", data[0], data[1], data[2]); switch (m_rme_model) { case RME_MODEL_FIREFACE800: conf_reg = RME_FF800_CONF_REG; break; case RME_MODEL_FIREFACE400: conf_reg = RME_FF400_CONF_REG; break; default: debugOutput(DEBUG_LEVEL_ERROR, "unimplemented model %d\n", m_rme_model); return -1; } if (writeBlock(conf_reg, data, 3) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to write device settings\n"); return -1; } return 0; } signed int Device::read_tco(quadlet_t *tco_data, signed int size) { // Read the TCO registers and return the respective values in *tco_data. // Return value is 0 on success, or -1 if there is no TCO present. // "size" is the size (in quadlets) of the array pointed to by tco_data. // To obtain all TCO data "size" should be at least 4. If the caller // doesn't care about the data returned by the TCO, tco_data can be // NULL. quadlet_t buf[4]; signed int i; // Only the Fireface 800 can have the TCO fitted if (m_rme_model != RME_MODEL_FIREFACE800) return -1; if (readBlock(RME_FF_TCO_READ_REG, buf, 4) != 0) return -1; if (tco_data != NULL) { for (i=0; i<((size<4)?size:4); i++) tco_data[i] = buf[i]; } if ( (buf[0] & 0x80808080) == 0x80808080 && (buf[1] & 0x80808080) == 0x80808080 && (buf[2] & 0x80808080) == 0x80808080 && (buf[3] & 0x8000FFFF) == 0x80008000) { // A TCO is present return 0; } return -1; } signed int Device::write_tco(quadlet_t *tco_data, signed int size) { // Writes data to the TCO. No check is made as to whether a TCO is // present in the current device. Return value is 0 on success or -1 on // error. "size" is the size (in quadlets) of the data pointed to by // "tco_data". The first 4 quadlets of tco_data are significant; all // others are ignored. If fewer than 4 quadlets are supplied (as // indicated by the "size" parameter, -1 will be returned. if (size < 4) return -1; // Don't bother trying to write if the device is not a FF800 since the // TCO can only be fitted to a FF800. if (m_rme_model != RME_MODEL_FIREFACE800) return -1; if (writeBlock(RME_FF_TCO_WRITE_REG, tco_data, 4) != 0) return -1; return 0; } signed int Device::hardware_is_streaming(void) { // Return 1 if the hardware is streaming, 0 if not. return dev_config->is_streaming; } signed int Device::read_tco_state(FF_TCO_state_t *tco_state) { // Reads the current TCO state into the supplied state structure quadlet_t tc[4]; unsigned int PLL_phase; if (read_tco(tc, 4) != 0) return -1; // The timecode is stored in BCD (binary coded decimal) in register 0. tco_state->frames = (tc[0] & 0xf) + ((tc[0] & 0x30) >> 4)*10; tco_state->seconds = ((tc[0] & 0xf00) >> 8) + ((tc[0] & 0x7000) >> 12)*10; tco_state->minutes = ((tc[0] & 0xf0000) >> 16) + ((tc[0] & 0x700000) >> 20)*10; tco_state->hours = ((tc[0] & 0xf000000) >> 24) + ((tc[0] & 0x30000000) >> 28)*10; tco_state->locked = (tc[1] & FF_TCO1_TCO_lock) != 0; tco_state->ltc_valid = (tc[1] & FF_TCO1_LTC_INPUT_VALID) != 0; switch (tc[1] & FF_TCO1_LTC_FORMAT_MASK) { case FF_TC01_LTC_FORMAT_24fps: tco_state->frame_rate = FF_TCOSTATE_FRAMERATE_24fps; break; case FF_TCO1_LTC_FORMAT_25fps: tco_state->frame_rate = FF_TCOSTATE_FRAMERATE_25fps; break; case FF_TC01_LTC_FORMAT_29_97fps: tco_state->frame_rate = FF_TCOSTATE_FRAMERATE_29_97fps; break; case FF_TCO1_LTC_FORMAT_30fps: tco_state->frame_rate = FF_TCOSTATE_FRAMERATE_30fps; break; } tco_state->drop_frame = (tc[1] & FF_TCO1_SET_DROPFRAME) != 0; switch (tc[1] & FF_TCO1_VIDEO_INPUT_MASK) { case FF_TCO1_VIDEO_INPUT_NTSC: tco_state->video_input = FF_TCOSTATE_VIDEO_NTSC; break; case FF_TCO1_VIDEO_INPUT_PAL: tco_state->video_input = FF_TCOSTATE_VIDEO_PAL; break; default: tco_state->video_input = FF_TCOSTATE_VIDEO_NONE; } if ((tc[1] & FF_TCO1_WORD_CLOCK_INPUT_VALID) == 0) { tco_state->word_clock_state = FF_TCOSTATE_WORDCLOCK_NONE; } else { switch (tc[1] & FF_TCO1_WORD_CLOCK_INPUT_MASK) { case FF_TCO1_WORD_CLOCK_INPUT_1x: tco_state->word_clock_state = FF_TCOSTATE_WORDCLOCK_1x; break; case FF_TCO1_WORD_CLOCK_INPUT_2x: tco_state->word_clock_state = FF_TCOSTATE_WORDCLOCK_2x; break; case FF_TCO1_WORD_CLOCK_INPUT_4x: tco_state->word_clock_state = FF_TCOSTATE_WORDCLOCK_4x; break; } } PLL_phase = (tc[2] & 0x7f) + ((tc[2] & 0x7f00) >> 1); tco_state->sample_rate = (25000000.0 * 16.0)/PLL_phase; return 0; } signed int Device::write_tco_settings(FF_TCO_settings_t *tco_settings) { // Writes the supplied application-level settings to the device's TCO // (Time Code Option). Don't bother doing anything if the device doesn't // have a TCO fitted. Returns 0 on success, -1 on error. quadlet_t tc[4] = {0, 0, 0, 0}; if (!dev_config->tco_present) { return -1; } if (tco_settings->MTC) tc[0] |= FF_TCO0_MTC; switch (tco_settings->input) { case FF_TCOPARAM_INPUT_LTC: tc[2] |= FF_TCO2_INPUT_LTC; break; case FF_TCOPARAM_INPUT_VIDEO: tc[2] |= FF_TCO2_INPUT_VIDEO; break; case FF_TCOPARAM_INPUT_WCK: tc[2] |= FF_TCO2_INPUT_WORD_CLOCK; break; } switch (tco_settings->frame_rate) { case FF_TCOPARAM_FRAMERATE_24fps: tc[1] |= FF_TC01_LTC_FORMAT_24fps; break; case FF_TCOPARAM_FRAMERATE_25fps: tc[1] |= FF_TCO1_LTC_FORMAT_25fps; break; case FF_TCOPARAM_FRAMERATE_29_97fps: tc[1] |= FF_TC01_LTC_FORMAT_29_97fps; break; case FF_TCOPARAM_FRAMERATE_29_97dfps: tc[1] |= FF_TCO1_LTC_FORMAT_29_97dpfs; break; case FF_TCOPARAM_FRAMERATE_30fps: tc[1] |= FF_TCO1_LTC_FORMAT_30fps; break; case FF_TCOPARAM_FRAMERATE_30dfps: tc[1] |= FF_TCO1_LTC_FORMAT_30dfps; break; } switch (tco_settings->word_clock) { case FF_TCOPARAM_WORD_CLOCK_CONV_1_1: tc[2] |= FF_TCO2_WORD_CLOCK_CONV_1_1; break; case FF_TCOPARAM_WORD_CLOCK_CONV_44_48: tc[2] |= FF_TCO2_WORD_CLOCK_CONV_44_48; break; case FF_TCOPARAM_WORD_CLOCK_CONV_48_44: tc[2] |= FF_TCO2_WORD_CLOCK_CONV_48_44; break; } switch (tco_settings->sample_rate) { case FF_TCOPARAM_SRATE_44_1: tc[2] |= FF_TCO2_SRATE_44_1; break; case FF_TCOPARAM_SRATE_48: tc[2] |= FF_TCO2_SRATE_48; break; case FF_TCOPARAM_SRATE_FROM_APP: tc[2] |= FF_TCO2_SRATE_FROM_APP; break; } switch (tco_settings->pull) { case FF_TCOPARAM_PULL_NONE: tc[2] |= FF_TCO2_PULL_0; break; case FF_TCOPARAM_PULL_UP_01: tc[2] |= FF_TCO2_PULL_UP_01; break; case FF_TCOPARAM_PULL_DOWN_01: tc[2] |= FF_TCO2_PULL_DOWN_01; break; case FF_TCOPARAM_PULL_UP_40: tc[2] |= FF_TCO2_PULL_UP_40; break; case FF_TCOPARAM_PULL_DOWN_40: tc[2] |= FF_TCO2_PULL_DOWN_40; break; } if (tco_settings->termination == FF_TCOPARAM_TERMINATION_ON) tc[2] |= FF_TCO2_SET_TERMINATION; return write_tco(tc, 4); } signed int Device::set_hardware_dds_freq(signed int freq) { // Set the device's DDS to the given frequency (which in turn determines // the sampling frequency). Returns 0 on success, -1 on error. unsigned int ret = 0; if (freq < MIN_SPEED || freq > MAX_SPEED) return -1; switch (m_rme_model) { case RME_MODEL_FIREFACE400: ret = writeRegister(RME_FF400_STREAM_SRATE, freq); break; case RME_MODEL_FIREFACE800: ret = writeRegister(RME_FF800_STREAM_SRATE, freq); break; default: debugOutput(DEBUG_LEVEL_ERROR, "unimplemented model %d\n", m_rme_model); ret = -1; } if (ret == 0) dev_config->hardware_freq = freq; else debugOutput(DEBUG_LEVEL_ERROR, "failed to write DDS register\n"); return ret; } signed int Device::hardware_init_streaming(unsigned int sample_rate, unsigned int tx_channel) { // tx_channel is the ISO channel the PC will transmit on. quadlet_t buf[5]; fb_nodeaddr_t addr; unsigned int size; signed int ret; debugOutput(DEBUG_LEVEL_VERBOSE, "*** stream init: %d, %d, %d\n", sample_rate, num_channels, tx_channel); buf[0] = sample_rate; buf[1] = (num_channels << 11) + tx_channel; buf[2] = num_channels; buf[3] = 0; buf[4] = 0; if (speed800) { buf[2] |= RME_FF800_STREAMING_SPEED_800; } if (m_rme_model == RME_MODEL_FIREFACE400) { addr = RME_FF400_STREAM_INIT_REG; size = RME_FF400_STREAM_INIT_SIZE; } else if (m_rme_model == RME_MODEL_FIREFACE800) { addr = RME_FF800_STREAM_INIT_REG; size = RME_FF800_STREAM_INIT_SIZE; } else { debugOutput(DEBUG_LEVEL_ERROR, "unimplemented model %d\n", m_rme_model); return -1; } ret = writeBlock(addr, buf, size); if (ret != 0) debugOutput(DEBUG_LEVEL_ERROR, "failed to write streaming parameters\n"); return ret; } signed int Device::hardware_start_streaming(unsigned int listen_channel) { signed int ret = 0; // Listen_channel is the ISO channel the PC will listen on for data sent // by the Fireface. fb_nodeaddr_t addr; quadlet_t data = num_channels; config_lock(); if (not(hardware_is_streaming())) { debugOutput(DEBUG_LEVEL_VERBOSE,"*** starting: listen=%d, num_ch=%d\n", listen_channel, num_channels); if (m_rme_model == RME_MODEL_FIREFACE400) { addr = RME_FF400_STREAM_START_REG; data |= (listen_channel << 5); } else if (m_rme_model == RME_MODEL_FIREFACE800) { addr = RME_FF800_STREAM_START_REG; if (speed800) data |= RME_FF800_STREAMING_SPEED_800; // Flag 800 Mbps speed } else { debugOutput(DEBUG_LEVEL_ERROR, "unimplemented model %d\n", m_rme_model); return -1; } debugOutput(DEBUG_LEVEL_VERBOSE, "start 0x%016" PRIx64 " data: %08x\n", addr, data); ret = writeRegister(addr, data); debugOutput(DEBUG_LEVEL_VERBOSE, " ret=%d\n", ret); if (ret == 0) { dev_config->is_streaming = 1; } else debugOutput(DEBUG_LEVEL_ERROR, "failed to write for streaming start\n"); set_hardware_channel_mute(-1, 0); } else ret = 0; config_unlock(); return ret; } signed int Device::hardware_stop_streaming(void) { fb_nodeaddr_t addr; quadlet_t buf[4] = {0, 0, 0, 1}; unsigned int size, ret = 0; config_lock(); if (hardware_is_streaming()) { if (m_rme_model == RME_MODEL_FIREFACE400) { addr = RME_FF400_STREAM_END_REG; size = RME_FF400_STREAM_END_SIZE; } else if (m_rme_model == RME_MODEL_FIREFACE800) { addr = RME_FF800_STREAM_END_REG; size = RME_FF800_STREAM_END_SIZE; } else { debugOutput(DEBUG_LEVEL_ERROR, "unimplemented model %d\n", m_rme_model); return -1; } ret = writeBlock(addr, buf, size); if (ret == 0) { dev_config->is_streaming = 0; } else debugOutput(DEBUG_LEVEL_ERROR, "failed to write for streaming stop\n"); set_hardware_channel_mute(-1, 1); } else ret = 0; config_unlock(); return ret; } signed int Device::set_hardware_ampgain(unsigned int index, signed int val) { // "index" indicates the hardware amplifier gain to set. Values of 0-3 // correspond to input amplifier gains. Values from 4 on relate to output // volume. // // "val" is in dB except for inputs 3/4 where it's in units of 0.5 dB. This // function is responsible for converting to/from the scale used by the // device. // // Only the FF400 has the hardware gain register which is controlled by this // function. quadlet_t regval = 0; signed int devval = 0; signed int ret; if (val > 120) val = 120; if (val < -120) val = -120; if (index <= FF400_AMPGAIN_MIC2) { if (val >= 10) devval = val; else devval = 0; } else if (index <= FF400_AMPGAIN_INPUT4) { devval = val; } else { devval = 6 - val; if (devval > 53) devval = 0x3f; // Mute } regval |= devval; regval |= (index << 16); ret = writeRegister(RME_FF400_GAIN_REG, regval); if (ret != 0) debugOutput(DEBUG_LEVEL_ERROR, "failed to write amp gains\n"); return ret; } signed int Device::set_hardware_mixergain(unsigned int ctype, unsigned int src_channel, unsigned int dest_channel, signed int val) { // Set the value of a matrix mixer control. ctype is one of the RME_FF_MM_* // defines: // RME_FF_MM_INPUT: source is a physical input // RME_FF_MM_PLAYBACK: source is playback from PC // RME_FF_MM_OUTPUT: source is the physical output whose gain is to be // changed, destination is ignored // Val is the integer value sent to the device. The amount of gain (in dB) // applied can be calculated using // dB = 20.log10(val/32768) // The maximum value of val is 0x10000, corresponding to +6dB of gain. // The minimum is 0x00000 corresponding to mute. unsigned int n_channels; signed int ram_output_block_size; unsigned int ram_addr; if (m_rme_model == RME_MODEL_FIREFACE400) { n_channels = RME_FF400_MAX_CHANNELS; ram_output_block_size = 0x48; } else if (m_rme_model == RME_MODEL_FIREFACE800) { n_channels = RME_FF800_MAX_CHANNELS; ram_output_block_size = 0x80; } else { debugOutput(DEBUG_LEVEL_ERROR, "unimplemented model %d\n", m_rme_model); return -1; } if (src_channel>n_channels || dest_channel>n_channels) return -1; if (abs(val)>0x10000) return -1; ram_addr = RME_FF_MIXER_RAM; switch (ctype) { case RME_FF_MM_INPUT: case RME_FF_MM_PLAYBACK: ram_addr += (dest_channel*2*ram_output_block_size) + 4*src_channel; if (ctype == RME_FF_MM_PLAYBACK) ram_addr += ram_output_block_size; break; case RME_FF_MM_OUTPUT: if (m_rme_model == RME_MODEL_FIREFACE400) ram_addr += 0x0f80; else ram_addr += 0x1f80; ram_addr += 4*src_channel; break; } if (writeRegister(ram_addr, val) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to write mixer gain element\n"); } // If setting the output volume and the device is the FF400, keep // the separate gain register in sync. if (ctype==RME_FF_MM_OUTPUT && m_rme_model==RME_MODEL_FIREFACE400) { signed int dB; if (val < 0) val = -val; if (val==0) dB = -90; else dB = roundl(20.0*log10(val/32768.0)); set_hardware_ampgain(FF400_AMPGAIN_OUTPUT1+src_channel, dB); } return 0; } signed int Device::set_hardware_channel_mute(signed int chan, signed int mute) { // Mute hardware channels as instructed. This mute probably relates to the // sampled input channels as delivered to the PC. If "chan" is -1 the // supplied "mute" status is applied to all channels. This is the only // supported "chan" value for now. Down the track, if there's a need, // this could be extended to allow individual channel control. quadlet_t buf[28]; signed int i; signed int n_channels; if (m_rme_model == RME_MODEL_FIREFACE400) n_channels = RME_FF400_MAX_CHANNELS; else if (m_rme_model == RME_MODEL_FIREFACE800) n_channels = RME_FF800_MAX_CHANNELS; else { debugOutput(DEBUG_LEVEL_ERROR, "unimplemented model %d\n", m_rme_model); return -1; } i = 0; if (chan < 0) { while (i. * */ #include #include "rme/rme_avdevice.h" #include "rme/fireface_settings_ctrls.h" namespace Rme { RmeSettingsCtrl::RmeSettingsCtrl(Device &parent, unsigned int type, unsigned int info) : Control::Discrete(&parent) , m_parent(parent) , m_type(type) , m_value(0) , m_info(info) { } RmeSettingsCtrl::RmeSettingsCtrl(Device &parent, unsigned int type, unsigned int info, std::string name, std::string label, std::string descr) : Control::Discrete(&parent) , m_parent(parent) , m_type(type) , m_value(0) , m_info(info) { setName(name); setLabel(label); setDescription(descr); } bool RmeSettingsCtrl::setValue(int v) { signed int i; signed int err = 0; if (m_type>=RME_CTRL_TCO_FIRST && m_type<=RME_CTRL_TCO_LAST) { } switch (m_type) { case RME_CTRL_NONE: debugOutput(DEBUG_LEVEL_ERROR, "control has no type set\n"); err = 1; break; case RME_CTRL_PHANTOM_SW: // Lowest 16 bits are phantom status bits (max 16 channels). // High 16 bits are "write enable" bits for the corresponding // channel represented in the low 16 bits. This way changes can // be made to one channel without needing to first read the // existing value. // // At present there are at most 4 phantom-capable channels. // Flag attempts to write to the bits corresponding to channels // beyond this. if (v & 0xfff00000) { debugOutput(DEBUG_LEVEL_WARNING, "Ignored out-of-range phantom set request: mask=0x%04x, value=0x%04x\n", (v >> 16) & 0xfff0, v && 0xfff0); } for (i=0; i<4; i++) { if (v & (0x00010000 << i)) { unsigned int on = (v & (0x00000001 << i)) != 0; err = m_parent.setPhantom(i, on); if (!err) { if (on) { m_value |= (0x01 << i); } else { m_value &= ~(0x01 << i); } } } } break; case RME_CTRL_INPUT_LEVEL: switch (v) { case 0: i = FF_SWPARAM_ILEVEL_LOGAIN; break; case 1: i = FF_SWPARAM_ILEVEL_m10dBV; break; default: i = FF_SWPARAM_ILEVEL_4dBU; } if (m_parent.setInputLevel(i) == 0) { m_value = v; } break; case RME_CTRL_OUTPUT_LEVEL: switch (v) { case 0: i = FF_SWPARAM_OLEVEL_m10dBV; break; case 1: i = FF_SWPARAM_OLEVEL_4dBU; break; default: i = FF_SWPARAM_OLEVEL_HIGAIN; break; } if (m_parent.setOutputLevel(i) == 0) { m_value = v; } break; case RME_CTRL_FF400_PAD_SW: // Object's "m_info" field is the channel if (m_parent.setInputPadOpt(m_info, v) == 0) { m_value = (v != 0); } break; case RME_CTRL_FF400_INSTR_SW: // Object's "m_info" field is the channel if (m_parent.setInputInstrOpt(m_info, v) == 0) { m_value = (v != 0); } break; case RME_CTRL_INPUT_SOURCE: { // m_info is the channel number signed int src = 0; if (v==0 || v==2) src |= FF_SWPARAM_FF800_INPUT_OPT_FRONT; if (v==1 || v==2) src |= FF_SWPARAM_FF800_INPUT_OPT_REAR; if (m_parent.setInputSource(m_info, src) == 0) m_value = src; break; } case RME_CTRL_INSTRUMENT_OPTIONS: // m_info is the channel number, v is expected to be a bitmap // comprised of the FF800_INSTR_OPT_* values. if (m_parent.setInputInstrOpt(m_info, v) == 0) m_value = v; break; case RME_CTRL_SPDIF_INPUT_MODE: if (m_parent.setSpdifInputMode(v==0?FF_SWPARAM_SPDIF_INPUT_COAX:FF_SWPARAM_SPDIF_INPUT_OPTICAL)) { m_value = v; } break; case RME_CTRL_SPDIF_OUTPUT_OPTICAL: if (m_parent.setSpdifOutputIsOptical(v!=0) == 0) { m_value = (v != 0); } break; case RME_CTRL_SPDIF_OUTPUT_PRO: if (m_parent.setSpdifOutputProOn(v!=0) == 0) { m_value = (v != 0); } break; case RME_CTRL_SPDIF_OUTPUT_EMPHASIS: if (m_parent.setSpdifOutputEmphasisOn(v!=0) == 0) { m_value = (v != 0); } break; case RME_CTRL_SPDIF_OUTPUT_NONAUDIO: if (m_parent.setSpdifOutputNonAudioOn(v!=0) == 0) { m_value = (v != 0); } break; case RME_CTRL_PHONES_LEVEL: switch (v) { case 0: i = FF_SWPARAM_PHONESLEVEL_HIGAIN; break; case 1: i = FF_SWPARAM_PHONESLEVEL_4dBU; break; default: i = FF_SWPARAM_PHONESLEVEL_m10dBV; } if (m_parent.setPhonesLevel(i) == 0) { m_value = v; } break; case RME_CTRL_CLOCK_MODE: if (m_parent.setClockMode(v==1?FF_SWPARAM_CLOCK_MODE_AUTOSYNC:FF_SWPARAM_CLOCK_MODE_MASTER) == 0) { m_value = v; } break; case RME_CTRL_SYNC_REF: { signed int val; switch (v) { case 0: val = FF_SWPARAM_SYNCREF_WORDCLOCK; break; case 1: val = FF_SWPARAM_SYNCREF_ADAT1; break; case 2: val = FF_SWPARAM_SYNCREF_ADAT2; break; case 3: val = FF_SWPARAM_SYNCREF_SPDIF; break; case 4: val = FF_SWPARAM_SYNCREC_TCO; break; default: val = FF_SWPARAM_SYNCREF_WORDCLOCK; } if (m_parent.setSyncRef(val) == 0) { m_value = v; } break; } case RME_CTRL_LIMIT_BANDWIDTH: { signed int val; switch (v) { case 0: val = FF_DEV_FLASH_BWLIMIT_SEND_ALL_CHANNELS; break; case 1: val = FF_DEV_FLASH_BWLIMIT_NO_ADAT2; break; case 2: val = FF_DEV_FLASH_BWLIMIT_ANALOG_SPDIF_ONLY; break; case 3: val = FF_DEV_FLASH_BWLIMIT_ANALOG_ONLY; break; default: val = FF_DEV_FLASH_BWLIMIT_SEND_ALL_CHANNELS; } if (m_parent.setBandwidthLimit(val) == 0) { m_value = v; } break; } case RME_CTRL_FLASH: switch (v) { case RME_CTRL_FLASH_SETTINGS_LOAD: m_parent.read_device_flash_settings(NULL); break; case RME_CTRL_FLASH_SETTINGS_SAVE: m_parent.write_device_flash_settings(NULL); break; case RME_CTRL_FLASH_MIXER_LOAD: m_parent.read_device_mixer_settings(NULL); break; case RME_CTRL_FLASH_MIXER_SAVE: m_parent.write_device_mixer_settings(NULL); break; default: debugOutput(DEBUG_LEVEL_ERROR, "unknown command value %d for flash control type 0x%04x\n", v, m_type); err = 1; } break; case RME_CTRL_MIXER_PRESET: debugOutput(DEBUG_LEVEL_VERBOSE, "mixer presets not implemented yet\n"); break; // All RME_CTRL_INFO_* controls are read-only. Warn on attempts to // set these. case RME_CTRL_INFO_MODEL: case RME_CTRL_INFO_TCO_PRESENT: case RME_CTRL_INFO_SYSCLOCK_MODE: case RME_CTRL_INFO_SYSCLOCK_FREQ: case RME_CTRL_INFO_AUTOSYNC_FREQ: case RME_CTRL_INFO_AUTOSYNC_SRC: case RME_CTRL_INFO_SYNC_STATUS: case RME_CTRL_INFO_SPDIF_FREQ: debugOutput(DEBUG_LEVEL_ERROR, "Attempt to set readonly info control 0x%08x\n", m_type); err = 1; break; case RME_CTRL_TCO_LTC_IN: case RME_CTRL_TCO_INPUT_LTC_VALID: case RME_CTRL_TCO_INPUT_LTC_FPS: case RME_CTRL_TCO_INPUT_LTC_DROPFRAME: case RME_CTRL_TCO_INPUT_VIDEO_TYPE: case RME_CTRL_TCO_INPUT_WORD_CLK: case RME_CTRL_TCO_INPUT_LOCK: case RME_CTRL_TCO_FREQ: debugOutput(DEBUG_LEVEL_ERROR, "Attempt to set readonly TCO control 0x%08x\n", m_type); err = 1; break; case RME_CTRL_TCO_SYNC_SRC: switch (v) { case 0: i = FF_TCOPARAM_INPUT_LTC; break; case 1: i = FF_TCOPARAM_INPUT_VIDEO; break; case 2: i = FF_TCOPARAM_INPUT_VIDEO; break; default: i = FF_TCOPARAM_INPUT_VIDEO; } return m_parent.setTcoSyncSrc(i); break; case RME_CTRL_TCO_FRAME_RATE: switch (v) { case 0: i = FF_TCOPARAM_FRAMERATE_24fps; break; case 1: i = FF_TCOPARAM_FRAMERATE_25fps; break; case 2: i = FF_TCOPARAM_FRAMERATE_29_97fps; break; case 3: i = FF_TCOPARAM_FRAMERATE_29_97dfps; break; case 4: i = FF_TCOPARAM_FRAMERATE_30fps; break; case 5: i = FF_TCOPARAM_FRAMERATE_30dfps; break; default: i = FF_TCOPARAM_FRAMERATE_25fps; break; } return m_parent.setTcoFrameRate(i); break; case RME_CTRL_TCO_SAMPLE_RATE: switch (v) { case 0: i = FF_TCOPARAM_SRATE_44_1; break; case 1: i = FF_TCOPARAM_SRATE_48; break; default: i = FF_TCOPARAM_SRATE_48; break; } return m_parent.setTcoSampleRate(i); break; case RME_CTRL_TCO_SAMPLE_RATE_OFS: switch (v) { case 0: i = FF_TCOPARAM_PULL_NONE; break; case 1: i = FF_TCOPARAM_PULL_UP_01; break; case 2: i = FF_TCOPARAM_PULL_DOWN_01; break; case 3: i = FF_TCOPARAM_PULL_UP_40; break; case 4: i = FF_TCOPARAM_PULL_DOWN_40; break; default: i = FF_TCOPARAM_PULL_NONE; } return m_parent.setTcoPull(i); break; case RME_CTRL_TCO_VIDEO_IN_TERM: return m_parent.setTcoTermination(v == 1); break; case RME_CTRL_TCO_WORD_CLK_CONV: switch (v) { case 0: i = FF_TCOPARAM_WORD_CLOCK_CONV_1_1; break; case 1: i = FF_TCOPARAM_WORD_CLOCK_CONV_44_48; break; case 2: i = FF_TCOPARAM_WORD_CLOCK_CONV_48_44; break; default: i = FF_TCOPARAM_WORD_CLOCK_CONV_1_1; } return m_parent.setTcoWordClkConv(i); break; default: debugOutput(DEBUG_LEVEL_ERROR, "Unknown control type 0x%08x\n", m_type); err = 1; } return err==0?true:false; } int RmeSettingsCtrl::getValue() { signed int i; signed int val = 0; FF_state_t ff_state; switch (m_type) { case RME_CTRL_NONE: debugOutput(DEBUG_LEVEL_ERROR, "control has no type set\n"); break; case RME_CTRL_PHANTOM_SW: for (i=0; i<3; i++) val |= (m_parent.getPhantom(i) << i); return val; break; case RME_CTRL_INPUT_LEVEL: switch (m_parent.getInputLevel()) { case FF_SWPARAM_ILEVEL_LOGAIN: return 0; case FF_SWPARAM_ILEVEL_m10dBV: return 1; default: return 2; } break; case RME_CTRL_OUTPUT_LEVEL: switch (m_parent.getOutputLevel()) { case FF_SWPARAM_OLEVEL_m10dBV: return 0; case FF_SWPARAM_OLEVEL_4dBU: return 1; default: return 2; } break; case RME_CTRL_FF400_PAD_SW: return m_parent.getInputPadOpt(m_info); break; case RME_CTRL_FF400_INSTR_SW: return m_parent.getInputInstrOpt(m_info); break; case RME_CTRL_INPUT_SOURCE: { signed int src; src = m_parent.getInputSource(m_info); if (src == FF_SWPARAM_FF800_INPUT_OPT_FRONT) return 0; if (src == FF_SWPARAM_FF800_INPUT_OPT_REAR) return 1; return 2; break; } case RME_CTRL_INSTRUMENT_OPTIONS: return m_parent.getInputInstrOpt(m_info); break; case RME_CTRL_SPDIF_INPUT_MODE: i = m_parent.getSpdifInputMode(); return i==FF_SWPARAM_SPDIF_INPUT_COAX?0:1; break; case RME_CTRL_SPDIF_OUTPUT_OPTICAL: return m_parent.getSpdifOutputIsOptical(); break; case RME_CTRL_SPDIF_OUTPUT_PRO: return m_parent.getSpdifOutputProOn(); break; case RME_CTRL_SPDIF_OUTPUT_EMPHASIS: return m_parent.getSpdifOutputEmphasisOn(); break; case RME_CTRL_SPDIF_OUTPUT_NONAUDIO: return m_parent.getSpdifOutputNonAudioOn(); break; case RME_CTRL_PHONES_LEVEL: return m_parent.getPhonesLevel(); break; case RME_CTRL_CLOCK_MODE: return m_parent.getClockMode()==FF_SWPARAM_CLOCK_MODE_AUTOSYNC?1:0; break; case RME_CTRL_SYNC_REF: { signed int val = m_parent.getSyncRef(); switch (val) { case FF_SWPARAM_SYNCREF_WORDCLOCK: return 0; case FF_SWPARAM_SYNCREF_ADAT1: return 1; case FF_SWPARAM_SYNCREF_ADAT2: return 2; case FF_SWPARAM_SYNCREF_SPDIF: return 3; case FF_SWPARAM_SYNCREC_TCO: return 4; default: return 0; } break; } case RME_CTRL_LIMIT_BANDWIDTH: { signed int val = m_parent.getBandwidthLimit(); switch (val) { case FF_DEV_FLASH_BWLIMIT_SEND_ALL_CHANNELS: return 0; case FF_DEV_FLASH_BWLIMIT_NO_ADAT2: return 1; case FF_DEV_FLASH_BWLIMIT_ANALOG_SPDIF_ONLY: return 2; case FF_DEV_FLASH_BWLIMIT_ANALOG_ONLY: return 3; default: return 0; } break; } case RME_CTRL_INFO_MODEL: return m_parent.getRmeModel(); break; case RME_CTRL_FLASH: case RME_CTRL_MIXER_PRESET: debugOutput(DEBUG_LEVEL_ERROR, "read requested for write-only control type 0x%04x\n", m_type); return 0; break; case RME_CTRL_INFO_TCO_PRESENT: return m_parent.getTcoPresent(); case RME_CTRL_INFO_SYSCLOCK_MODE: if (m_parent.get_hardware_state(&ff_state) == 0) return ff_state.clock_mode; else debugOutput(DEBUG_LEVEL_ERROR, "failed to read device state\n"); break; case RME_CTRL_INFO_SYSCLOCK_FREQ: return m_parent.getSamplingFrequency(); case RME_CTRL_INFO_AUTOSYNC_FREQ: if (m_parent.get_hardware_state(&ff_state) == 0) return ff_state.autosync_freq; else debugOutput(DEBUG_LEVEL_ERROR, "failed to read device state\n"); break; case RME_CTRL_INFO_AUTOSYNC_SRC: if (m_parent.get_hardware_state(&ff_state) == 0) return ff_state.autosync_source; else debugOutput(DEBUG_LEVEL_ERROR, "failed to read device state\n"); break; case RME_CTRL_INFO_SYNC_STATUS: if (m_parent.get_hardware_state(&ff_state) == 0) return (ff_state.adat1_sync_status) | (ff_state.adat2_sync_status << 2) | (ff_state.spdif_sync_status << 4) | (ff_state.wclk_sync_status << 6) | (ff_state.tco_sync_status << 8); else debugOutput(DEBUG_LEVEL_ERROR, "failed to read device state\n"); break; case RME_CTRL_INFO_SPDIF_FREQ: if (m_parent.get_hardware_state(&ff_state) == 0) return ff_state.spdif_freq; else debugOutput(DEBUG_LEVEL_ERROR, "failed to read device state\n"); break; case RME_CTRL_TCO_LTC_IN: return m_parent.getTcoLtc(); case RME_CTRL_TCO_INPUT_LTC_VALID: return m_parent.getTcoLtcValid(); break; case RME_CTRL_TCO_INPUT_LTC_FPS: switch (m_parent.getTcoLtcFrameRate()) { case FF_TCOSTATE_FRAMERATE_24fps: return 0; case FF_TCOSTATE_FRAMERATE_25fps: return 1; case FF_TCOSTATE_FRAMERATE_29_97fps: return 2; case FF_TCOSTATE_FRAMERATE_30fps: return 3; default: return 1; } break; case RME_CTRL_TCO_INPUT_LTC_DROPFRAME: return m_parent.getTcoLtcDropFrame(); case RME_CTRL_TCO_INPUT_VIDEO_TYPE: switch (m_parent.getTcoVideoType()) { case FF_TCOSTATE_VIDEO_NONE: return 0; case FF_TCOSTATE_VIDEO_PAL: return 1; case FF_TCOSTATE_VIDEO_NTSC: return 2; default: return 1; } break; case RME_CTRL_TCO_INPUT_WORD_CLK: switch (m_parent.getTcoWordClk()) { case FF_TCOSTATE_WORDCLOCK_NONE: return 0; case FF_TCOSTATE_WORDCLOCK_1x: return 1; case FF_TCOSTATE_WORDCLOCK_2x: return 2; case FF_TCOSTATE_WORDCLOCK_4x: return 3; default: return 0; } break; case RME_CTRL_TCO_INPUT_LOCK: return m_parent.getTcoLock(); break; case RME_CTRL_TCO_FREQ: return m_parent.getTcoFrequency(); break; case RME_CTRL_TCO_SYNC_SRC: switch (m_parent.getTcoSyncSrc()) { case FF_TCOPARAM_INPUT_LTC: return 0; case FF_TCOPARAM_INPUT_VIDEO: return 1; case FF_TCOPARAM_INPUT_WCK: return 2; default: return 1; } break; case RME_CTRL_TCO_FRAME_RATE: switch (m_parent.getTcoFrameRate()) { case FF_TCOPARAM_FRAMERATE_24fps: return 0; case FF_TCOPARAM_FRAMERATE_25fps: return 1; case FF_TCOPARAM_FRAMERATE_29_97fps: return 2; case FF_TCOPARAM_FRAMERATE_29_97dfps: return 3; case FF_TCOPARAM_FRAMERATE_30fps: return 4; case FF_TCOPARAM_FRAMERATE_30dfps: return 5; default: return 1; } break; case RME_CTRL_TCO_SAMPLE_RATE: switch (m_parent.getTcoSampleRate()) { case FF_TCOPARAM_SRATE_44_1: return 0; case FF_TCOPARAM_SRATE_48: return 1; default: return 1; } break; case RME_CTRL_TCO_SAMPLE_RATE_OFS: switch (m_parent.getTcoPull()) { case FF_TCOPARAM_PULL_NONE: return 0; case FF_TCOPARAM_PULL_UP_01: return 1; case FF_TCOPARAM_PULL_DOWN_01: return 2; case FF_TCOPARAM_PULL_UP_40: return 3; case FF_TCOPARAM_PULL_DOWN_40: return 4; default: return 0; } break; case RME_CTRL_TCO_VIDEO_IN_TERM: return m_parent.getTcoTermination(); break; case RME_CTRL_TCO_WORD_CLK_CONV: switch (m_parent.getTcoWordClkConv()) { case FF_TCOPARAM_WORD_CLOCK_CONV_1_1: return 0; case FF_TCOPARAM_WORD_CLOCK_CONV_44_48: return 1; case FF_TCOPARAM_WORD_CLOCK_CONV_48_44: return 2; default: return 0; } break; default: debugOutput(DEBUG_LEVEL_ERROR, "Unknown control type 0x%08x\n", m_type); } return 0; } static std::string getOutputName(const signed int model, const int idx) { char buf[64]; switch(model) { case RME_MODEL_FIREFACE400: if (idx >= 10) snprintf(buf, sizeof(buf), "ADAT out %d", idx-9); else if (idx >= 8) snprintf(buf, sizeof(buf), "SPDIF out %d", idx-7); else if (idx >= 6) snprintf(buf, sizeof(buf), "Mon out %d", idx+1); else snprintf(buf, sizeof(buf), "Line out %d", idx+1); break; case RME_MODEL_FIREFACE800: if (idx >= 20) snprintf(buf, sizeof(buf), "ADAT-2 out %d", idx-19); else if (idx >= 12) snprintf(buf, sizeof(buf), "ADAT-1 out %d", idx-11); else if (idx >= 10) snprintf(buf, sizeof(buf), "SPDIF out %d", idx-9); else if (idx >= 8) snprintf(buf, sizeof(buf), "Mon, ch %d", idx+1); else snprintf(buf, sizeof(buf), "Line out %d", idx+1); break; default: snprintf(buf, sizeof(buf), "out %d", idx); } return buf; } static std::string getInputName(const signed int model, const int idx) { char buf[64]; switch (model) { case RME_MODEL_FIREFACE400: if (idx >= 10) snprintf(buf, sizeof(buf), "ADAT in %d", idx-9); else if (idx >= 8) snprintf(buf, sizeof(buf), "SPDIF in %d", idx-7); else if (idx >= 4) snprintf(buf, sizeof(buf), "Line in %d", idx+1); else if (idx >= 2) snprintf(buf, sizeof(buf), "Inst/line %d", idx+1); else snprintf(buf, sizeof(buf), "Mic/line %d", idx+1); break; case RME_MODEL_FIREFACE800: if (idx >= 20) snprintf(buf, sizeof(buf), "ADAT-2 in %d", idx-19); else if (idx >= 12) snprintf(buf, sizeof(buf), "ADAT-1 in %d", idx-11); else if (idx >= 10) snprintf(buf, sizeof(buf), "SPDIF in %d", idx-9); else if (idx >= 6) snprintf(buf, sizeof(buf), "Mic/line %d", idx+1); else if (idx >= 1) snprintf(buf, sizeof(buf), "Line %d", idx+1); else snprintf(buf, sizeof(buf), "Instr/line %d", idx+1); break; default: snprintf(buf, sizeof(buf), "in %d", idx); } return buf; } RmeSettingsMatrixCtrl::RmeSettingsMatrixCtrl(Device &parent, unsigned int type) : Control::MatrixMixer(&parent) , m_parent(parent) , m_type(type) { } RmeSettingsMatrixCtrl::RmeSettingsMatrixCtrl(Device &parent, unsigned int type, std::string name) : Control::MatrixMixer(&parent) , m_parent(parent) , m_type(type) { setName(name); } void RmeSettingsMatrixCtrl::show() { debugOutput(DEBUG_LEVEL_NORMAL, "RME matrix mixer type %d\n", m_type); } std::string RmeSettingsMatrixCtrl::getRowName(const int row) { if (m_type == RME_MATRIXCTRL_OUTPUT_FADER) return ""; return getOutputName(m_parent.getRmeModel(), row); } std::string RmeSettingsMatrixCtrl::getColName(const int col) { if (m_type == RME_MATRIXCTRL_PLAYBACK_FADER) return ""; if (m_type == RME_MATRIXCTRL_OUTPUT_FADER) return getOutputName(m_parent.getRmeModel(), col); return getInputName(m_parent.getRmeModel(), col); } int RmeSettingsMatrixCtrl::getRowCount() { switch (m_type) { case RME_MATRIXCTRL_GAINS: if (m_parent.getRmeModel() == RME_MODEL_FIREFACE400) return 1; break; case RME_MATRIXCTRL_INPUT_FADER: case RME_MATRIXCTRL_PLAYBACK_FADER: if (m_parent.getRmeModel() == RME_MODEL_FIREFACE400) return RME_FF400_MAX_CHANNELS; else return RME_FF800_MAX_CHANNELS; break; case RME_MATRIXCTRL_OUTPUT_FADER: return 1; break; } return 0; } int RmeSettingsMatrixCtrl::getColCount() { switch (m_type) { case RME_MATRIXCTRL_GAINS: if (m_parent.getRmeModel() == RME_MODEL_FIREFACE400) return 22; break; case RME_MATRIXCTRL_INPUT_FADER: case RME_MATRIXCTRL_PLAYBACK_FADER: case RME_MATRIXCTRL_OUTPUT_FADER: if (m_parent.getRmeModel() == RME_MODEL_FIREFACE400) return RME_FF400_MAX_CHANNELS; else return RME_FF800_MAX_CHANNELS; break; } return 0; } double RmeSettingsMatrixCtrl::setValue(const int row, const int col, const double val) { signed int ret = true; signed int i; switch (m_type) { case RME_MATRIXCTRL_GAINS: i = val; if (i >= 0) ret = m_parent.setAmpGain(col, val); else ret = -1; break; // For values originating from the input, playback or output faders, // the MatrixMixer widget uses a value of 0x004000 for 0 dB gain. // The RME hardware (via setMixerGain()) uses 0x008000 as the 0 dB // reference point. Correct for this mismatch when calling // setMixerGain(). case RME_MATRIXCTRL_INPUT_FADER: return m_parent.setMixerGain(RME_FF_MM_INPUT, col, row, val*2); break; case RME_MATRIXCTRL_PLAYBACK_FADER: return m_parent.setMixerGain(RME_FF_MM_PLAYBACK, col, row, val*2); break; case RME_MATRIXCTRL_OUTPUT_FADER: return m_parent.setMixerGain(RME_FF_MM_OUTPUT, col, row, val*2); break; case RME_MATRIXCTRL_INPUT_MUTE: return m_parent.setMixerFlags(RME_FF_MM_INPUT, col, row, FF_SWPARAM_MF_MUTED, val!=0); break; case RME_MATRIXCTRL_PLAYBACK_MUTE: return m_parent.setMixerFlags(RME_FF_MM_PLAYBACK, col, row, FF_SWPARAM_MF_MUTED, val!=0); break; case RME_MATRIXCTRL_OUTPUT_MUTE: return m_parent.setMixerFlags(RME_FF_MM_OUTPUT, col, row, FF_SWPARAM_MF_MUTED, val!=0); break; case RME_MATRIXCTRL_INPUT_INVERT: return m_parent.setMixerFlags(RME_FF_MM_INPUT, col, row, FF_SWPARAM_MF_INVERTED, val!=0); break; case RME_MATRIXCTRL_PLAYBACK_INVERT: return m_parent.setMixerFlags(RME_FF_MM_PLAYBACK, col, row, FF_SWPARAM_MF_INVERTED, val!=0); break; } return ret; } double RmeSettingsMatrixCtrl::getValue(const int row, const int col) { double val = 0.0; switch (m_type) { case RME_MATRIXCTRL_GAINS: val = m_parent.getAmpGain(col); break; // The MatrixMixer widget (as used for the input, playback and // output faders) uses a value of 0x004000 for 0 dB gain, but the // gain value from the RME hardware (received getMixerGain()) uses // 0x008000 as the 0 dB reference point. Correct for this mismatch // as the value is obtained. case RME_MATRIXCTRL_INPUT_FADER: val = m_parent.getMixerGain(RME_FF_MM_INPUT, col, row) / 2; break; case RME_MATRIXCTRL_PLAYBACK_FADER: val = m_parent.getMixerGain(RME_FF_MM_PLAYBACK, col, row) / 2; break; case RME_MATRIXCTRL_OUTPUT_FADER: val = m_parent.getMixerGain(RME_FF_MM_OUTPUT, col, row) / 2; break; case RME_MATRIXCTRL_INPUT_MUTE: return m_parent.getMixerFlags(RME_FF_MM_INPUT, col, row, FF_SWPARAM_MF_MUTED) != 0; break; case RME_MATRIXCTRL_PLAYBACK_MUTE: return m_parent.getMixerFlags(RME_FF_MM_PLAYBACK, col, row, FF_SWPARAM_MF_MUTED) != 0; break; case RME_MATRIXCTRL_OUTPUT_MUTE: return m_parent.getMixerFlags(RME_FF_MM_OUTPUT, col, row, FF_SWPARAM_MF_MUTED) != 0; break; case RME_MATRIXCTRL_INPUT_INVERT: return m_parent.getMixerFlags(RME_FF_MM_INPUT, col, row, FF_SWPARAM_MF_INVERTED) != 0; break; case RME_MATRIXCTRL_PLAYBACK_INVERT: return m_parent.getMixerFlags(RME_FF_MM_PLAYBACK, col, row, FF_SWPARAM_MF_INVERTED) != 0; break; } return val; } } libffado-2.4.5/src/rme/fireface_settings_ctrls.h0000644000175000001440000001251614206145246021307 0ustar jwoitheusers/* * Copyright (C) 2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "debugmodule/debugmodule.h" #include "libcontrol/BasicElements.h" #include "libcontrol/MatrixMixer.h" namespace Rme { /* Control types for an RmeSettingsCtrl object */ #define RME_CTRL_NONE 0x0000 #define RME_CTRL_PHANTOM_SW 0x0001 #define RME_CTRL_SPDIF_INPUT_MODE 0x0002 #define RME_CTRL_SPDIF_OUTPUT_OPTICAL 0x0003 #define RME_CTRL_SPDIF_OUTPUT_PRO 0x0004 #define RME_CTRL_SPDIF_OUTPUT_EMPHASIS 0x0005 #define RME_CTRL_SPDIF_OUTPUT_NONAUDIO 0x0006 #define RME_CTRL_CLOCK_MODE 0x0007 #define RME_CTRL_SYNC_REF 0x0008 #define RME_CTRL_DEV_OPTIONS 0x0009 #define RME_CTRL_LIMIT_BANDWIDTH 0x000a #define RME_CTRL_INPUT_LEVEL 0x000b #define RME_CTRL_OUTPUT_LEVEL 0x000c #define RME_CTRL_INSTRUMENT_OPTIONS 0x000d #define RME_CTRL_WCLK_SINGLE_SPEED 0x000e #define RME_CTRL_PHONES_LEVEL 0x000f #define RME_CTRL_INPUT_SOURCE 0x0010 #define RME_CTRL_FF400_PAD_SW 0x0013 #define RME_CTRL_FF400_INSTR_SW 0x0014 #define RME_CTRL_FLASH 0x0050 #define RME_CTRL_MIXER_PRESET 0x0051 #define RME_CTRL_INFO_MODEL 0x0100 #define RME_CTRL_INFO_TCO_PRESENT 0x0200 #define RME_CTRL_INFO_SYSCLOCK_MODE 0x0300 #define RME_CTRL_INFO_SYSCLOCK_FREQ 0x0301 #define RME_CTRL_INFO_AUTOSYNC_FREQ 0x0310 #define RME_CTRL_INFO_AUTOSYNC_SRC 0x0311 #define RME_CTRL_INFO_SYNC_STATUS 0x0312 #define RME_CTRL_INFO_SPDIF_FREQ 0x0313 #define RME_CTRL_TCO_FIRST 0x0400 #define RME_CTRL_TCO_LTC_IN 0x0400 #define RME_CTRL_TCO_INPUT_LTC_VALID 0x0401 #define RME_CTRL_TCO_INPUT_LTC_FPS 0x0402 #define RME_CTRL_TCO_INPUT_LTC_DROPFRAME 0x0403 #define RME_CTRL_TCO_INPUT_VIDEO_TYPE 0x0404 #define RME_CTRL_TCO_INPUT_WORD_CLK 0x0405 #define RME_CTRL_TCO_INPUT_LOCK 0x0406 #define RME_CTRL_TCO_SYNC_SRC 0x0407 #define RME_CTRL_TCO_VIDEO_IN_TERM 0x0408 #define RME_CTRL_TCO_FREQ 0x0409 #define RME_CTRL_TCO_FRAME_RATE 0x040a #define RME_CTRL_TCO_SAMPLE_RATE 0x040b #define RME_CTRL_TCO_SAMPLE_RATE_OFS 0x040c #define RME_CTRL_TCO_WORD_CLK_CONV 0x040d #define RME_CTRL_TCO_LAST 0x040d /* Control types for an RmeSettingsMatrixCtrl object */ #define RME_MATRIXCTRL_NONE 0x0000 #define RME_MATRIXCTRL_GAINS 0x0001 #define RME_MATRIXCTRL_INPUT_FADER 0x0002 #define RME_MATRIXCTRL_PLAYBACK_FADER 0x0003 #define RME_MATRIXCTRL_OUTPUT_FADER 0x0004 #define RME_MATRIXCTRL_INPUT_MUTE 0x0005 #define RME_MATRIXCTRL_PLAYBACK_MUTE 0x0006 #define RME_MATRIXCTRL_OUTPUT_MUTE 0x0007 #define RME_MATRIXCTRL_INPUT_INVERT 0x0008 #define RME_MATRIXCTRL_PLAYBACK_INVERT 0x0009 /* Commands sent via RME_CTRL_FLASH values */ #define RME_CTRL_FLASH_SETTINGS_LOAD 0x0000 #define RME_CTRL_FLASH_SETTINGS_SAVE 0x0001 #define RME_CTRL_FLASH_MIXER_LOAD 0x0002 #define RME_CTRL_FLASH_MIXER_SAVE 0x0003 class Device; class RmeSettingsCtrl : public Control::Discrete { public: RmeSettingsCtrl(Device &parent, unsigned int type, unsigned int info); RmeSettingsCtrl(Device &parent, unsigned int type, unsigned int info, std::string name, std::string label, std::string descr); virtual bool setValue(int v); virtual int getValue(); virtual bool setValue(int idx, int v) { return setValue(v); }; virtual int getValue(int idx) { return getValue(); }; virtual int getMinimum() {return 0; }; virtual int getMaximum() {return 0; }; protected: Device &m_parent; unsigned int m_type; unsigned int m_value, m_info; }; class RmeSettingsMatrixCtrl : public Control::MatrixMixer { public: RmeSettingsMatrixCtrl(Device &parent, unsigned int type); RmeSettingsMatrixCtrl(Device &parent, unsigned int type, std::string name); virtual void show(); bool hasNames() const { return true; } bool canConnect() const { return false; } virtual std::string getRowName(const int row); virtual std::string getColName(const int col); virtual int canWrite( const int, const int ) { return true; } virtual int getRowCount(); virtual int getColCount(); virtual double setValue(const int row, const int col, const double val); virtual double getValue(const int row, const int col); // functions to access the entire coefficient map at once virtual bool getCoefficientMap(int &) {return false;}; virtual bool storeCoefficientMap(int &) {return false;}; protected: Device &m_parent; unsigned int m_type; }; } libffado-2.4.5/src/rme/rme_avdevice.cpp0000644000175000001440000013137614206145246017406 0ustar jwoitheusers/* * Copyright (C) 2005-2012 by Jonathan Woithe * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "rme/rme_avdevice.h" #include "rme/fireface_def.h" #include "rme/fireface_settings_ctrls.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libieee1394/IsoHandlerManager.h" #include "debugmodule/debugmodule.h" #include "libstreaming/rme/RmePort.h" #include "devicemanager.h" #include #include #include #include #include #include #include // Known values for the unit version of RME devices #define RME_UNITVERSION_FF800 0x0001 #define RME_UNITVERSION_FF400 0x0002 #define RME_UNITVERSION_UFX 0x0003 #define RME_UNITVERSION_UCX 0x0004 namespace Rme { Device::Device( DeviceManager& d, ffado_smartptr( configRom )) : FFADODevice( d, configRom ) , m_rme_model( RME_MODEL_NONE ) , settings( NULL ) , tco_settings( NULL ) , dev_config ( NULL ) , num_channels( 0 ) , frames_per_packet( 0 ) , speed800( 0 ) , provide_midi( 0 ) // MIDI not currently implemented in FFADO , iso_tx_channel( -1 ) , iso_rx_channel( -1 ) , m_receiveProcessor( NULL ) , m_transmitProcessor( NULL ) , m_MixerContainer( NULL ) , m_ControlContainer( NULL ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Rme::Device (NodeID %d)\n", getConfigRom().getNodeId() ); } Device::~Device() { delete m_receiveProcessor; delete m_transmitProcessor; if (iso_tx_channel>=0 && !get1394Service().freeIsoChannel(iso_tx_channel)) { debugOutput(DEBUG_LEVEL_VERBOSE, "Could not free tx iso channel %d\n", iso_tx_channel); } if (iso_rx_channel>=0 && m_rme_model==RME_MODEL_FIREFACE400 && !get1394Service().freeIsoChannel(iso_rx_channel)) { // FF800 handles the rx channel itself debugOutput(DEBUG_LEVEL_VERBOSE, "Could not free rx iso channel %d\n", iso_rx_channel); } destroyMixer(); if (dev_config != NULL) { switch (rme_shm_close(dev_config)) { case RSO_CLOSE: debugOutput( DEBUG_LEVEL_VERBOSE, "Configuration shared data object closed\n"); break; case RSO_CLOSE_DELETE: debugOutput( DEBUG_LEVEL_VERBOSE, "Configuration shared data object closed and deleted (no other users)\n"); break; } } } bool Device::buildMixer() { signed int i; bool result = true; destroyMixer(); debugOutput(DEBUG_LEVEL_VERBOSE, "Building an RME mixer...\n"); // Non-mixer device controls m_ControlContainer = new Control::Container(this, "Control"); if (!m_ControlContainer) { debugError("Could not create control container\n"); destroyMixer(); return false; } result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INFO_MODEL, 0, "Model", "Model ID", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INFO_TCO_PRESENT, 0, "TCO_present", "TCO is present", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INFO_SYSCLOCK_MODE, 0, "sysclock_mode", "System clock mode", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INFO_SYSCLOCK_FREQ, 0, "sysclock_freq", "System clock frequency", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INFO_AUTOSYNC_FREQ, 0, "autosync_freq", "Autosync frequency", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INFO_AUTOSYNC_SRC, 0, "autosync_src", "Autosync source", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INFO_SYNC_STATUS, 0, "sync_status", "Sync status", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INFO_SPDIF_FREQ, 0, "spdif_freq", "SPDIF frequency", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_PHANTOM_SW, 0, "Phantom", "Phantom switches", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INPUT_LEVEL, 0, "Input_level", "Input level", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_OUTPUT_LEVEL, 0, "Output_level", "Output level", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_SPDIF_INPUT_MODE, 0, "SPDIF_input_mode", "SPDIF input mode", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_SPDIF_OUTPUT_OPTICAL, 0, "SPDIF_output_optical", "SPDIF output optical", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_SPDIF_OUTPUT_EMPHASIS, 0, "SPDIF_output_emphasis", "SPDIF output emphasis", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_SPDIF_OUTPUT_PRO, 0, "SPDIF_output_pro", "SPDIF output pro", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_SPDIF_OUTPUT_NONAUDIO, 0, "SPDIF_output_nonaudio", "SPDIF output non-audio", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_PHONES_LEVEL, 0, "Phones_level", "Phones level", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_CLOCK_MODE, 0, "Clock_mode", "Clock mode", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_SYNC_REF, 0, "Sync_ref", "Preferred sync ref", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_LIMIT_BANDWIDTH, 0, "Bandwidth_limit", "Bandwidth limit", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_FLASH, 0, "Flash_control", "Flash control", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_MIXER_PRESET, 0, "Mixer_preset", "Mixer preset", "")); if (m_rme_model == RME_MODEL_FIREFACE800) { result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INPUT_SOURCE, 1, "Chan1_source", "Channel 1 source", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INPUT_SOURCE, 7, "Chan7_source", "Channel 7 source", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INPUT_SOURCE, 8, "Chan8_source", "Channel 8 source", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_INSTRUMENT_OPTIONS, 1, "Chan1_instr_opts", "Input instrument options channel 1", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_LTC_IN, 0, "Tco_ltc_in", "TCO input LTC received", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_INPUT_LTC_VALID, 0, "Tco_input_ltc_valid", "TCO input LTC valid", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_INPUT_LTC_FPS, 0, "Tco_input_ltc_fps", "TCO input LTC frame rate ID", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_INPUT_LTC_DROPFRAME, 0, "Tco_input_ltc_dropframe", "TCO input LTC dropframe detected", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_INPUT_VIDEO_TYPE, 0, "Tco_input_video_type", "TCO input video type", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_INPUT_WORD_CLK, 0, "Tco_input_word_clk", "TCO input word clock type", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_INPUT_LOCK, 0, "Tco_input_lock", "TCO input is locked", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_SYNC_SRC, 0, "Tco_sync_src", "TCO sync source", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_VIDEO_IN_TERM, 0, "Tco_video_in_term", "TCO video input terminator is enabled", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_WORD_CLK_CONV, 0, "Tco_word_clk_conv", "TCO word clock conversion", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_FREQ, 0, "Tco_freq", "TCO measured frequency (Hz)", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_FRAME_RATE, 0, "Tco_frame_rate", "TCO frame rate ID", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_SAMPLE_RATE, 0, "Tco_sample_rate", "TCO sample rate ID", "")); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_TCO_SAMPLE_RATE_OFS, 0, "Tco_sample_rate_ofs", "TCO sample rate pulldown offset ID", "")); } if (m_rme_model == RME_MODEL_FIREFACE400) { // Instrument input options for (i=3; i<=4; i++) { char path[32], desc[64]; snprintf(path, sizeof(path), "Chan%d_opt_instr", i); snprintf(desc, sizeof(desc), "Chan%d instrument option", i); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_FF400_INSTR_SW, i, path, desc, "")); snprintf(path, sizeof(path), "Chan%d_opt_pad", i); snprintf(desc, sizeof(desc), "Chan%d pad option", i); result &= m_ControlContainer->addElement( new RmeSettingsCtrl(*this, RME_CTRL_FF400_PAD_SW, i, path, desc, "")); } // Input/output gains result &= m_ControlContainer->addElement( new RmeSettingsMatrixCtrl(*this, RME_MATRIXCTRL_GAINS, "Gains")); } /* Mixer controls */ m_MixerContainer = new Control::Container(this, "Mixer"); if (!m_MixerContainer) { debugError("Could not create mixer container\n"); destroyMixer(); return false; } result &= m_MixerContainer->addElement( new RmeSettingsMatrixCtrl(*this, RME_MATRIXCTRL_INPUT_FADER, "InputFaders")); result &= m_MixerContainer->addElement( new RmeSettingsMatrixCtrl(*this, RME_MATRIXCTRL_PLAYBACK_FADER, "PlaybackFaders")); result &= m_MixerContainer->addElement( new RmeSettingsMatrixCtrl(*this, RME_MATRIXCTRL_OUTPUT_FADER, "OutputFaders")); result &= m_MixerContainer->addElement( new RmeSettingsMatrixCtrl(*this, RME_MATRIXCTRL_INPUT_MUTE, "InputMutes")); result &= m_MixerContainer->addElement( new RmeSettingsMatrixCtrl(*this, RME_MATRIXCTRL_PLAYBACK_MUTE, "PlaybackMutes")); result &= m_MixerContainer->addElement( new RmeSettingsMatrixCtrl(*this, RME_MATRIXCTRL_OUTPUT_MUTE, "OutputMutes")); result &= m_MixerContainer->addElement( new RmeSettingsMatrixCtrl(*this, RME_MATRIXCTRL_INPUT_INVERT, "InputInverts")); result &= m_MixerContainer->addElement( new RmeSettingsMatrixCtrl(*this, RME_MATRIXCTRL_PLAYBACK_INVERT, "PlaybackInverts")); if (!result) { debugWarning("One or more device control/mixer elements could not be created\n"); destroyMixer(); return false; } if (!addElement(m_ControlContainer) || !addElement(m_MixerContainer)) { debugWarning("Could not register controls/mixer to device\n"); // clean up destroyMixer(); return false; } return true; } bool Device::destroyMixer() { bool ret = true; debugOutput(DEBUG_LEVEL_VERBOSE, "destroy mixer...\n"); if (m_MixerContainer == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "no mixer to destroy...\n"); } else if (!deleteElement(m_MixerContainer)) { debugError("Mixer present but not registered to the avdevice\n"); ret = false; } else { // remove and delete (as in free) child control elements m_MixerContainer->clearElements(true); delete m_MixerContainer; m_MixerContainer = NULL; } // remove control container if (m_ControlContainer == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "no controls to destroy...\n"); } else if (!deleteElement(m_ControlContainer)) { debugError("Controls present but not registered to the avdevice\n"); ret = false; } else { // remove and delete (as in free) child control elements m_ControlContainer->clearElements(true); delete m_ControlContainer; m_ControlContainer = NULL; } return ret; } bool Device::probe( Util::Configuration& c, ConfigRom& configRom, bool generic ) { if (generic) { return false; } else { // check if device is in supported devices list. Note that the RME // devices use the unit version to identify the individual devices. // To avoid having to extend the configuration file syntax to // include this at this point, we'll use the configuration file // model ID to test against the device unit version. This can be // tidied up if the configuration file is extended at some point to // include the unit version. unsigned int vendorId = configRom.getNodeVendorId(); unsigned int unitVersion = configRom.getUnitVersion(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, unitVersion ); return c.isValid(vme) && vme.driver == Util::Configuration::eD_RME; } } FFADODevice * Device::createDevice(DeviceManager& d, ffado_smartptr( configRom )) { return new Device(d, configRom ); } bool Device::discover() { signed int i; std::string id; unsigned int vendorId = getConfigRom().getNodeVendorId(); // See note in Device::probe() about why we use the unit version here. unsigned int unitVersion = getConfigRom().getUnitVersion(); Util::Configuration &c = getDeviceManager().getConfiguration(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, unitVersion ); if (c.isValid(vme) && vme.driver == Util::Configuration::eD_RME) { debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", vme.vendor_name.c_str(), vme.model_name.c_str()); } else { debugWarning("Device '%s %s' unsupported by RME driver (no generic RME support)\n", getConfigRom().getVendorName().c_str(), getConfigRom().getModelName().c_str()); } switch (unitVersion) { case RME_UNITVERSION_FF800: m_rme_model = RME_MODEL_FIREFACE800; break; case RME_UNITVERSION_FF400: m_rme_model = RME_MODEL_FIREFACE400; break; case RME_UNITVERSION_UFX: m_rme_model = RME_MODEL_FIREFACE_UFX; break; case RME_UNITVERSION_UCX: m_rme_model = RME_MODEL_FIREFACE_UCX; break; default: debugError("Unsupported model\n"); return false; } if (m_rme_model==RME_MODEL_FIREFACE_UFX || m_rme_model==RME_MODEL_FIREFACE_UCX) { debugError("Fireface UFX/UCX are not currently supported\n"); return false; } id = std::string("dev0"); if (!getOption("id", id)) { debugWarning("Could not retrieve id parameter, defaulting to 'dev0'\n"); } // Set up the shared data object for configuration data i = rme_shm_open(id, &dev_config); if (i == RSO_OPEN_CREATED) { debugOutput( DEBUG_LEVEL_VERBOSE, "New configuration shared data object created, ID %s\n", id.c_str()); } else if (i == RSO_OPEN_ATTACHED) { debugOutput( DEBUG_LEVEL_VERBOSE, "Attached to existing configuration shared data object for ID %s\n", id.c_str()); } if (dev_config == NULL) { debugOutput( DEBUG_LEVEL_WARNING, "Could not create/access shared configuration memory object, using process-local storage\n"); memset(&local_dev_config_obj, 0, sizeof(local_dev_config_obj)); dev_config = &local_dev_config_obj; } settings = &dev_config->settings; tco_settings = &dev_config->tco_settings; // If device is FF800, check to see if the TCO is fitted if (m_rme_model == RME_MODEL_FIREFACE800) { dev_config->tco_present = (read_tco(NULL, 0) == 0); } debugOutput(DEBUG_LEVEL_VERBOSE, "TCO present: %s\n", dev_config->tco_present?"yes":"no"); init_hardware(); if (!buildMixer()) { debugWarning("Could not build mixer\n"); } return true; } int Device::getSamplingFrequency( ) { // Retrieve the current sample rate. For practical purposes this // is the software rate currently in use if in master clock mode, or // the external clock if in slave mode. // // If dds_freq functionality is pursued, some thinking will be required // here because the streaming engine will take its timings from the // value returned by this function. If the DDS is not running at // software_freq, returning software_freq won't work for the streaming // engine. User software, on the other hand, would require the // software_freq value. Ultimately the streaming engine will probably // have to be changed to obtain the "real" sample rate through other // means. // The kernel (as of 3.10 at least) seems to crash with an out-of-memory // condition if this function calls get_hardware_state() too frequently // (for example, several times per iso cycle). The code of the RME // driver should be structured in such a way as to prevent such calls // from the fast path, but it's always possible that other components // will call into this function when streaming is active (ffado-mixer // for instance. In such cases return the software frequency as a proxy // for the true rate. if (hardware_is_streaming()) { return dev_config->software_freq; } FF_state_t state; if (get_hardware_state(&state) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to read device state\n"); return 0; } if (state.clock_mode == FF_STATE_CLOCKMODE_AUTOSYNC) { // Note: this could return 0 if there is no valid external clock return state.autosync_freq; } return dev_config->software_freq; } int Device::getConfigurationId() { return 0; } bool Device::setDDSFrequency( int dds_freq ) { // Set a fixed DDS frequency. If the device is the clock master this // will immediately be copied to the hardware DDS register. Otherwise // it will take effect as required at the time the sampling rate is // changed or streaming is started. // If the device is streaming, the new DDS rate must have the same // multiplier as the software sample rate. // // Since FFADO doesn't make use of the dds_freq functionality at present // (there being no user access provided for this) the effect of changing // the hardware DDS while streaming is active has not been tested. A // new DDS value will change the timestamp intervals applicable to the // streaming engine, so an alteration here without at least a restart of // the streaming will almost certainly cause trouble. Initially it may // be easiest to disallow such changes when streaming is active. if (hardware_is_streaming()) { if (multiplier_of_freq(dds_freq) != multiplier_of_freq(dev_config->software_freq)) return false; } dev_config->dds_freq = dds_freq; if (settings->clock_mode == FF_STATE_CLOCKMODE_MASTER) { if (set_hardware_dds_freq(dds_freq) != 0) return false; } return true; } bool Device::setSamplingFrequency( int samplingFrequency ) { // Request a sampling rate on behalf of software. Software is limited // to sample rates of 32k, 44.1k, 48k and the 2x/4x multiples of these. // The user may lock the device to a much wider range of frequencies via // the explicit DDS controls in the control panel. If the explicit DDS // control is active the software is limited to the "standard" speeds // corresponding to the multiplier in use by the DDS. // // Similarly, if the device is externally clocked the software is // limited to the external clock frequency. // // Otherwise the software has free choice of the software speeds noted // above. bool ret = false; signed int i, j; signed int mult[3] = {1, 2, 4}; unsigned int base_freq[3] = {32000, 44100, 48000}; unsigned int freq = samplingFrequency; FF_state_t state; signed int fixed_freq = 0; if (get_hardware_state(&state) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to read device state\n"); return false; } // If device is locked to a frequency via external clock, explicit // setting of the DDS or by virtue of streaming being active, get that // frequency. if (state.clock_mode == FF_STATE_CLOCKMODE_AUTOSYNC) { // The autosync frequency can be retrieved from state.autosync_freq. // An autosync_freq value of 0 indicates the absence of a valid // external clock. Allow sampling frequencies which match the // sync rate and reject all others. // // A further note: if synced to TCO, is autosync_freq valid? if (state.autosync_freq == 0) { debugOutput(DEBUG_LEVEL_ERROR, "slave clock mode active but no valid external clock present\n"); } if (state.autosync_freq==0 || (int)state.autosync_freq!=samplingFrequency) return false; dev_config->software_freq = samplingFrequency; return true; } else if (dev_config->dds_freq > 0) { fixed_freq = dev_config->dds_freq; } else if (hardware_is_streaming()) { // See comments in getSamplingFrequency() as to why this may not // be successful in the long run. fixed_freq = dev_config->software_freq; } // If the device is running to a fixed frequency, software can only // request frequencies with the same multiplier. Similarly, the // multiplier is locked in "master" clock mode if the device is // streaming. if (fixed_freq > 0) { unsigned int fixed_mult = multiplier_of_freq(fixed_freq); if (multiplier_of_freq(freq) != fixed_mult) { debugOutput(DEBUG_LEVEL_ERROR, "DDS currently set to %d Hz, new sampling rate %d does not have the same multiplier\n", fixed_freq, freq); return false; } for (j=0; j<3; j++) { if (freq == base_freq[j]*fixed_mult) { ret = true; break; } } } else { for (i=0; i<3; i++) { for (j=0; j<3; j++) { if (freq == base_freq[j]*mult[i]) { ret = true; break; } } } } // If requested frequency is unavailable, return false if (ret == false) { debugOutput(DEBUG_LEVEL_ERROR, "requested sampling rate %d Hz not available\n", freq); return false; } // If a DDS frequency has been explicitly requested this is always used // to program the hardware DDS regardless of the rate requested by the // software (such use of the DDS is only possible if the Fireface is // operating in master clock mode). Otherwise we use the requested // sampling rate. if (dev_config->dds_freq>0 && state.clock_mode==FF_STATE_CLOCKMODE_MASTER) freq = dev_config->dds_freq; if (set_hardware_dds_freq(freq) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to set hardware sample rate to %d Hz\n", freq); return false; } debugOutput(DEBUG_LEVEL_VERBOSE, "hardware set to sampling frequency %d Hz\n", samplingFrequency); dev_config->software_freq = samplingFrequency; settings->sample_rate = samplingFrequency; return true; } std::vector Device::getSupportedSamplingFrequencies() { std::vector frequencies; signed int i, j; signed int mult[3] = {1, 2, 4}; signed int freq[3] = {32000, 44100, 48000}; FF_state_t state; if (get_hardware_state(&state) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to read device state\n"); return frequencies; } // Generate the list of supported frequencies. If the device is // externally clocked the frequency is limited to the external clock // frequency. if (state.clock_mode == FF_STATE_CLOCKMODE_AUTOSYNC) { // FIXME: if synced to TCO, is autosync_freq valid? // The autosync frequency will be zero if no valid clock is available frequencies.push_back(state.autosync_freq); } else // If the device is running the multiplier is fixed. if (state.is_streaming) { // It's not certain that permitting rate changes while streaming // is active will work. See comments in setSamplingFrequency() and // elsewhere. unsigned int fixed_mult = multiplier_of_freq(dev_config->software_freq); for (j=0; j<3; j++) { frequencies.push_back(freq[j]*fixed_mult); } } else { for (i=0; i<3; i++) { for (j=0; j<3; j++) { frequencies.push_back(freq[j]*mult[i]); } } } return frequencies; } // The RME clock source selection logic is a little more complex than a // simple list can cater for. Therefore we just put in a placeholder and // rely on the extended controls in ffado-mixer to deal with the details. // FFADODevice::ClockSource Device::dummyClockSource(void) { ClockSource s; s.id = 0; s.type = eCT_Internal; s.description = "Selected via device controls"; s.valid = s.active = s.locked = true; s.slipping = false; return s; } FFADODevice::ClockSourceVector Device::getSupportedClockSources() { FFADODevice::ClockSourceVector r; ClockSource s; s = dummyClockSource(); r.push_back(s); return r; } bool Device::setActiveClockSource(ClockSource s) { return true; } FFADODevice::ClockSource Device::getActiveClockSource() { return dummyClockSource(); } bool Device::lock() { return true; } bool Device::unlock() { return true; } void Device::showDevice() { unsigned int vendorId = getConfigRom().getNodeVendorId(); unsigned int modelId = getConfigRom().getModelId(); Util::Configuration &c = getDeviceManager().getConfiguration(); Util::Configuration::VendorModelEntry vme = c.findDeviceVME( vendorId, modelId ); debugOutput(DEBUG_LEVEL_VERBOSE, "%s %s at node %d\n", vme.vendor_name.c_str(), vme.model_name.c_str(), getNodeId()); } bool Device::resetForStreaming() { signed int err; signed int iso_rx; unsigned int stat[4]; signed int i; // Ensure the transmit processor is ready to start streaming. When // this function is called from prepare() the transmit processor // won't be allocated. if (m_transmitProcessor != NULL) m_transmitProcessor->resetForStreaming(); // Whenever streaming is restarted hardware_init_streaming() needs to be // called. Otherwise the device won't start sending data when data is // sent to it and the rx stream will fail to start. err = hardware_init_streaming(dev_config->hardware_freq, iso_tx_channel) != 0; if (err) { debugFatal("Could not initialise device streaming system\n"); return false; } i = 0; while (i < 100) { err = (get_hardware_streaming_status(stat, 4) != 0); if (err) { debugFatal("error reading status register\n"); break; } debugOutput(DEBUG_LEVEL_VERBOSE, "rme init stat: %08x %08x %08x %08x\n", stat[0], stat[1], stat[2], stat[3]); if (m_rme_model == RME_MODEL_FIREFACE400) { break; } // The Fireface-800 chooses its tx channel (our rx channel). Wait // for the device busy flag to clear, then confirm that the rx iso // channel hasn't changed (it shouldn't across a restart). if (stat[2] == 0xffffffff) { // Device not ready; wait 5 ms and try again usleep(5000); i++; } else { iso_rx = stat[2] & 63; if (iso_rx!=iso_rx_channel && iso_rx_channel!=-1) debugOutput(DEBUG_LEVEL_WARNING, "rx iso: now %d, was %d\n", iso_rx, iso_rx_channel); iso_rx_channel = iso_rx; // Even if the rx channel has changed, the device takes care of // registering the channel itself, so we don't have to (neither // do we have to release the old one). If we try to call // raw1394_channel_modify() on the returned channel we'll get an // error. // iso_rx_channel = get1394Service().allocateFixedIsoChannelGeneric(iso_rx_channel, bandwidth); break; } } if (i==100 || err) { if (i == 100) debugFatal("timeout waiting for device not busy\n"); return false; } else { signed int init_samplerate; if ((stat[1] & SR1_CLOCK_MODE_MASTER) || (stat[0] & SR0_AUTOSYNC_FREQ_MASK)==0 || (stat[0] & SR0_AUTOSYNC_SRC_MASK)==SR0_AUTOSYNC_SRC_NONE) { init_samplerate = dev_config->hardware_freq; } else { init_samplerate = (stat[0] & SR0_STREAMING_FREQ_MASK) * 250; } debugOutput(DEBUG_LEVEL_VERBOSE, "sample rate on start: %d\n", init_samplerate); } return FFADODevice::resetForStreaming(); } bool Device::prepare() { signed int mult, bandwidth; signed int freq; signed int err = 0; debugOutput(DEBUG_LEVEL_NORMAL, "Preparing Device...\n" ); // If there is no iso data to send in a given cycle the RMEs simply // don't send anything. This is in contrast to most other interfaces // which at least send an empty packet. As a result the IsoHandler // contains code which detects missing packets as dropped packets. // For RME devices we must turn this test off since missing packets // are in fact to be expected. get1394Service().getIsoHandlerManager().setMissedCyclesOK(true); freq = getSamplingFrequency(); if (freq <= 0) { debugOutput(DEBUG_LEVEL_ERROR, "Can't continue: sampling frequency not set\n"); return false; } mult = freq<68100?1:(freq<136200?2:4); frames_per_packet = getFramesPerPacket(); // The number of active channels depends on sample rate and whether // bandwidth limitation is active. First set up the number of analog // channels (which differs between devices), then add SPDIF channels if // relevant. Finally, the number of channels available from each ADAT // interface depends on sample rate: 0 at 4x, 4 at 2x and 8 at 1x. // Note that "analog only" bandwidth limit mode means analog 1-8 // regardless of the fireface model in use. if (m_rme_model==RME_MODEL_FIREFACE800 && settings->limit_bandwidth!=FF_SWPARAM_BWLIMIT_ANALOG_ONLY) num_channels = 10; else num_channels = 8; if (settings->limit_bandwidth != FF_SWPARAM_BWLIMIT_ANALOG_ONLY) num_channels += 2; if (settings->limit_bandwidth==FF_SWPARAM_BWLIMIT_SEND_ALL_CHANNELS || settings->limit_bandwidth==FF_DEV_FLASH_BWLIMIT_NO_ADAT2) num_channels += (mult==4?0:(mult==2?4:8)); if (m_rme_model==RME_MODEL_FIREFACE800 && settings->limit_bandwidth==FF_SWPARAM_BWLIMIT_SEND_ALL_CHANNELS) num_channels += (mult==4?0:(mult==2?4:8)); // Bandwidth is calculated here. For the moment we assume the device // is connected at S400, so 1 allocation unit is 1 transmitted byte. // There is 25 allocation units of protocol overhead per packet. Each // channel of audio data is sent/received as a 32 bit integer. bandwidth = 25 + num_channels*4*frames_per_packet; // Both the FF400 and FF800 require we allocate a tx iso channel and // then initialise the device. Device status is then read at least once // regardless of which interface is in use. The rx channel is then // allocated for the FF400 or acquired from the device in the case of // the FF800. Even though the FF800 chooses the rx channel it does not // handle the bus-level channel/bandwidth allocation so we must do that // here. if (iso_tx_channel < 0) { iso_tx_channel = get1394Service().allocateIsoChannelGeneric(bandwidth); } if (iso_tx_channel < 0) { debugFatal("Could not allocate iso tx channel\n"); return false; } else { debugOutput(DEBUG_LEVEL_NORMAL, "iso tx channel: %d\n", iso_tx_channel); } // Call this to initialise the device's streaming system and, in the // case of the FF800, obtain the rx iso channel to use. Having that // functionality in resetForStreaming() means it's effectively done // twice when FFADO is first started, but this does no harm. if (resetForStreaming() == false) return false; if (err) { if (iso_tx_channel >= 0) get1394Service().freeIsoChannel(iso_tx_channel); if (iso_rx_channel>=0 && m_rme_model==RME_MODEL_FIREFACE400) // The FF800 manages this channel itself. get1394Service().freeIsoChannel(iso_rx_channel); return false; } /* We need to manage the FF400's iso rx channel */ if (m_rme_model == RME_MODEL_FIREFACE400) { iso_rx_channel = get1394Service().allocateIsoChannelGeneric(bandwidth); } // get the device specific and/or global SP configuration Util::Configuration &config = getDeviceManager().getConfiguration(); // base value is the config.h value float recv_sp_dll_bw = STREAMPROCESSOR_DLL_BW_HZ; float xmit_sp_dll_bw = STREAMPROCESSOR_DLL_BW_HZ; // we can override that globally config.getValueForSetting("streaming.spm.recv_sp_dll_bw", recv_sp_dll_bw); config.getValueForSetting("streaming.spm.xmit_sp_dll_bw", xmit_sp_dll_bw); // or override in the device section config.getValueForDeviceSetting(getConfigRom().getNodeVendorId(), getConfigRom().getModelId(), "recv_sp_dll_bw", recv_sp_dll_bw); config.getValueForDeviceSetting(getConfigRom().getNodeVendorId(), getConfigRom().getModelId(), "xmit_sp_dll_bw", xmit_sp_dll_bw); // Calculate the event size. Each audio channel is allocated 4 bytes in // the data stream. /* FIXME: this will still require fine-tuning, but it's a start */ signed int event_size = num_channels * 4; // Set up receive stream processor, initialise it and set DLL bw m_receiveProcessor = new Streaming::RmeReceiveStreamProcessor(*this, m_rme_model, event_size); m_receiveProcessor->setVerboseLevel(getDebugLevel()); if (!m_receiveProcessor->init()) { debugFatal("Could not initialize receive processor!\n"); return false; } if (!m_receiveProcessor->setDllBandwidth(recv_sp_dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete m_receiveProcessor; m_receiveProcessor = NULL; return false; } // Add ports to the processor - TODO std::string id=std::string("dev?"); if (!getOption("id", id)) { debugWarning("Could not retrieve id parameter, defaulting to 'dev?'\n"); } addDirPorts(Streaming::Port::E_Capture); /* Now set up the transmit stream processor */ m_transmitProcessor = new Streaming::RmeTransmitStreamProcessor(*this, m_rme_model, event_size); m_transmitProcessor->setVerboseLevel(getDebugLevel()); if (!m_transmitProcessor->init()) { debugFatal("Could not initialise receive processor!\n"); return false; } if (!m_transmitProcessor->setDllBandwidth(xmit_sp_dll_bw)) { debugFatal("Could not set DLL bandwidth\n"); delete m_transmitProcessor; m_transmitProcessor = NULL; return false; } // Other things to be done: // * add ports to transmit stream processor addDirPorts(Streaming::Port::E_Playback); return true; } int Device::getStreamCount() { return 2; // one receive, one transmit } Streaming::StreamProcessor * Device::getStreamProcessorByIndex(int i) { switch (i) { case 0: return m_receiveProcessor; case 1: return m_transmitProcessor; default: debugWarning("Invalid stream index %d\n", i); } return NULL; } enum FFADODevice::eStreamingState Device::getStreamingState() { if (hardware_is_streaming()) return eSS_Both; return eSS_Idle; } bool Device::startStreamByIndex(int i) { // The RME does not allow separate enabling of the transmit and receive // streams. Therefore we start all streaming when index 0 is referenced // and silently ignore the start requests for other streams // (unconditionally flagging them as being successful). if (i == 0) { m_receiveProcessor->setChannel(iso_rx_channel); m_transmitProcessor->setChannel(iso_tx_channel); if (hardware_start_streaming(iso_rx_channel) != 0) return false; } return true; } bool Device::stopStreamByIndex(int i) { // See comments in startStreamByIndex() as to why we act only when stream // 0 is requested. if (i == 0) { if (hardware_stop_streaming() != 0) return false; } return true; } signed int Device::getFramesPerPacket(void) { // The number of frames transmitted in a single packet is solely // determined by the sample rate. This function is called several times // per iso cycle by the tx stream processor, so use the software rate as // a proxy for the hardware sample rate. Calling getSamplingFrequency() // is best avoided because otherwise the kernel tends to crash having // run out of memory (something about the timing of async commands in // getSamplingFrequency() and the iso tx handler seems to be tripping it // up). // // If streaming is active the software sampling rate should be set up. // If the dds_freq functionality is implemented the software rate can // probably still be used because the hardware dictates that both // must share the same multiplier, and the only reason for obtaining // the sampling frequency is to determine the multiplier. signed int freq = dev_config->software_freq; signed int mult = multiplier_of_freq(freq); switch (mult) { case 2: return 15; case 4: return 25; default: return 7; } return 7; } bool Device::addPort(Streaming::StreamProcessor *s_processor, char *name, enum Streaming::Port::E_Direction direction, int position, int size) { Streaming::Port *p; p = new Streaming::RmeAudioPort(*s_processor, name, direction, position, size); if (p == NULL) { debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n",name); } return true; } bool Device::addDirPorts(enum Streaming::Port::E_Direction direction) { const char *mode_str = direction==Streaming::Port::E_Capture?"cap":"pbk"; Streaming::StreamProcessor *s_processor; std::string id; char name[128]; signed int i; signed int n_analog, n_phones, n_adat, n_spdif; signed int sample_rate = getSamplingFrequency(); /* Apply bandwidth limit if selected. This effectively sets up the * number of adat and spdif channels assuming single-rate speed. * The total number of expected analog channels is also set here. */ n_spdif = 2; n_analog = (m_rme_model==RME_MODEL_FIREFACE800)?10:8; switch (dev_config->settings.limit_bandwidth) { case FF_SWPARAM_BWLIMIT_ANALOG_ONLY: n_adat = n_spdif = 0; // "Analog only" means "Analog 1-8" regardless of the interface model n_analog = 8; break; case FF_SWPARAM_BWLIMIT_ANALOG_SPDIF_ONLY: n_adat = 0; break; case FF_SWPARAM_BWLIMIT_NO_ADAT2: /* FF800 only */ n_adat = 8; break; default: /* Send all channels */ n_adat = (m_rme_model==RME_MODEL_FIREFACE800)?16:8; } /* Adjust the spdif and ADAT channels according to the current sample * rate. */ if (sample_rate>=MIN_DOUBLE_SPEED && sample_rate= MIN_QUAD_SPEED) { n_adat = 0; } n_phones = 0; if (direction == Streaming::Port::E_Capture) { s_processor = m_receiveProcessor; } else { s_processor = m_transmitProcessor; /* Phones generally count as two of the analog outputs. For * the FF800 in "Analog 1-8" bandwidth limit mode this is * not the case and the phones are inactive. */ if (m_rme_model==RME_MODEL_FIREFACE400 || dev_config->settings.limit_bandwidth!=FF_SWPARAM_BWLIMIT_ANALOG_ONLY) { n_analog -= 2; n_phones = 2; } } id = std::string("dev?"); if (!getOption("id", id)) { debugWarning("Could not retrieve id parameter, defaulting to 'dev?'\n"); } for (i=0; i. * */ #ifndef RMEDEVICE_H #define RMEDEVICE_H #include "ffadodevice.h" #include "debugmodule/debugmodule.h" #include "libavc/avc_definitions.h" #include "libutil/Configuration.h" #include "libutil/ByteSwap.h" #include "fireface_def.h" #include "libstreaming/rme/RmeReceiveStreamProcessor.h" #include "libstreaming/rme/RmeTransmitStreamProcessor.h" #include "rme_shm.h" class ConfigRom; class Ieee1394Service; namespace Rme { // The RME devices expect packet data in little endian format (as // opposed to bus order, which is big endian). Therefore define our own // 32-bit byteswap function to account for this. #if __BYTE_ORDER == __BIG_ENDIAN #define RME_BYTESWAP32(x) ByteSwap32(x) #else #define RME_BYTESWAP32(x) (x) #endif static inline uint32_t ByteSwapToDevice32(uint32_t d) { return RME_BYTESWAP32(d); } static inline uint32_t ByteSwapFromDevice32(uint32_t d) { return RME_BYTESWAP32(d); } // Note: the values in this enum do not have to correspond to the unit // version reported by the respective devices. It just so happens that they // currently do for the Fireface-800 and Fireface-400. enum ERmeModel { RME_MODEL_NONE = 0x0000, RME_MODEL_FIREFACE800 = 0x0001, RME_MODEL_FIREFACE400 = 0x0002, RME_MODEL_FIREFACE_UFX = 0x0003, RME_MODEL_FIREFACE_UCX = 0x0004, }; class Device : public FFADODevice { public: Device( DeviceManager& d, ffado_smartptr( configRom )); virtual ~Device(); virtual bool buildMixer(); virtual bool destroyMixer(); static bool probe( Util::Configuration& c, ConfigRom& configRom, bool generic = false ); static FFADODevice * createDevice( DeviceManager& d, ffado_smartptr( configRom )); static int getConfigurationId( ); virtual bool discover(); virtual void showDevice(); bool setDDSFrequency( int dds_freq ); virtual bool setSamplingFrequency( int samplingFrequency ); virtual int getSamplingFrequency( ); virtual std::vector getSupportedSamplingFrequencies(); ClockSource dummyClockSource(void); virtual ClockSourceVector getSupportedClockSources(); virtual bool setActiveClockSource(ClockSource); virtual ClockSource getActiveClockSource(); virtual int getStreamCount(); virtual Streaming::StreamProcessor *getStreamProcessorByIndex(int i); virtual bool resetForStreaming(); virtual bool prepare(); virtual bool lock(); virtual bool unlock(); virtual enum FFADODevice::eStreamingState getStreamingState(); virtual bool startStreamByIndex(int i); virtual bool stopStreamByIndex(int i); signed int getNumChannels(void) { return num_channels; }; signed int getFramesPerPacket(void); 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 readRegister(fb_nodeaddr_t reg); signed int readBlock(fb_nodeaddr_t reg, quadlet_t *buf, unsigned int n_quads); signed int writeRegister(fb_nodeaddr_t reg, quadlet_t data); signed int writeBlock(fb_nodeaddr_t reg, quadlet_t *data, unsigned int n_quads); /* Device control functions */ signed int getPhantom(unsigned int channel); signed int setPhantom(unsigned int channel, unsigned int status); signed int getInputLevel(void); signed int setInputLevel(unsigned int level); signed int getOutputLevel(void); signed int setOutputLevel(unsigned int level); signed int getPhonesLevel(void); signed int setPhonesLevel(unsigned int level); signed int getInputPadOpt(unsigned int channel); signed int setInputPadOpt(unsigned int channel, unsigned int status); signed int getInputInstrOpt(unsigned int channel); signed int setInputInstrOpt(unsigned int channel, unsigned int status); signed int getInputSource(unsigned int channel); signed int setInputSource(unsigned int channel, unsigned int src); signed int getSpdifInputMode(void); signed int setSpdifInputMode(signed int mode); signed int getSpdifOutputIsOptical(void); signed int setSpdifOutputIsOptical(signed int enable); signed int getSpdifOutputEmphasisOn(void); signed int setSpdifOutputEmphasisOn(signed int enable); signed int getSpdifOutputNonAudioOn(void); signed int setSpdifOutputNonAudioOn(signed int enable); signed int getSpdifOutputProOn(void); signed int setSpdifOutputProOn(signed int enable); signed int getAmpGain(unsigned int index); signed int setAmpGain(unsigned int index, signed int val); signed int getMixerGain(unsigned int ctype, unsigned int src_channel, unsigned int dest_channel); signed int setMixerGain(unsigned int ctype, unsigned int src_channel, unsigned int dest_channel, signed int val); signed int getMixerFlags(unsigned int ctype, unsigned int src_channel, unsigned int dest_channel, unsigned int flagmask); signed int setMixerFlags(unsigned int ctype, unsigned int src_channel, unsigned int dest_channel, unsigned int flagmask, signed int val); signed int getClockMode(void); signed int setClockMode(signed int mode); signed int getSyncRef(void); signed int setSyncRef(signed int ref); signed int getBandwidthLimit(void); signed int setBandwidthLimit(signed int limit); /* TCO control functions */ signed int getTcoLtc(void); signed int getTcoLtcValid(void); signed int getTcoLock(void); signed int getTcoLtcFrameRate(void); signed int getTcoLtcDropFrame(void); signed int getTcoVideoType(void); signed int getTcoWordClk(void); float getTcoFrequency(void); signed int getTcoWordClkState(void); signed int getTcoSyncSrc(void); signed int setTcoSyncSrc(signed int src); signed int getTcoFrameRate(void); signed int setTcoFrameRate(signed int rate_id); signed int getTcoPull(void); signed int setTcoPull(signed int pull); signed int getTcoSampleRate(void); signed int setTcoSampleRate(signed int rate_param_id); signed int getTcoTermination(void); signed int setTcoTermination(signed int enable); signed int getTcoWordClkConv(void); signed int setTcoWordClkConv(signed int conv); /* General information functions */ signed int getRmeModel(void) { return m_rme_model; } signed int getTcoPresent(void) { return dev_config->tco_present; } Streaming::RmeReceiveStreamProcessor *getRxSP(void) { return m_receiveProcessor; } protected: enum ERmeModel m_rme_model; FF_software_settings_t *settings; FF_TCO_settings_t *tco_settings; rme_shm_t *dev_config, local_dev_config_obj; signed int num_channels; signed int frames_per_packet; // 1 frame includes 1 sample from each channel signed int speed800; signed int provide_midi; // 0=no, 1=yes signed int iso_tx_channel, iso_rx_channel; Streaming::RmeReceiveStreamProcessor *m_receiveProcessor; Streaming::RmeTransmitStreamProcessor *m_transmitProcessor; private: unsigned long long int cmd_buffer_addr(); unsigned long long int stream_init_reg(); unsigned long long int stream_start_reg(); unsigned long long int stream_end_reg(); unsigned long long int flash_settings_addr(); unsigned long long int flash_mixer_vol_addr(); unsigned long long int flash_mixer_pan_addr(); unsigned long long int flash_mixer_hw_addr(); /* Low-level flash memory functions */ signed int wait_while_busy(unsigned int init_delay); signed int get_revision(unsigned int *revision); signed int read_flash(fb_nodeaddr_t addr, quadlet_t *buf, unsigned int n_quads); signed int erase_flash(unsigned int flags); signed int write_flash(fb_nodeaddr_t addr, quadlet_t *buf, unsigned int n_quads); /* Upper level flash memory functions */ public: signed int read_device_flash_settings(FF_software_settings_t *dsettings); signed int write_device_flash_settings(FF_software_settings_t *dsettings); signed int read_device_mixer_settings(FF_software_settings_t *dsettings); signed int write_device_mixer_settings(FF_software_settings_t *dsettings); private: /* Low-level hardware functions */ unsigned int multiplier_of_freq(unsigned int freq); void config_lock(void); void config_unlock(void); signed int init_hardware(void); signed int get_hardware_status(unsigned int *stat0, unsigned int *stat1); signed int get_hardware_streaming_status(unsigned int *stat, unsigned int n); public: signed int get_hardware_state(FF_state_t *state); private: signed int set_hardware_params(FF_software_settings_t *use_settings = NULL); signed int read_tco(quadlet_t *tco_data, signed int size); signed int write_tco(quadlet_t *tco_data, signed int size); signed int hardware_is_streaming(void); signed int read_tco_state(FF_TCO_state_t *tco_state); signed int write_tco_settings(FF_TCO_settings_t *tco_settings); signed int set_hardware_dds_freq(signed int freq); signed int hardware_init_streaming(unsigned int sample_rate, unsigned int tx_channel); signed int hardware_start_streaming(unsigned int listen_channel); signed int hardware_stop_streaming(void); signed int set_hardware_ampgain(unsigned int index, signed int val); signed int set_hardware_mixergain(unsigned int ctype, unsigned int src_channel, unsigned int dest_channel, signed int val); signed int set_hardware_channel_mute(signed int chan, signed int mute); signed int set_hardware_output_rec(signed int rec); signed int getMixerGainIndex(unsigned int src_channel, unsigned int dest_channel); Control::Container *m_MixerContainer; Control::Container *m_ControlContainer; }; } #endif libffado-2.4.5/src/rme/rme_avdevice_settings.cpp0000644000175000001440000004501514206145246021320 0ustar jwoitheusers/* * Copyright (C) 2005-2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "rme/rme_avdevice.h" #include "rme/fireface_def.h" #include "debugmodule/debugmodule.h" namespace Rme { signed int Device::getPhantom(unsigned int channel) { if (channel > 3) { debugOutput(DEBUG_LEVEL_WARNING, "Channel %d phantom power not supported\n", channel); return -1; } return settings->mic_phantom[channel] != 0; } signed int Device::setPhantom(unsigned int channel, unsigned int status) { if (channel > 3) { debugOutput(DEBUG_LEVEL_WARNING, "Channel %d phantom power not supported\n", channel); return -1; } settings->mic_phantom[channel] = (status != 0); set_hardware_params(); return 0; } signed int Device::getInputLevel(void) { return settings->input_level; } signed int Device::setInputLevel(unsigned int level) { if (levelFF_SWPARAM_ILEVEL_4dBU) { debugOutput(DEBUG_LEVEL_WARNING, "Invalid input level ID %d\n", level); return -1; } settings->input_level = level; set_hardware_params(); return 0; } signed int Device::getOutputLevel(void) { return settings->output_level; } signed int Device::setOutputLevel(unsigned int level) { if (levelFF_SWPARAM_OLEVEL_HIGAIN) { debugOutput(DEBUG_LEVEL_WARNING, "Invalid output level ID %d\n", level); return -1; } settings->output_level = level; set_hardware_params(); return 0; } signed int Device::getPhonesLevel(void) { return settings->phones_level; } signed int Device::setPhonesLevel(unsigned int level) { if (levelFF_SWPARAM_PHONESLEVEL_m10dBV) { debugOutput(DEBUG_LEVEL_WARNING, "Invalid phones level ID %d\n", level); return -1; } settings->phones_level = level; set_hardware_params(); return 0; } signed int Device::getInputPadOpt(unsigned int channel) { switch (m_rme_model) { case RME_MODEL_FIREFACE800: debugOutput(DEBUG_LEVEL_WARNING, "channel input pad option not supported by FF800 hardware\n"); return -1; case RME_MODEL_FIREFACE400: if (channel<3 || channel>4) { debugOutput(DEBUG_LEVEL_WARNING, "channel %d input pad option not supported on FF400\n", channel); return -1; } return settings->ff400_input_pad[channel-3] != 0; default: debugOutput(DEBUG_LEVEL_WARNING, "unimplemented for model %d\n", m_rme_model); } return 0; } signed int Device::setInputPadOpt(unsigned int channel, unsigned int status) { switch (m_rme_model) { case RME_MODEL_FIREFACE800: debugOutput(DEBUG_LEVEL_WARNING, "channel input pad option not supported by FF800 hardware\n"); return -1; case RME_MODEL_FIREFACE400: if (channel<3 || channel>4) { debugOutput(DEBUG_LEVEL_WARNING, "channel %d input pad option not supported on FF400\n", channel); return -1; } settings->ff400_input_pad[channel-3] = (status != 0); break; default: debugOutput(DEBUG_LEVEL_WARNING, "unimplemented for model %d\n", m_rme_model); return -1; } set_hardware_params(); return 0; } signed int Device::getInputInstrOpt(unsigned int channel) { switch (m_rme_model) { case RME_MODEL_FIREFACE400: if (channel<3 || channel>4) { debugOutput(DEBUG_LEVEL_WARNING, "Channel %d input instrument option not supported for model FF400\n", channel); return -1; } return settings->ff400_instr_input[channel-3] != 0; break; case RME_MODEL_FIREFACE800: if (channel != 1) { debugOutput(DEBUG_LEVEL_WARNING, "Channel %d input instrument options not supported for FF800\n", channel); return -1; } return (settings->filter?FF800_INSTR_OPT_FILTER:0) | (settings->fuzz?FF800_INSTR_OPT_FUZZ:0) | (settings->limiter?FF800_INSTR_OPT_LIMITER:0); break; default: debugOutput(DEBUG_LEVEL_WARNING, "unimplemented for model %d\n", m_rme_model); return -1; } return -1; } signed int Device::setInputInstrOpt(unsigned int channel, unsigned int status) { switch (m_rme_model) { case RME_MODEL_FIREFACE400: if (channel<3 || channel>4) { debugOutput(DEBUG_LEVEL_WARNING, "channel %d input instrument option not supported for FF400\n", channel); return -1; } settings->ff400_instr_input[channel-3] = (status != 0); break; case RME_MODEL_FIREFACE800: if (channel != 1) { debugOutput(DEBUG_LEVEL_WARNING, "channel %d input instrument options not supported for FF800\n", channel); return -1; } settings->filter = (status & FF800_INSTR_OPT_FILTER)!=0; settings->fuzz = (status & FF800_INSTR_OPT_FUZZ)!=0; settings->limiter = (status & FF800_INSTR_OPT_LIMITER)!=0; break; default: debugOutput(DEBUG_LEVEL_WARNING, "unimplemented for model %d\n", m_rme_model); return -1; } set_hardware_params(); return 0; } signed int Device::getInputSource(unsigned int channel) { if (m_rme_model != RME_MODEL_FIREFACE800) { debugOutput(DEBUG_LEVEL_WARNING, "selected channel sources are settable only on FF800\n"); return -1; } if (channel!=1 && channel!=7 && channel!=8) { debugOutput(DEBUG_LEVEL_WARNING, "channel %d source is fixed on FF800\n", channel); return -1; } if (channel == 1) return settings->input_opt[0]; else return settings->input_opt[channel-6]; } signed int Device::setInputSource(unsigned int channel, unsigned int src) { /* "opt" should be composed only of the FF_SWPARAM_FF800_INPUT_OPT_* * defines. */ signed int index; if (m_rme_model != RME_MODEL_FIREFACE800) { debugOutput(DEBUG_LEVEL_WARNING, "selected channel sources are settable only on FF800\n"); return -1; } if (channel!=1 && channel!=7 && channel!=8) { debugOutput(DEBUG_LEVEL_WARNING, "channel %d source is fixed on FF800\n", channel); return -1; } if (channel == 1) index = 0; else index = channel-6; settings->input_opt[index] = src; set_hardware_params(); return 0; } signed int Device::getSpdifInputMode(void) { return settings->spdif_input_mode; } signed int Device::setSpdifInputMode(signed int mode) { settings->spdif_input_mode = mode; set_hardware_params(); return 0; } signed int Device::getSpdifOutputIsOptical(void) { return settings->spdif_output_mode == FF_SWPARAM_SPDIF_OUTPUT_OPTICAL; } signed int Device::setSpdifOutputIsOptical(signed int enable) { settings->spdif_output_mode = enable==1?FF_SWPARAM_SPDIF_OUTPUT_OPTICAL:FF_SWPARAM_SPDIF_OUTPUT_COAX; set_hardware_params(); return 0; } signed int Device::getSpdifOutputEmphasisOn(void) { return settings->spdif_output_emphasis == FF_SWPARAM_SPDIF_OUTPUT_EMPHASIS_ON; } signed int Device::setSpdifOutputEmphasisOn(signed int enable) { settings->spdif_output_emphasis = enable==1?FF_SWPARAM_SPDIF_OUTPUT_EMPHASIS_ON:0; set_hardware_params(); return 0; } signed int Device::getSpdifOutputNonAudioOn(void) { return settings->spdif_output_nonaudio == FF_SWPARAM_SPDIF_OUTPUT_NONAUDIO_ON; } signed int Device::setSpdifOutputNonAudioOn(signed int enable) { settings->spdif_output_nonaudio = enable==1?FF_SWPARAM_SPDIF_OUTPUT_NONAUDIO_ON:0; set_hardware_params(); return 0; } signed int Device::getSpdifOutputProOn(void) { return settings->spdif_output_pro == FF_SWPARAM_SPDIF_OUTPUT_PRO_ON; } signed int Device::setSpdifOutputProOn(signed int enable) { settings->spdif_output_pro = enable==1?FF_SWPARAM_SPDIF_OUTPUT_PRO_ON:0; set_hardware_params(); return 0; } signed int Device::getAmpGain(unsigned int index) { if (m_rme_model != RME_MODEL_FIREFACE400) { debugOutput(DEBUG_LEVEL_WARNING, "Amp gains only supported on FF400\n"); return -1; } if (index > 21) { debugOutput(DEBUG_LEVEL_WARNING, "Amp gain index %d invalid\n", index); return -1; } return settings->amp_gains[index]; } signed int Device::setAmpGain(unsigned int index, signed int val) { if (m_rme_model != RME_MODEL_FIREFACE400) { debugOutput(DEBUG_LEVEL_WARNING, "Amp gains only supported on FF400\n"); return -1; } if (index > 21) { debugOutput(DEBUG_LEVEL_WARNING, "Amp gain index %d invalid\n", index); return -1; } settings->amp_gains[index] = val & 0xff; return set_hardware_ampgain(index, val); } signed int Device::getMixerGainIndex(unsigned int src_channel, unsigned int dest_channel) { return dest_channel*RME_FF800_MAX_CHANNELS + src_channel; } signed int Device::getMixerGain(unsigned int ctype, unsigned int src_channel, unsigned int dest_channel) { signed int idx = getMixerGainIndex(src_channel, dest_channel); switch (ctype) { case RME_FF_MM_INPUT: return settings->input_faders[idx]; break; case RME_FF_MM_PLAYBACK: return settings->playback_faders[idx]; break; case RME_FF_MM_OUTPUT: return settings->output_faders[src_channel]; break; } return 0; } signed int Device::setMixerGain(unsigned int ctype, unsigned int src_channel, unsigned int dest_channel, signed int val) { unsigned char *mixerflags = NULL; signed int idx = getMixerGainIndex(src_channel, dest_channel); switch (ctype) { case RME_FF_MM_INPUT: settings->input_faders[idx] = val; mixerflags = settings->input_mixerflags; break; case RME_FF_MM_PLAYBACK: settings->playback_faders[idx] = val; mixerflags = settings->playback_mixerflags; break; case RME_FF_MM_OUTPUT: settings->output_faders[src_channel] = val; mixerflags = settings->output_mixerflags; break; } // If the matrix channel is muted, override the fader value and // set it to zero. Note that this is different to the hardware // mute control dealt with by set_hardware_channel_mute(); the // latter deals with a muting separate from the mixer. if (mixerflags!=NULL && (mixerflags[idx] & FF_SWPARAM_MF_MUTED)!=0) { val = 0; } // Phase inversion is effected by sending a negative volume to the // hardware. However, when transitioning from 0 (-inf dB) to -1 (-90 // dB), the hardware seems to first send the volume up to a much higher // level before it drops down to the set point after about a tenth of a // second (this also seems to be the case when switching between // inversion modes). To work around this for the moment (at least until // it's understood, silently map a value of 0 to -1 when phase inversion // is active. if (mixerflags!=NULL && (mixerflags[idx] & FF_SWPARAM_MF_INVERTED)!=0) { if (val == 0) val = 1; val = -val; } return set_hardware_mixergain(ctype, src_channel, dest_channel, val); } signed int Device::getMixerFlags(unsigned int ctype, unsigned int src_channel, unsigned int dest_channel, unsigned int flagmask) { unsigned char *mixerflags = NULL; signed int idx = getMixerGainIndex(src_channel, dest_channel); if (ctype == RME_FF_MM_OUTPUT) { mixerflags = settings->output_mixerflags; idx = src_channel; } else if (ctype == RME_FF_MM_INPUT) mixerflags = settings->input_mixerflags; else mixerflags = settings->playback_mixerflags; return mixerflags[idx] & flagmask; } signed int Device::setMixerFlags(unsigned int ctype, unsigned int src_channel, unsigned int dest_channel, unsigned int flagmask, signed int val) { unsigned char *mixerflags = NULL; signed int idx = getMixerGainIndex(src_channel, dest_channel); if (ctype == RME_FF_MM_OUTPUT) { mixerflags = settings->output_mixerflags; idx = src_channel; } else if (ctype == RME_FF_MM_INPUT) mixerflags = settings->input_mixerflags; else mixerflags = settings->playback_mixerflags; // FIXME: When switching inversion modes, the hardware seems to a channel to // full volume for about 1/10 sec. Attempt to avoid this by temporarily // muting the channel. This doesn't seem to work though. // if (flagmask & FF_SWPARAM_MF_INVERTED) // set_hardware_mixergain(ctype, src_channel, dest_channel, 0); if (val == 0) mixerflags[idx] &= ~flagmask; else mixerflags[idx] |= flagmask; if (flagmask & (FF_SWPARAM_MF_MUTED|FF_SWPARAM_MF_INVERTED)) { // Mixer channel muting/inversion is handled via the gain control return setMixerGain(ctype, src_channel, dest_channel, getMixerGain(ctype, src_channel, dest_channel)); } return 0; } signed int Device::getClockMode(void) { return settings->clock_mode; } signed int Device::setClockMode(signed int mode) { if (mode!=FF_SWPARAM_CLOCK_MODE_MASTER && mode!=FF_SWPARAM_CLOCK_MODE_AUTOSYNC) return -1; settings->clock_mode = mode; set_hardware_params(); return 0; } signed int Device::getSyncRef(void) { return settings->sync_ref; } signed int Device::setSyncRef(signed int ref) { settings->sync_ref = ref; set_hardware_params(); return 0; } signed int Device::getBandwidthLimit(void) { return settings->limit_bandwidth; } signed int Device::setBandwidthLimit(signed int limit) { if (limit < FF_DEV_FLASH_BWLIMIT_SEND_ALL_CHANNELS || limit > FF_DEV_FLASH_BWLIMIT_ANALOG_ONLY) return -1; // FF400 doesn't have a second ADAT channel, so this setting isn't // relevant for that interface. if (m_rme_model==RME_MODEL_FIREFACE400 && limit==FF_DEV_FLASH_BWLIMIT_NO_ADAT2) return -1; settings->limit_bandwidth = limit; set_hardware_params(); return 0; } signed int Device::getTcoLtc(void) { FF_TCO_state_t ff_tco_state; if (read_tco_state(&ff_tco_state) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to read TCO state\n"); return 0; } return (ff_tco_state.hours<<24) | (ff_tco_state.minutes<<16) | (ff_tco_state.seconds<<8) | (ff_tco_state.frames); } signed int Device::getTcoLtcValid(void) { FF_TCO_state_t ff_tco_state; if (read_tco_state(&ff_tco_state) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to read TCO state\n"); return 0; } return ff_tco_state.ltc_valid; } signed int Device::getTcoLock(void) { FF_TCO_state_t ff_tco_state; if (read_tco_state(&ff_tco_state) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to read TCO state\n"); return 0; } return ff_tco_state.locked; } signed int Device::getTcoLtcFrameRate(void) { FF_TCO_state_t ff_tco_state; if (read_tco_state(&ff_tco_state) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to read TCO state\n"); return 0; } return ff_tco_state.frame_rate; } signed int Device::getTcoLtcDropFrame(void) { FF_TCO_state_t ff_tco_state; if (read_tco_state(&ff_tco_state) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to read TCO state\n"); return 0; } return ff_tco_state.drop_frame; } signed int Device::getTcoVideoType(void) { FF_TCO_state_t ff_tco_state; if (read_tco_state(&ff_tco_state) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to read TCO state\n"); return 0; } return ff_tco_state.video_input; } signed int Device::getTcoWordClk(void) { FF_TCO_state_t ff_tco_state; if (read_tco_state(&ff_tco_state) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to read TCO state\n"); return 0; } return ff_tco_state.word_clock_state; } float Device::getTcoFrequency(void) { FF_TCO_state_t ff_tco_state; if (read_tco_state(&ff_tco_state) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to read TCO state\n"); return 0; } return ff_tco_state.sample_rate; } signed int Device::getTcoWordClkState(void) { FF_TCO_state_t ff_tco_state; if (read_tco_state(&ff_tco_state) != 0) { debugOutput(DEBUG_LEVEL_ERROR, "failed to read TCO state\n"); return 0; } return ff_tco_state.word_clock_state; } signed int Device::getTcoSyncSrc(void) { return tco_settings->input; } signed int Device::setTcoSyncSrc(signed int src) { tco_settings->input = src; return write_tco_settings(tco_settings); } signed int Device::getTcoFrameRate(void) { return tco_settings->frame_rate; } signed int Device::setTcoFrameRate(signed int rate_id) { tco_settings->frame_rate = rate_id; return write_tco_settings(tco_settings); } signed int Device::getTcoSampleRate(void) { return tco_settings->sample_rate; } signed int Device::setTcoSampleRate(signed int rate_param_id) { tco_settings->sample_rate = rate_param_id; return write_tco_settings(tco_settings); } signed int Device::getTcoPull(void) { return tco_settings->pull; } signed int Device::setTcoPull(signed int pull) { tco_settings->pull = pull; return write_tco_settings(tco_settings); } signed int Device::getTcoTermination(void) { return tco_settings->termination == FF_TCOPARAM_TERMINATION_ON; } signed int Device::setTcoTermination(signed int enable) { tco_settings->termination = enable ? FF_TCOPARAM_TERMINATION_ON : 0; return write_tco_settings(tco_settings); } signed int Device::getTcoWordClkConv(void) { return tco_settings->word_clock; } signed int Device::setTcoWordClkConv(signed int conv) { tco_settings->word_clock = conv; return write_tco_settings(tco_settings); } } libffado-2.4.5/src/rme/rme_shm.cpp0000644000175000001440000001125514206145246016400 0ustar jwoitheusers/* * Copyright (C) 2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * This file implements a simple interface to a shared memory object used * to share device configuration between FFADO components. This is required * because a significant amount of device information is not available for * reading from the device itself. * * The idea is that each RME FFADO process will call rme_shm_open() to * obtain a pointer to shared memory containing the structure of interest. * If no process has yet created the shared object it will be created at * this point; otherwise the existing object will be used. Each new process * to use the object increments a reference count. * * On exit, processes using this shared object will call rme_shm_close(). * The reference counter is decremented and if it becomes zero the shared * object will be unlinked. This way, so long as at least one RME process * is active (which doesn't have to be the process which created the object * initially) the device's configuration will be persistent. */ #define RME_SHM_NAME "/ffado:rme_shm-" #define RME_SHM_SIZE sizeof(rme_shm_t) #define RME_SHM_LOCKNAME "/ffado:rme_shm_lock" #include #include #include #include #include #include #include #include "rme_shm.h" static signed int rme_shm_lock_for_setup(void) { signed lockfd; do { // The check for existance and shm creation are atomic so it's safe // to use this as the basis for a global lock. lockfd = shm_open(RME_SHM_LOCKNAME, O_RDWR | O_CREAT | O_EXCL, 0644); if (lockfd < 0) usleep(10000); } while (lockfd < 0); return lockfd; } static void rme_shm_unlock_for_setup(signed int lockfd) { close(lockfd); shm_unlink(RME_SHM_LOCKNAME); } void rme_shm_lock(rme_shm_t *shm_data) { pthread_mutex_lock(&shm_data->lock); } void rme_shm_unlock(rme_shm_t *shm_data) { pthread_mutex_unlock(&shm_data->lock); } signed int rme_shm_open(std::string id, rme_shm_t **shm_data) { std::string shm_name; signed int shmfd, lockfd; rme_shm_t *data; signed int created = 0; if (shm_data == NULL) { return RSO_ERROR; } *shm_data = NULL; lockfd = rme_shm_lock_for_setup(); shm_name = std::string(RME_SHM_NAME); shm_name.append(id); shmfd = shm_open(shm_name.c_str(), O_RDWR, 0644); if (shmfd < 0) { if (errno == ENOENT) { shmfd = shm_open(shm_name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0644); if (shmfd < 0) return RSO_ERR_SHM; else { ftruncate(shmfd, RME_SHM_SIZE); created = 1; } } else return RSO_ERR_SHM; } data = (rme_shm_t *)mmap(NULL, RME_SHM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0); close(shmfd); if (data == MAP_FAILED) return RSO_ERR_MMAP; if (created) { pthread_mutex_init(&data->lock, NULL); snprintf(data->shm_name, sizeof(data->shm_name), "%s", shm_name.c_str()); } rme_shm_lock(data); data->ref_count++; rme_shm_unlock(data); rme_shm_unlock_for_setup(lockfd); *shm_data = data; return created?RSO_OPEN_CREATED:RSO_OPEN_ATTACHED; } signed int rme_shm_close(rme_shm_t *shm_data) { std::string shm_name = std::string(shm_data->shm_name); signed int unlink = 0; signed int lockfd; lockfd = rme_shm_lock_for_setup(); rme_shm_lock(shm_data); shm_data->ref_count--; unlink = (shm_data->ref_count == 0); rme_shm_unlock(shm_data); if (unlink) { // This is safe: if the reference count is zero there can't be any // other process using the lock at this point. pthread_mutex_destroy(&shm_data->lock); } munmap(shm_data, RME_SHM_SIZE); if (unlink) shm_unlink(shm_name.c_str()); rme_shm_unlock_for_setup(lockfd); return unlink?RSO_CLOSE_DELETE:RSO_CLOSE; } libffado-2.4.5/src/rme/rme_shm.h0000644000175000001440000000404014206145246016037 0ustar jwoitheusers/* * Copyright (C) 2009 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef _RME_SHM_H #define _RME_SHM_H #include #include #include "fireface_def.h" #define RME_SHM_NAMELEN 64 /* Structure used within shared memory object */ typedef struct rme_shm_t { signed int ref_count; signed int settings_valid, tco_settings_valid; FF_software_settings_t settings; signed int tco_present; FF_TCO_settings_t tco_settings; signed int dds_freq; // Optionally explicitly set hardware freq signed int software_freq; // Sampling frequency in use by software signed int hardware_freq; // Frequency actually programmed into hardware signed int is_streaming; pthread_mutex_t lock; char shm_name[RME_SHM_NAMELEN]; } rme_shm_t; /* Return values from rme_shm_open(). RSO = Rme Shared Object. */ #define RSO_ERR_MMAP -3 #define RSO_ERR_SHM -2 #define RSO_ERROR -1 #define RSO_OPEN_CREATED 0 #define RSO_OPEN_ATTACHED 1 /* Return values from rme_shm_close() */ #define RSO_CLOSE 0 #define RSO_CLOSE_DELETE 1 /* Functions */ void rme_shm_lock(rme_shm_t *shm_data); void rme_shm_unlock(rme_shm_t *shm_data); signed int rme_shm_open(std::string id, rme_shm_t **shm_data); signed int rme_shm_close(rme_shm_t *shm_data); #endif libffado-2.4.5/src/.gitignore0000644000175000001440000000007512132617070015440 0ustar jwoitheuserstest-cyclecalc test-debugmodule test-dll test-unittests-util libffado-2.4.5/support/0000755000175000001440000000000014206145613014376 5ustar jwoitheuserslibffado-2.4.5/support/SConscript0000644000175000001440000000201714206145246016412 0ustar jwoitheusers# # Copyright (C) 2007, 2008, 2010 Arnold Krille # Copyright (C) 2007, 2008 Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Import( 'env' ) env = env.Clone() dirs=["mixer-qt4","firmware","tools","alsa"] if env['DBUS1_FLAGS']: dirs.append('dbus') env.SConscript( dirs=dirs, exports="env" ) # vim: et libffado-2.4.5/support/alsa/0000755000175000001440000000000014206145613015316 5ustar jwoitheuserslibffado-2.4.5/support/alsa/SConscript0000644000175000001440000000243514206145246017336 0ustar jwoitheusers# # Copyright (C) 2007-2008 Arnold Krille # Copyright (C) 2007-2008 Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Import( 'env' ) env = env.Clone() # # For the alsa plugin # env.AppendUnique( CPPPATH=["#/", "#/src"] ) env.PrependUnique( LIBPATH=[env['build_base']+"src"] ) env.PrependUnique( LIBS=["ffado"] ) #if not env.GetOption( "clean" ): sources = ["alsa_plugin.cpp"] if 'ALSA_FLAGS' in env and env['ALSA_FLAGS']: env.MergeFlags( env["ALSA_FLAGS"].decode() ) env.MergeFlags( "-DPIC" ) alsaplugin = env.SharedLibrary( "asound_module_pcm_ffado", sources ) libffado-2.4.5/support/alsa/alsa_plugin.cpp0000644000175000001440000005045211066143774020335 0ustar jwoitheusers/* * ALSA FireWire Plugin * * Copyright (c) 2008 Pieter Palmers * * Based upon the JACK <-> ALSA plugin * Copyright (c) 2003 by Maarten de Boer * 2005 Takashi Iwai * * 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 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 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 DECLARE_GLOBAL_DEBUG_MODULE; #include "libutil/IpcRingBuffer.h" #include "libutil/SystemTimeSource.h" using namespace Util; extern "C" { #include "version.h" #include #include #include #include #include #include #include #include #define FFADO_PLUGIN_VERSION "0.0.2" #define PRINT_FUNCTION_ENTRY (printMessage("entering %s\n",__FUNCTION__)) // #define PRINT_FUNCTION_ENTRY typedef struct { snd_pcm_ioplug_t io; int fd; int activated; unsigned int hw_ptr; unsigned int channels; snd_pcm_channel_area_t *areas; snd_pcm_stream_t stream; // thread for polling pthread_t thread; // IPC stuff Util::IpcRingBuffer* buffer; // options long int verbose; snd_pcm_uframes_t period; long int nb_buffers; } snd_pcm_ffado_t; static int snd_pcm_ffado_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) { PRINT_FUNCTION_ENTRY; snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; ffado->channels=0; if (ffado->stream == SND_PCM_STREAM_PLAYBACK) { // HACK ffado->channels=2; } else { // HACK ffado->channels=2; } return 0; } // static snd_pcm_sframes_t snd_pcm_ffado_write(snd_pcm_ioplug_t *io, // const snd_pcm_channel_area_t *areas, // snd_pcm_uframes_t offset, // snd_pcm_uframes_t size) // { // PRINT_FUNCTION_ENTRY; // snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; // IpcRingBuffer::eResult res; // unsigned int i; // // do { // uint32_t *audiobuffers_raw; // res = ffado->buffer->requestBlockForWrite((void**) &audiobuffers_raw); // pointer voodoo // if(res == IpcRingBuffer::eR_OK) { // memset(audiobuffers_raw, 0, ffado->channels * ffado->period * 4); // for (i = 0; i < ffado->channels; i++) { // uint32_t *alsa_data_ptr = (uint32_t *)((char *)areas[i].addr + (areas[i].step * offset / 8)); // uint32_t *ffado_data_ptr = audiobuffers_raw + i*ffado->period; // memcpy(ffado_data_ptr, alsa_data_ptr, ffado->period * 4); // } // // release the block // res = ffado->buffer->releaseBlockForWrite(); // if(res != IpcRingBuffer::eR_OK) { // debugOutput(DEBUG_LEVEL_NORMAL, "PBK: error committing memory block\n"); // break; // } // } else if(res != IpcRingBuffer::eR_Again) { // debugOutput(DEBUG_LEVEL_NORMAL, "PBK: error getting memory block\n"); // } // } while (res == IpcRingBuffer::eR_Again); // // ffado->hw_ptr += ffado->period; // ffado->hw_ptr %= io->buffer_size; // // if(size != ffado->period) { // debugWarning("size %d, period %d, offset %d\n", size, ffado->period, offset); // } else { // debugOutput(DEBUG_LEVEL_NORMAL, "size %d, period %d, offset %d\n", size, ffado->period, offset); // } // // if(res == IpcRingBuffer::eR_OK) { // return ffado->period; // } else { // debugOutput(DEBUG_LEVEL_NORMAL, "error happened\n"); // return -1; // } // } // // static snd_pcm_sframes_t snd_pcm_ffado_read(snd_pcm_ioplug_t *io, // const snd_pcm_channel_area_t *areas, // snd_pcm_uframes_t offset, // snd_pcm_uframes_t size) // { // PRINT_FUNCTION_ENTRY; // snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; // IpcRingBuffer::eResult res; // unsigned int i; // // do { // uint32_t *audiobuffers_raw; // res = ffado->buffer->requestBlockForRead((void**) &audiobuffers_raw); // pointer voodoo // if(res == IpcRingBuffer::eR_OK) { // for (i = 0; i < ffado->channels; i++) { // uint32_t *alsa_data_ptr = (uint32_t *)((char *)areas[i].addr + (areas[i].step * offset / 8)); // uint32_t *ffado_data_ptr = audiobuffers_raw + i*ffado->period; // memcpy(alsa_data_ptr, ffado_data_ptr, ffado->period * 4); // } // // release the block // res = ffado->buffer->releaseBlockForRead(); // if(res != IpcRingBuffer::eR_OK) { // debugOutput(DEBUG_LEVEL_NORMAL, "CAP: error committing memory block\n"); // break; // } // } else if(res != IpcRingBuffer::eR_Again) { // debugOutput(DEBUG_LEVEL_NORMAL, "CAP: error getting memory block\n"); // } // } while (res == IpcRingBuffer::eR_Again); // // ffado->hw_ptr += ffado->period; // ffado->hw_ptr %= io->buffer_size; // // if(res == IpcRingBuffer::eR_OK) { // return ffado->period; // } else { // debugOutput(DEBUG_LEVEL_NORMAL, "error happened\n"); // return -1; // } // } static int snd_pcm_ffado_pollfunction(snd_pcm_ffado_t *ffado) { // PRINT_FUNCTION_ENTRY; static char buf[1]; snd_pcm_ioplug_t *io = &ffado->io; const snd_pcm_channel_area_t *areas; assert(ffado); assert(ffado->buffer); IpcRingBuffer::eResult res; if (ffado->stream == SND_PCM_STREAM_PLAYBACK) { debugOutput(DEBUG_LEVEL_VERBOSE, "PBK: wait\n"); res = ffado->buffer->waitForWrite(); debugOutput(DEBUG_LEVEL_VERBOSE, "PBK: done, fill: %d\n", ffado->buffer->getBufferFill()); if(res == IpcRingBuffer::eR_OK) { do { uint32_t *audiobuffers_raw; res = ffado->buffer->requestBlockForWrite((void**) &audiobuffers_raw); // pointer voodoo if(res == IpcRingBuffer::eR_OK) { // we have the memory block, do the actual transfer // silence the block memset(audiobuffers_raw, 0, ffado->channels * ffado->period * 4); if(io->state == SND_PCM_STATE_RUNNING) { // get the data address the data comes from areas = snd_pcm_ioplug_mmap_areas(io); // create the list of areas where the data goes to unsigned int channel = 0; for (channel = 0; channel < ffado->channels; channel++) { uint32_t *target = (audiobuffers_raw + channel*ffado->period); ffado->areas[channel].addr = target; ffado->areas[channel].first = 0; ffado->areas[channel].step = 4*8; // FIXME: hardcoded sample size } snd_pcm_uframes_t xfer = 0; while (xfer < ffado->period) { snd_pcm_uframes_t frames = ffado->period - xfer; snd_pcm_uframes_t offset = ffado->hw_ptr; snd_pcm_uframes_t cont = io->buffer_size - offset; if (cont < frames) frames = cont; for (channel = 0; channel < ffado->channels; channel++) { snd_pcm_area_copy(&ffado->areas[channel], xfer, &areas[channel], offset, frames, io->format); } ffado->hw_ptr += frames; ffado->hw_ptr %= io->buffer_size; xfer += frames; } } // release the block res = ffado->buffer->releaseBlockForWrite(); if(res != IpcRingBuffer::eR_OK) { debugOutput(DEBUG_LEVEL_NORMAL, "PBK: error committing memory block\n"); break; } } else if(res != IpcRingBuffer::eR_Again) { debugOutput(DEBUG_LEVEL_NORMAL, "PBK: error getting memory block\n"); } if (res == IpcRingBuffer::eR_Again) { debugWarning("Again\n"); } } while (res == IpcRingBuffer::eR_Again); } else { debugError("Error while waiting\n"); } } else { res = ffado->buffer->waitForRead(); if(res == IpcRingBuffer::eR_OK) { do { uint32_t *audiobuffers_raw; res = ffado->buffer->requestBlockForRead((void**) &audiobuffers_raw); // pointer voodoo if(res == IpcRingBuffer::eR_OK) { // we have the memory block, do the actual transfer if(io->state == SND_PCM_STATE_RUNNING) { // get the data address the data goes to areas = snd_pcm_ioplug_mmap_areas(io); // create the list of areas where the data comes from unsigned int channel = 0; for (channel = 0; channel < io->channels; channel++) { ffado->areas[channel].addr = audiobuffers_raw + channel*ffado->period; ffado->areas[channel].first = 0; ffado->areas[channel].step = 4*8; // FIXME: hardcoded sample size } snd_pcm_uframes_t xfer = 0; while (xfer < ffado->period) { snd_pcm_uframes_t frames = ffado->period - xfer; snd_pcm_uframes_t offset = ffado->hw_ptr; snd_pcm_uframes_t cont = io->buffer_size - offset; if (cont < frames) frames = cont; for (channel = 0; channel < io->channels; channel++) { snd_pcm_area_copy(&areas[channel], offset, &ffado->areas[channel], xfer, frames, io->format); } ffado->hw_ptr += frames; ffado->hw_ptr %= io->buffer_size; xfer += frames; } } // release the block res = ffado->buffer->releaseBlockForRead(); if(res != IpcRingBuffer::eR_OK) { debugOutput(DEBUG_LEVEL_NORMAL, "CAP: error committing memory block\n"); break; } } else if(res != IpcRingBuffer::eR_Again) { debugOutput(DEBUG_LEVEL_NORMAL, "CAP: error getting memory block\n"); } } while (res == IpcRingBuffer::eR_Again); } else { debugError("Error while waiting\n"); } } write(ffado->fd, buf, 1); /* for polling */ if(res == IpcRingBuffer::eR_OK) { return 0; } else { debugOutput(DEBUG_LEVEL_NORMAL, "error happened\n"); return -1; } } // static int // snd_pcm_ffado_pollfunction(snd_pcm_ffado_t *ffado) // { // // wait for a period // IpcRingBuffer::eResult res; // if(ffado->stream == SND_PCM_STREAM_PLAYBACK) { // res = ffado->buffer->waitForWrite(); // } else { // res = ffado->buffer->waitForRead(); // } // // if(res == IpcRingBuffer::eR_OK) { // char buf[1]; // write(ffado->fd, buf, 1); /* for polling */ // return 0; // } else { // return -1; // } // } static void * ffado_workthread(void *arg) { PRINT_FUNCTION_ENTRY; snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)arg; int oldstate; pthread_setcancelstate (PTHREAD_CANCEL_DEFERRED, &oldstate); while (snd_pcm_ffado_pollfunction(ffado) == 0) { pthread_testcancel(); } return 0; } static int snd_pcm_ffado_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) { static char buf[1]; PRINT_FUNCTION_ENTRY; assert(pfds && nfds == 1 && revents); read(pfds[0].fd, buf, 1); *revents = pfds[0].revents; return 0; } static void snd_pcm_ffado_free(snd_pcm_ffado_t *ffado) { PRINT_FUNCTION_ENTRY; if (ffado) { if (ffado->fd >= 0) close(ffado->fd); if (ffado->io.poll_fd >= 0) close(ffado->io.poll_fd); free(ffado->areas); free(ffado); } } static int snd_pcm_ffado_close(snd_pcm_ioplug_t *io) { PRINT_FUNCTION_ENTRY; snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; // cleanup the SHM structures here delete ffado->buffer; ffado->buffer = NULL; snd_pcm_ffado_free(ffado); return 0; } static snd_pcm_sframes_t snd_pcm_ffado_pointer(snd_pcm_ioplug_t *io) { PRINT_FUNCTION_ENTRY; snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; return ffado->hw_ptr; } static int snd_pcm_ffado_start(snd_pcm_ioplug_t *io) { int result = 0; snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; PRINT_FUNCTION_ENTRY; result = pthread_create (&ffado->thread, 0, ffado_workthread, ffado); if(result) return result; // FIXME: start the SHM stuff return 0; } static int snd_pcm_ffado_stop(snd_pcm_ioplug_t *io) { snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; PRINT_FUNCTION_ENTRY; if(pthread_cancel(ffado->thread)) { debugError("could not cancel thread!\n"); } if(pthread_join(ffado->thread,NULL)) { debugError("could not join thread!\n"); } // FIXME: stop the SHM client return 0; } static int snd_pcm_ffado_prepare(snd_pcm_ioplug_t *io) { PRINT_FUNCTION_ENTRY; return 0; } static snd_pcm_ioplug_callback_t ffado_pcm_callback; #define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0])) static int ffado_set_hw_constraint(snd_pcm_ffado_t *ffado) { PRINT_FUNCTION_ENTRY; unsigned int access_list[] = { SND_PCM_ACCESS_MMAP_NONINTERLEAVED, // SND_PCM_ACCESS_RW_NONINTERLEAVED, }; unsigned int rate_list[1]; unsigned int format = SND_PCM_FORMAT_S24; int err; // FIXME: make all of the parameters dynamic instead of static rate_list[0] = 48000; // setup the plugin capabilities if ((err = snd_pcm_ioplug_set_param_list(&ffado->io, SND_PCM_IOPLUG_HW_ACCESS, ARRAY_SIZE(access_list), access_list)) < 0 || (err = snd_pcm_ioplug_set_param_list(&ffado->io, SND_PCM_IOPLUG_HW_RATE, ARRAY_SIZE(rate_list), rate_list)) < 0 || (err = snd_pcm_ioplug_set_param_list(&ffado->io, SND_PCM_IOPLUG_HW_FORMAT, 1, &format)) < 0 || (err = snd_pcm_ioplug_set_param_minmax(&ffado->io, SND_PCM_IOPLUG_HW_CHANNELS, ffado->channels, ffado->channels)) < 0 || (err = snd_pcm_ioplug_set_param_minmax(&ffado->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, ffado->period, ffado->period)) < 0 || (err = snd_pcm_ioplug_set_param_minmax(&ffado->io, SND_PCM_IOPLUG_HW_PERIODS, ffado->nb_buffers, ffado->nb_buffers)) < 0) return err; return 0; } static int snd_pcm_ffado_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode) { PRINT_FUNCTION_ENTRY; snd_pcm_ffado_t *ffado; int err; int fd[2]; assert(pcmp); ffado = (snd_pcm_ffado_t *)calloc(1, sizeof(*ffado)); if (!ffado) return -ENOMEM; ffado->stream=stream; // discover the devices to discover the capabilities // get the SHM structure socketpair(AF_LOCAL, SOCK_STREAM, 0, fd); ffado->fd = fd[0]; // initialize callback struct ffado_pcm_callback.close = snd_pcm_ffado_close; ffado_pcm_callback.start = snd_pcm_ffado_start; ffado_pcm_callback.stop = snd_pcm_ffado_stop; ffado_pcm_callback.pointer = snd_pcm_ffado_pointer; ffado_pcm_callback.hw_params = snd_pcm_ffado_hw_params; ffado_pcm_callback.prepare = snd_pcm_ffado_prepare; ffado_pcm_callback.poll_revents = snd_pcm_ffado_poll_revents; // if (stream == SND_PCM_STREAM_PLAYBACK) { // ffado_pcm_callback.transfer = snd_pcm_ffado_write; // } else { // ffado_pcm_callback.transfer = snd_pcm_ffado_read; // } // prepare io struct ffado->io.version = SND_PCM_IOPLUG_VERSION; ffado->io.name = "FFADO PCM Plugin"; ffado->io.callback = &ffado_pcm_callback; ffado->io.private_data = ffado; // ffado->io.mmap_rw = 0; ffado->io.mmap_rw = 1; ffado->io.poll_fd = fd[1]; ffado->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN; err = snd_pcm_ioplug_create(&ffado->io, name, stream, mode); if (err < 0) { snd_pcm_ffado_free(ffado); return err; } err = ffado_set_hw_constraint(ffado); if (err < 0) { snd_pcm_ioplug_delete(&ffado->io); return err; } *pcmp = ffado->io.pcm; // these are the params ffado->channels = 2; ffado->period = 1024; ffado->verbose = 6; ffado->nb_buffers = 5; setDebugLevel(ffado->verbose); ffado->areas = (snd_pcm_channel_area_t *)calloc(ffado->channels, sizeof(snd_pcm_channel_area_t)); if (!ffado->areas) { snd_pcm_ffado_free(ffado); return -ENOMEM; } // prepare the IPC buffer unsigned int buffsize = ffado->channels * ffado->period * 4; if(stream == SND_PCM_STREAM_PLAYBACK) { ffado->buffer = new IpcRingBuffer("playbackbuffer", IpcRingBuffer::eBT_Slave, IpcRingBuffer::eD_Outward, IpcRingBuffer::eB_Blocking, ffado->nb_buffers, buffsize); if(ffado->buffer == NULL) { debugError("Could not create playbackbuffer\n"); return -1; } if(!ffado->buffer->init()) { debugError("Could not init playbackbuffer\n"); delete ffado->buffer; ffado->buffer = NULL; return -1; } ffado->buffer->setVerboseLevel(ffado->verbose); } else { ffado->buffer = new IpcRingBuffer("capturebuffer", IpcRingBuffer::eBT_Slave, IpcRingBuffer::eD_Inward, IpcRingBuffer::eB_Blocking, ffado->nb_buffers, buffsize); if(ffado->buffer == NULL) { debugError("Could not create capturebuffer\n"); return -1; } if(!ffado->buffer->init()) { debugError("Could not init capturebuffer\n"); delete ffado->buffer; ffado->buffer = NULL; return -1; } ffado->buffer->setVerboseLevel(ffado->verbose); } return 0; } SND_PCM_PLUGIN_DEFINE_FUNC(ffado) { printMessage("FireWire plugin for ALSA\n version %s compiled %s %s\n using %s\n", FFADO_PLUGIN_VERSION, __DATE__, __TIME__, PACKAGE_STRING); snd_config_iterator_t i, next; int err; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); const char *id; if (snd_config_get_id(n, &id) < 0) continue; if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) continue; SNDERR("Unknown field %s", id); return -EINVAL; } err = snd_pcm_ffado_open(pcmp, name, stream, mode); return err; } SND_PCM_PLUGIN_SYMBOL(ffado); } // extern "C" libffado-2.4.5/support/dbus/0000755000175000001440000000000014206145613015333 5ustar jwoitheuserslibffado-2.4.5/support/dbus/SConscript0000644000175000001440000000652514206145246017357 0ustar jwoitheusers#!/bin/env python # # Copyright (C) 2007,2008,2011 Arnold Krille # Copyright (C) 2007-2008 Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os Import( 'env' ) env = env.Clone() # # For the debugging apps # env.AppendUnique( CPPPATH=["#/", "#/src"] ) env.PrependUnique( LIBPATH=[env['build_base']+"src"] ) env.PrependUnique( LIBS=["ffado", "pthread"] ) if not env.GetOption( "clean" ): env.MergeFlags( env["DBUS1_FLAGS"].decode() ) env.MergeFlags( env["DBUSC1_FLAGS"].decode() ) env.MergeFlags( env['LIBRAW1394_FLAGS'].decode() ) if not env['SERIALIZE_USE_EXPAT']: if 'LIBXML30_FLAGS' in env : env.MergeFlags( env['LIBXML30_FLAGS'].decode() ) if not('LIBXML30_FLAGS' in env) : env.MergeFlags( env['LIBXML26_FLAGS'].decode() ) else: env.PrependUnique( LIBS=["expat"] ) env.Xml2Cpp_Proxy('controlclient-glue.h', 'control-interface.xml') env.Xml2Cpp_Adaptor('controlserver-glue.h', 'control-interface.xml') static_env = env.Clone() apps = { } installapps = [] apps = { "ffado-dbus-server":"ffado-dbus-server.cpp controlserver.cpp", "test-dbus" : "test-dbus.cpp controlclient.cpp", "test-dbus-server" : "test-dbus-server.cpp controlserver.cpp", } manpages = ( "ffado-dbus-server.1", ) installapps += [ "ffado-dbus-server" ] for app in apps.keys(): env.Program( target=app, source = env.Split( apps[app] ) ) if app.find( "test" ) == -1: env.Install( "$bindir", app ) for manpage in manpages: section = manpage.split(".")[1] dest = os.path.join("$mandir", "man"+section, manpage) env.InstallAs(source=manpage, target=dest) servicefile = env.ScanReplace('org.ffado.Control.service.in') if env['dbus_service_dir'] and ( env.destdir or os.access( env['dbus_service_dir'], os.W_OK ) ): print("Will install the service-file") targetdir = env.destdir + env['dbus_service_dir'].decode() env.Alias( "install", env.Install( env.destdir + env['dbus_service_dir'].decode(), servicefile ) ) else: if not env['dbus_service_dir']: print('Can\'t install the system-wide dbus service file as the concerned variable is not defined.') else: if not os.access( env['dbus_service_dir'], os.W_OK ): print('Insufficient rights to install the system-wide dbus service file.') print('Please run the "scons install" command with higher authority.') # static versions if static_env['BUILD_STATIC_TOOLS']: static_env.Append(LIBS=File('#/src/libffado.a')) for app in apps.keys(): static_app = app + "-static" static_env.Program( target=static_app, source = static_env.Split( apps[app] ) ) # vim: et libffado-2.4.5/support/dbus/controlclient.cpp0000644000175000001440000000236414206145246020725 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "controlclient.h" namespace DBusControl { // --- IMPL_DEBUG_MODULE( ContinuousClient, ContinuousClient, DEBUG_LEVEL_VERBOSE ); ContinuousClient::ContinuousClient( DBus::Connection& connection, const char* path, const char* name ) : DBus::ObjectProxy(connection, path, name) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created ContinuousClient '%s' on '%s'\n", name, path ); } } // end of namespace Control libffado-2.4.5/support/dbus/controlclient.h0000644000175000001440000000273214206145246020371 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef CONTROLCLIENT_H #define CONTROLCLIENT_H #include "debugmodule/debugmodule.h" /* Work around a bug in dbus-c++ 0.9.0 which prevents compilation under gcc7 */ #ifndef DBUS_HAS_RECURSIVE_MUTEX #define DBUS_HAS_RECURSIVE_MUTEX #endif #include #include "controlclient-glue.h" namespace DBusControl { // simple fader element class ContinuousClient : public org::ffado::Control::Element::Continuous_proxy, public DBus::IntrospectableProxy, public DBus::ObjectProxy { public: ContinuousClient( DBus::Connection& connection, const char* path, const char* name ); private: DECLARE_DEBUG_MODULE; }; } #endif //CONTROLCLIENT_H libffado-2.4.5/support/dbus/controlserver.cpp0000644000175000001440000006405114206145246020756 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "controlserver.h" #include "libcontrol/Element.h" #include "libcontrol/BasicElements.h" #include "libcontrol/MatrixMixer.h" #include "libcontrol/CrossbarRouter.h" #include "libutil/Time.h" #include "libutil/PosixMutex.h" namespace DBusControl { IMPL_DEBUG_MODULE( Element, Element, DEBUG_LEVEL_NORMAL ); // --- Element Element::Element( DBus::Connection& connection, std::string p, Element* parent, Control::Element &slave) : DBus::ObjectAdaptor(connection, p) , m_Parent(parent) , m_Slave(slave) , m_UpdateLock( NULL ) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Element on '%s'\n", path().c_str() ); // allocate a lock if(parent == NULL) { m_UpdateLock = new Util::PosixMutex("CTLSVEL"); } else { m_UpdateLock = NULL; } // set verbose level AFTER allocating the lock setVerboseLevel(m_Slave.getVerboseLevel()); } void Element::setVerboseLevel( const int32_t &i) { setDebugLevel(i); m_Slave.setVerboseLevel(i); if(m_UpdateLock) m_UpdateLock->setVerboseLevel(i); } int32_t Element::getVerboseLevel() { return getDebugLevel(); } bool Element::canChangeValue() { return m_Slave.canChangeValue(); } void Element::Lock() { if(m_Parent) { m_Parent->Lock(); } else { m_UpdateLock->Lock(); } } void Element::Unlock() { if(m_Parent) { m_Parent->Unlock(); } else { m_UpdateLock->Unlock(); } } bool Element::isLocked() { if(m_Parent) { return m_Parent->isLocked(); } else { return m_UpdateLock->isLocked(); } } Util::Mutex* Element::getLock() { if(m_Parent) { return m_Parent->getLock(); } else { return m_UpdateLock; } } uint64_t Element::getId( ) { return m_Slave.getId(); } std::string Element::getName( ) { return std::string(m_Slave.getName()); } std::string Element::getLabel( ) { return std::string(m_Slave.getLabel()); } std::string Element::getDescription( ) { return std::string(m_Slave.getDescription()); } // --- Container Container::Container( DBus::Connection& connection, std::string p, Element* parent, Control::Container &slave) : Element(connection, p, parent, slave) , m_Slave(slave) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Container on '%s'\n", path().c_str() ); setDebugLevel(slave.getVerboseLevel()); // register an update signal handler m_updateFunctor = new MemberSignalFunctor1< Container*, void (Container::*)(int) > ( this, &Container::updated, (int)Control::Container::eS_Updated ); if(m_updateFunctor) { if(!slave.addSignalHandler(m_updateFunctor)) { debugWarning("Could not add update signal functor\n"); } } else { debugWarning("Could not create update signal functor\n"); } // build the initial tree m_Slave = slave; updateTree(); } Container::~Container() { debugOutput( DEBUG_LEVEL_VERBOSE, "Deleting Container on '%s'\n", path().c_str() ); Destroyed(); //send dbus signal if(m_updateFunctor) { if(!m_Slave.remSignalHandler(m_updateFunctor)) { debugWarning("Could not remove update signal functor\n"); } } delete m_updateFunctor; for ( ElementVectorIterator it = m_Children.begin(); it != m_Children.end(); ++it ) { delete (*it); } } void Container::setVerboseLevel( const int32_t & i) { Element::setVerboseLevel(i); for ( ElementVectorIterator it = m_Children.begin(); it != m_Children.end(); ++it ) { (*it)->setVerboseLevel(i); } } int32_t Container::getNbElements( ) { return m_Slave.countElements(); } std::string Container::getElementName( const int32_t& i ) { int nbElements=m_Slave.countElements(); if (igetName(); m_Slave.unlockControl(); return name; } else return ""; } // Util::MutexLockHelper lock(*m_access_lock); // NOTE: call with tree locked void Container::updateTree() { bool something_changed = false; debugOutput( DEBUG_LEVEL_VERBOSE, "Updating tree...\n"); // send a pre update signal PreUpdate(); debugOutput( DEBUG_LEVEL_VERBOSE, "Add handlers for elements...\n"); // add handlers for the slaves that don't have one yet const Control::ElementVector elements = m_Slave.getElementVector(); for ( Control::ConstElementVectorIterator it = elements.begin(); it != elements.end(); ++it ) { Element *e = findElementForControl((*it)); if(e == NULL) { // element not in tree e = createHandler(this, *(*it)); if (e) { e->setVerboseLevel(getDebugLevel()); m_Children.push_back(e); debugOutput( DEBUG_LEVEL_VERBOSE, "Created handler %p for Control::Element %s...\n", e, (*it)->getName().c_str()); something_changed = true; } else { debugWarning("Failed to create handler for Control::Element %s\n", (*it)->getName().c_str()); } } else { // element already present debugOutput( DEBUG_LEVEL_VERBOSE, "Already have handler (%p) for Control::Element %s...\n", e, (*it)->getName().c_str()); } } debugOutput( DEBUG_LEVEL_VERBOSE, "Remove handlers without element...\n"); std::vector to_remove; // remove handlers that don't have a slave anymore for ( ElementVectorIterator it = m_Children.begin(); it != m_Children.end(); ++it ) { Element *e = *it; bool found = false; for ( Control::ConstElementVectorIterator it2 = elements.begin(); it2 != elements.end(); ++it2 ) { if(&(e)->m_Slave == *it2) { found = true; debugOutput( DEBUG_LEVEL_VERBOSE, "Slave for handler %p at %s is present: Control::Element %s...\n", e, e->path().c_str(), (*it)->getName().c_str()); break; } } if (!found) { debugOutput(DEBUG_LEVEL_VERBOSE, "going to remove handler %p on path %s since slave is gone\n", e, e->path().c_str()); // can't remove while iterating to_remove.push_back(e); something_changed = true; } } // do the actual remove while(to_remove.size()) { Element * e = *(to_remove.begin()); removeElement(e); to_remove.erase(to_remove.begin()); } if(something_changed) { debugOutput(DEBUG_LEVEL_VERBOSE, "send dbus signal for path %s since something changed\n", path().c_str()); // send a dbus signal Updated(); } // send a post update signal PostUpdate(); } void Container::removeElement(Element *e) { debugOutput(DEBUG_LEVEL_VERBOSE, "removing handler %p on path %s\n", e, path().c_str()); for ( ElementVectorIterator it = m_Children.begin(); it != m_Children.end(); ++it ) { if(*it == e) { m_Children.erase(it); delete e; return; } } debugError("BUG: Element %p not found!\n", e); } // NOTE: call with access lock held! Element * Container::findElementForControl(Control::Element *e) { for ( ElementVectorIterator it = m_Children.begin(); it != m_Children.end(); ++it ) { if(&(*it)->m_Slave == e) return (*it); } return NULL; } void Container::updated(int new_nb_elements) { debugOutput( DEBUG_LEVEL_VERBOSE, "Got updated signal, new count='%d'\n", new_nb_elements ); // we lock the tree first Lock(); // also lock the slave tree m_Slave.lockControl(); // update our tree updateTree(); // now unlock the slave tree m_Slave.unlockControl(); // and unlock the access Unlock(); } /** * \brief create a correct DBusControl counterpart for a given Control::Element */ Element * Container::createHandler(Element *parent, Control::Element& e) { debugOutput( DEBUG_LEVEL_VERBOSE, "Creating handler for '%s'\n", e.getName().c_str() ); try { if (dynamic_cast(&e) != NULL) { debugOutput( DEBUG_LEVEL_VERBOSE, "Source is a Control::Container\n"); return new Container(conn(), std::string(path()+"/"+e.getName()), parent, *dynamic_cast(&e)); } if (dynamic_cast(&e) != NULL) { debugOutput( DEBUG_LEVEL_VERBOSE, "Source is a Control::Continuous\n"); return new Continuous(conn(), std::string(path()+"/"+e.getName()), parent, *dynamic_cast(&e)); } if (dynamic_cast(&e) != NULL) { debugOutput( DEBUG_LEVEL_VERBOSE, "Source is a Control::Discrete\n"); return new Discrete(conn(), std::string(path()+"/"+e.getName()), parent, *dynamic_cast(&e)); } if (dynamic_cast(&e) != NULL) { debugOutput( DEBUG_LEVEL_VERBOSE, "Source is a Control::Text\n"); return new Text(conn(), std::string(path()+"/"+e.getName()), parent, *dynamic_cast(&e)); } if (dynamic_cast(&e) != NULL) { debugOutput( DEBUG_LEVEL_VERBOSE, "Source is a Control::Register\n"); return new Register(conn(), std::string(path()+"/"+e.getName()), parent, *dynamic_cast(&e)); } // note that we have to check this before checking the Enum, // since Enum is a base class if (dynamic_cast(&e) != NULL) { debugOutput( DEBUG_LEVEL_VERBOSE, "Source is a Control::AttributeEnum\n"); return new AttributeEnum(conn(), std::string(path()+"/"+e.getName()), parent, *dynamic_cast(&e)); } if (dynamic_cast(&e) != NULL) { debugOutput( DEBUG_LEVEL_VERBOSE, "Source is a Control::Enum\n"); return new Enum(conn(), std::string(path()+"/"+e.getName()), parent, *dynamic_cast(&e)); } if (dynamic_cast(&e) != NULL) { debugOutput( DEBUG_LEVEL_VERBOSE, "Source is a Control::Boolean\n"); return new Boolean(conn(), std::string(path()+"/"+e.getName()), parent, *dynamic_cast(&e)); } if (dynamic_cast(&e) != NULL) { debugOutput( DEBUG_LEVEL_VERBOSE, "Source is a ConfigRom\n"); return new ConfigRomX(conn(), std::string(path()+"/"+e.getName()), parent, *dynamic_cast(&e)); } if (dynamic_cast(&e) != NULL) { debugOutput( DEBUG_LEVEL_VERBOSE, "Source is a Control::MatrixMixer\n"); return new MatrixMixer(conn(), std::string(path()+"/"+e.getName()), parent, *dynamic_cast(&e)); } if (dynamic_cast(&e) != NULL) { debugOutput( DEBUG_LEVEL_VERBOSE, "Source is a Control::CrossbarRouter\n"); return new CrossbarRouter(conn(), std::string(path()+"/"+e.getName()), parent, *dynamic_cast(&e)); } debugOutput( DEBUG_LEVEL_VERBOSE, "Source is a Control::Element\n"); return new Element(conn(), std::string(path()+"/"+e.getName()), parent, e); } catch (...) { debugWarning("Could not register %s\n", std::string(path()+"/"+e.getName()).c_str()); if(e.isControlLocked()) { e.unlockControl(); } if(isLocked()) { Unlock(); } return NULL; }; } // --- Continuous Continuous::Continuous( DBus::Connection& connection, std::string p, Element* parent, Control::Continuous &slave) : Element(connection, p, parent, slave) , m_Slave(slave) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Continuous on '%s'\n", path().c_str() ); } double Continuous::setValue( const double& value ) { m_Slave.setValue(value); /* SleepRelativeUsec(1000*500); debugOutput( DEBUG_LEVEL_VERBOSE, "setValue(%lf) => %lf\n", value, m_Slave.getValue() ); return m_Slave.getValue();*/ return value; } double Continuous::getValue( ) { double val = m_Slave.getValue(); debugOutput( DEBUG_LEVEL_VERBOSE, "getValue() => %lf\n", val ); return val; } double Continuous::setValueIdx( const int32_t & idx, const double& value ) { m_Slave.setValue(idx, value); /* SleepRelativeUsec(1000*500); debugOutput( DEBUG_LEVEL_VERBOSE, "setValue(%lf) => %lf\n", value, m_Slave.getValue() ); return m_Slave.getValue();*/ return value; } double Continuous::getValueIdx( const int32_t & idx ) { double val = m_Slave.getValue(idx); debugOutput( DEBUG_LEVEL_VERBOSE, "getValue(%d) => %lf\n", idx, val ); return val; } double Continuous::getMinimum() { double val = m_Slave.getMinimum(); debugOutput( DEBUG_LEVEL_VERBOSE, "getMinimum() => %lf\n", val ); return val; } double Continuous::getMaximum() { double val = m_Slave.getMaximum(); debugOutput( DEBUG_LEVEL_VERBOSE, "getMaximum() => %lf\n", val ); return val; } // --- Discrete Discrete::Discrete( DBus::Connection& connection, std::string p, Element* parent, Control::Discrete &slave) : Element(connection, p, parent, slave) , m_Slave(slave) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Discrete on '%s'\n", path().c_str() ); } int32_t Discrete::setValue( const int32_t& value ) { m_Slave.setValue(value); /* SleepRelativeUsec(1000*500); debugOutput( DEBUG_LEVEL_VERBOSE, "setValue(%d) => %d\n", value, m_Slave.getValue() ); return m_Slave.getValue();*/ return value; } int32_t Discrete::getValue() { int32_t val = m_Slave.getValue(); debugOutput( DEBUG_LEVEL_VERBOSE, "getValue() => %d\n", val ); return val; } int32_t Discrete::setValueIdx( const int32_t& idx, const int32_t& value ) { m_Slave.setValue(idx, value); /* SleepRelativeUsec(1000*500); debugOutput( DEBUG_LEVEL_VERBOSE, "setValue(%d) => %d\n", value, m_Slave.getValue() ); return m_Slave.getValue();*/ return value; } int32_t Discrete::getValueIdx( const int32_t& idx ) { int32_t val = m_Slave.getValue(idx); debugOutput( DEBUG_LEVEL_VERBOSE, "getValue(%d) => %d\n", idx, val ); return val; } // --- Text Text::Text( DBus::Connection& connection, std::string p, Element* parent, Control::Text &slave) : Element(connection, p, parent, slave) , m_Slave(slave) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Text on '%s'\n", path().c_str() ); } std::string Text::setValue( const std::string& value ) { m_Slave.setValue(value); /* SleepRelativeUsec(1000*500); debugOutput( DEBUG_LEVEL_VERBOSE, "setValue(%d) => %d\n", value, m_Slave.getValue() ); return m_Slave.getValue();*/ return value; } std::string Text::getValue() { std::string val = m_Slave.getValue(); debugOutput( DEBUG_LEVEL_VERBOSE, "getValue() => %s\n", val.c_str() ); return val; } // --- Register Register::Register( DBus::Connection& connection, std::string p, Element* parent, Control::Register &slave) : Element(connection, p, parent, slave) , m_Slave(slave) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Register on '%s'\n", path().c_str() ); } uint64_t Register::setValue( const uint64_t& addr, const uint64_t& value ) { m_Slave.setValue(addr, value); /* SleepRelativeUsec(1000*500); debugOutput( DEBUG_LEVEL_VERBOSE, "setValue(%d) => %d\n", value, m_Slave.getValue() ); return m_Slave.getValue();*/ return value; } uint64_t Register::getValue( const uint64_t& addr ) { uint64_t val = m_Slave.getValue(addr); debugOutput( DEBUG_LEVEL_VERBOSE, "getValue(%" PRId64 ") => %" PRId64 "\n", addr, val ); return val; } // --- Enum Enum::Enum( DBus::Connection& connection, std::string p, Element* parent, Control::Enum &slave) : Element(connection, p, parent, slave) , m_Slave(slave) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Enum on '%s'\n", path().c_str() ); } int32_t Enum::select( const int32_t& idx ) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "select(%d)\n", idx ); return m_Slave.select(idx); } int32_t Enum::selected() { int retval = m_Slave.selected(); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "selected() => %d\n", retval ); return retval; } int32_t Enum::count() { int retval = m_Slave.count(); debugOutput( DEBUG_LEVEL_VERBOSE, "count() => %d\n", retval ); return retval; } std::string Enum::getEnumLabel( const int32_t & idx ) { std::string retval = m_Slave.getEnumLabel(idx); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "getEnumLabel(%d) => %s\n", idx, retval.c_str() ); return retval; } bool Enum::devConfigChanged(const int32_t& idx) { return m_Slave.devConfigChanged( idx ); } // --- AttributeEnum AttributeEnum::AttributeEnum( DBus::Connection& connection, std::string p, Element* parent, Control::AttributeEnum &slave) : Element(connection, p, parent, slave) , m_Slave(slave) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Enum on '%s'\n", path().c_str() ); } int32_t AttributeEnum::select( const int32_t& idx ) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "select(%d)\n", idx ); return m_Slave.select(idx); } int32_t AttributeEnum::selected() { int retval = m_Slave.selected(); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "selected() => %d\n", retval ); return retval; } int32_t AttributeEnum::count() { int retval = m_Slave.count(); debugOutput( DEBUG_LEVEL_VERBOSE, "count() => %d\n", retval ); return retval; } int32_t AttributeEnum::attributeCount() { int retval = m_Slave.attributeCount(); debugOutput( DEBUG_LEVEL_VERBOSE, "attributeCount() => %d\n", retval ); return retval; } std::string AttributeEnum::getEnumLabel( const int32_t & idx ) { std::string retval = m_Slave.getEnumLabel(idx); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "getEnumLabel(%d) => %s\n", idx, retval.c_str() ); return retval; } std::string AttributeEnum::getAttributeValue( const int32_t & idx ) { std::string retval = m_Slave.getAttributeValue(idx); debugOutput( DEBUG_LEVEL_VERBOSE, "getAttributeValue(%d) => %s\n", idx, retval.c_str() ); return retval; } std::string AttributeEnum::getAttributeName( const int32_t & idx ) { std::string retval = m_Slave.getAttributeName(idx); debugOutput( DEBUG_LEVEL_VERBOSE, "getAttributeName(%d) => %s\n", idx, retval.c_str() ); return retval; } // --- ConfigRom ConfigRomX::ConfigRomX( DBus::Connection& connection, std::string p, Element* parent, ConfigRom &slave) : Element(connection, p, parent, slave) , m_Slave(slave) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created ConfigRomX on '%s'\n", path().c_str() ); } std::string ConfigRomX::getGUID( ) { return m_Slave.getGuidString(); } std::string ConfigRomX::getVendorName( ) { return m_Slave.getVendorName(); } std::string ConfigRomX::getModelName( ) { return m_Slave.getModelName(); } int32_t ConfigRomX::getVendorId( ) { return m_Slave.getNodeVendorId(); } int32_t ConfigRomX::getModelId( ) { return m_Slave.getModelId(); } int32_t ConfigRomX::getUnitVersion( ) { return m_Slave.getUnitVersion(); } // --- MatrixMixer MatrixMixer::MatrixMixer( DBus::Connection& connection, std::string p, Element* parent, Control::MatrixMixer &slave) : Element(connection, p, parent, slave) , m_Slave(slave) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created MatrixMixer on '%s'\n", path().c_str() ); } int32_t MatrixMixer::getRowCount( ) { return m_Slave.getRowCount(); } int32_t MatrixMixer::getColCount( ) { return m_Slave.getColCount(); } int32_t MatrixMixer::canWrite( const int32_t& row, const int32_t& col) { return m_Slave.canWrite(row,col); } double MatrixMixer::setValue( const int32_t& row, const int32_t& col, const double& val ) { return m_Slave.setValue(row,col,val); } double MatrixMixer::getValue( const int32_t& row, const int32_t& col) { return m_Slave.getValue(row,col); } bool MatrixMixer::hasNames() { return m_Slave.hasNames(); } std::string MatrixMixer::getRowName( const int32_t& row) { return m_Slave.getRowName(row); } std::string MatrixMixer::getColName( const int32_t& col) { return m_Slave.getColName(col); } bool MatrixMixer::setRowName( const int32_t& row, const std::string& name) { return m_Slave.setRowName(row, name); } bool MatrixMixer::setColName( const int32_t& col, const std::string& name) { return m_Slave.setColName(col, name); } bool MatrixMixer::canConnect() { return m_Slave.canConnect(); } std::vector MatrixMixer::availableConnectionsForRow( const int32_t& row) { return m_Slave.availableConnectionsForRow(row); } std::vector MatrixMixer::availableConnectionsForCol( const int32_t& col) { return m_Slave.availableConnectionsForCol(col); } bool MatrixMixer::connectRowTo( const int32_t& row, const std::string& target) { return m_Slave.connectRowTo(row, target); } bool MatrixMixer::connectColTo( const int32_t& col, const std::string& target) { return m_Slave.connectColTo(col, target); } // --- CrossbarRouter CrossbarRouter::CrossbarRouter( DBus::Connection& connection, std::string p, Element* parent, Control::CrossbarRouter &slave) : Element(connection, p, parent, slave) , m_Slave(slave) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created CrossbarRouter on '%s'\n", path().c_str() ); } /*int32_t CrossbarRouter::getSourceIndex(const std::string &name) { return m_Slave.getSourceIndex(name); } int32_t CrossbarRouter::getDestinationIndex(const std::string &name) { return m_Slave.getDestinationIndex(name); }*/ std::vector< std::string > CrossbarRouter::getSourceNames() { return m_Slave.getSourceNames(); } std::vector< std::string > CrossbarRouter::getDestinationNames() { return m_Slave.getDestinationNames(); } std::vector< std::string > CrossbarRouter::getDestinationsForSource(const std::string &idx) { return m_Slave.getDestinationsForSource(idx); } std::string CrossbarRouter::getSourceForDestination(const std::string &idx) { return m_Slave.getSourceForDestination(idx); } bool CrossbarRouter::canConnect(const std::string &source, const std::string &dest) { return m_Slave.canConnect(source, dest); } bool CrossbarRouter::setConnectionState(const std::string &source, const std::string &dest, const bool &enable) { return m_Slave.setConnectionState(source, dest, enable); } bool CrossbarRouter::getConnectionState(const std::string &source, const std::string &dest) { return m_Slave.getConnectionState(source, dest); } bool CrossbarRouter::clearAllConnections() { return m_Slave.clearAllConnections(); } bool CrossbarRouter::hasPeakMetering() { return m_Slave.hasPeakMetering(); } double CrossbarRouter::getPeakValue(const std::string &dest) { return m_Slave.getPeakValue(dest); } std::vector< DBus::Struct > CrossbarRouter::getPeakValues() { std::map peakvalues = m_Slave.getPeakValues(); std::vector< DBus::Struct > ret; for (std::map::iterator it=peakvalues.begin(); it!=peakvalues.end(); ++it) { DBus::Struct tmp; tmp._1 = it->first; tmp._2 = it->second; ret.push_back(tmp); } return ret; /*std::vector< DBus::Struct > out; Control::CrossbarRouter::PeakValues values = m_Slave.getPeakValues(); for ( unsigned int i=0; i tmp; tmp._1 = values[i].destination; tmp._2 = values[i].peakvalue; out.push_back(tmp); } return out;*/ } // --- Boolean Boolean::Boolean( DBus::Connection& connection, std::string p, Element* parent, Control::Boolean &slave) : Element(connection, p, parent, slave) , m_Slave(slave) { debugOutput( DEBUG_LEVEL_VERBOSE, "Created Boolean on '%s'\n", path().c_str() ); } bool Boolean::select( const bool& value ) { debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "select(%d)\n", value ); return m_Slave.select(value); } bool Boolean::selected() { bool retval = m_Slave.selected(); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "selected() => %d\n", retval ); return retval; } std::string Boolean::getBooleanLabel( const bool& value ) { std::string retval = m_Slave.getBooleanLabel(value); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "getBooleanLabel(%d) => %s\n", value, retval.c_str() ); return retval; } } // end of namespace Control libffado-2.4.5/support/dbus/controlserver.h0000644000175000001440000002376414206145246020431 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef CONTROLSERVER_H #define CONTROLSERVER_H #include "debugmodule/debugmodule.h" /* Work around a bug in dbus-c++ 0.9.0 which prevents compilation under gcc7 */ #ifndef DBUS_HAS_RECURSIVE_MUTEX #define DBUS_HAS_RECURSIVE_MUTEX #endif #include #include "controlserver-glue.h" #include "libcontrol/BasicElements.h" #include "libieee1394/configrom.h" #include "libutil/Mutex.h" namespace Control { class MatrixMixer; class CrossbarRouter; }; namespace DBusControl { class Element; class Container; template< typename CalleePtr, typename MemFunPtr > class MemberSignalFunctor0 : public Control::SignalFunctor { public: MemberSignalFunctor0( const CalleePtr& pCallee, MemFunPtr pMemFun, int pSignalId) : Control::SignalFunctor( pSignalId ) , m_pCallee( pCallee ) , m_pMemFun( pMemFun ) {} virtual ~MemberSignalFunctor0() {} virtual void operator() () { ( ( *m_pCallee ).*m_pMemFun )(); } virtual void operator() (int) {} private: CalleePtr m_pCallee; MemFunPtr m_pMemFun; }; template< typename CalleePtr, typename MemFunPtr > class MemberSignalFunctor1 : public Control::SignalFunctor { public: MemberSignalFunctor1( const CalleePtr& pCallee, MemFunPtr pMemFun, int pSignalId) : Control::SignalFunctor( pSignalId ) , m_pCallee( pCallee ) , m_pMemFun( pMemFun ) {} virtual ~MemberSignalFunctor1() {} virtual void operator() () {} virtual void operator() (int value) { ( ( *m_pCallee ).*m_pMemFun )(value); } private: CalleePtr m_pCallee; MemFunPtr m_pMemFun; }; class Element : public org::ffado::Control::Element::Element_adaptor , public DBus::IntrospectableAdaptor , public DBus::ObjectAdaptor { friend class Container; // required to have container access other slave elements public: Element( DBus::Connection& connection, std::string p, Element *, Control::Element &slave ); uint64_t getId( ); std::string getName( ); std::string getLabel( ); std::string getDescription( ); bool canChangeValue( ); void setVerboseLevel( const int32_t &); int32_t getVerboseLevel(); protected: void Lock(); void Unlock(); bool isLocked(); Util::Mutex* getLock(); Element * m_Parent; Control::Element & m_Slave; private: Util::Mutex* m_UpdateLock; protected: DECLARE_DEBUG_MODULE; }; typedef std::vector ElementVector; typedef std::vector::iterator ElementVectorIterator; typedef std::vector::const_iterator ConstElementVectorIterator; class Container : public org::ffado::Control::Element::Container_adaptor , public DBusControl::Element { public: Container( DBus::Connection& connection, std::string p, Element *, Control::Container &slave ); virtual ~Container(); int32_t getNbElements( ); std::string getElementName( const int32_t& ); void updated(int new_nb_elements); void destroyed(); void setVerboseLevel( const int32_t &); private: Element *createHandler(Element *, Control::Element& e); void updateTree(); Element * findElementForControl(Control::Element *e); void removeElement(Element *e); Control::Container & m_Slave; ElementVector m_Children; Control::SignalFunctor * m_updateFunctor; }; class Continuous : public org::ffado::Control::Element::Continuous_adaptor , public Element { public: Continuous( DBus::Connection& connection, std::string p, Element *, Control::Continuous &slave ); double setValue( const double & value ); double getValue( ); double getMinimum( ); double getMaximum( ); double setValueIdx( const int32_t & idx, const double & value ); double getValueIdx( const int32_t & idx ); private: Control::Continuous &m_Slave; }; class Discrete : public org::ffado::Control::Element::Discrete_adaptor , public Element { public: Discrete( DBus::Connection& connection, std::string p, Element *, Control::Discrete &slave ); int32_t setValue( const int32_t & value ); int32_t getValue( ); int32_t setValueIdx( const int32_t & idx, const int32_t & value ); int32_t getValueIdx( const int32_t & idx ); private: Control::Discrete &m_Slave; }; class Text : public org::ffado::Control::Element::Text_adaptor , public Element { public: Text( DBus::Connection& connection, std::string p, Element *, Control::Text &slave ); std::string setValue( const std::string & value ); std::string getValue( ); private: Control::Text &m_Slave; }; class Register : public org::ffado::Control::Element::Register_adaptor , public Element { public: Register( DBus::Connection& connection, std::string p, Element *, Control::Register &slave ); uint64_t setValue( const uint64_t & addr, const uint64_t & value ); uint64_t getValue( const uint64_t & addr ); private: Control::Register &m_Slave; }; class Enum : public org::ffado::Control::Element::Enum_adaptor , public Element { public: Enum( DBus::Connection& connection, std::string p, Element *, Control::Enum &slave ); int32_t select( const int32_t & idx ); int32_t selected( ); int32_t count( ); std::string getEnumLabel( const int32_t & idx ); bool devConfigChanged( const int32_t & ); private: Control::Enum &m_Slave; }; class AttributeEnum : public org::ffado::Control::Element::AttributeEnum_adaptor , public Element { public: AttributeEnum( DBus::Connection& connection, std::string p, Element *, Control::AttributeEnum &slave ); int32_t select( const int32_t & idx ); int32_t selected( ); int32_t count( ); int32_t attributeCount(); std::string getEnumLabel( const int32_t & idx ); std::string getAttributeValue( const int32_t & idx ); std::string getAttributeName( const int32_t & idx ); private: Control::AttributeEnum &m_Slave; }; // FIXME: to change this to a normal ConfigRom class name we have to // put the 1394 config rom class into a separate namespace. class ConfigRomX : public org::ffado::Control::Element::ConfigRomX_adaptor , public Element { public: ConfigRomX( DBus::Connection& connection, std::string p, Element *, ConfigRom &slave ); std::string getGUID( ); std::string getVendorName( ); std::string getModelName( ); int32_t getVendorId( ); int32_t getModelId( ); int32_t getUnitVersion( ); private: ConfigRom &m_Slave; }; class MatrixMixer : public org::ffado::Control::Element::MatrixMixer_adaptor , public Element { public: MatrixMixer( DBus::Connection& connection, std::string p, Element *, Control::MatrixMixer &slave ); int32_t getRowCount( ); int32_t getColCount( ); int32_t canWrite( const int32_t&, const int32_t& ); double setValue( const int32_t&, const int32_t&, const double& ); double getValue( const int32_t&, const int32_t& ); bool hasNames(); std::string getRowName( const int32_t& ); std::string getColName( const int32_t& ); bool setRowName( const int32_t&, const std::string& ); bool setColName( const int32_t&, const std::string& ); bool canConnect(); std::vector availableConnectionsForRow( const int32_t& ); std::vector availableConnectionsForCol( const int32_t& ); bool connectRowTo( const int32_t&, const std::string& ); bool connectColTo( const int32_t&, const std::string& ); private: Control::MatrixMixer &m_Slave; }; class CrossbarRouter : public org::ffado::Control::Element::CrossbarRouter_adaptor , public Element { public: CrossbarRouter( DBus::Connection& connection, std::string p, Element *, Control::CrossbarRouter &slave ); std::vector< std::string > getSourceNames(); std::vector< std::string > getDestinationNames(); std::vector< std::string > getDestinationsForSource(const std::string &); std::string getSourceForDestination(const std::string &); bool canConnect(const std::string &source, const std::string &dest); bool setConnectionState(const std::string &source, const std::string &dest, const bool &enable); bool getConnectionState(const std::string &source, const std::string &dest); bool clearAllConnections(); bool hasPeakMetering(); double getPeakValue(const std::string &dest); std::vector< DBus::Struct > getPeakValues(); private: Control::CrossbarRouter &m_Slave; }; class Boolean : public org::ffado::Control::Element::Boolean_adaptor , public Element { public: Boolean( DBus::Connection& connection, std::string p, Element *, Control::Boolean &slave ); bool select( const bool& value ); bool selected(); std::string getBooleanLabel( const bool& value ); private: Control::Boolean &m_Slave; }; } #endif // CONTROLSERVER_H libffado-2.4.5/support/dbus/ffado-dbus-server.cpp0000644000175000001440000002512214206145246021361 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * This version uses the CPP API */ #include "version.h" #include #include "libffado/ffado.h" #include "debugmodule/debugmodule.h" #include "fbtypes.h" #include "devicemanager.h" #include "ffadodevice.h" /* Work around a bug in dbus-c++ 0.9.0 which prevents compilation under gcc7 */ #ifndef DBUS_HAS_RECURSIVE_MUTEX #define DBUS_HAS_RECURSIVE_MUTEX #endif #include #include "controlserver.h" #include "libcontrol/BasicElements.h" #include "libutil/Functors.h" #include #include #include #include #include #include #include #include #include using namespace std; DECLARE_GLOBAL_DEBUG_MODULE; // DBUS stuff DBus::BusDispatcher dispatcher; DBusControl::Container *container = NULL; DBus::Connection * global_conn; DeviceManager *m_deviceManager = NULL; // signal handler int run=1; sem_t run_sem; static void sighandler (int sig) { run = 0; dispatcher.leave(); sem_post(&run_sem); } // global's const char *argp_program_version = PACKAGE_STRING; const char *argp_program_bug_address = PACKAGE_BUGREPORT; // Program documentation. static char doc[] = "ffado-dbus-server -- expose the mixer features of connected FFADO devices through DBus\n\n" ; // A description of the arguments we accept. static char args_doc[] = ""; struct arguments { short silent; short verbose; int use_cache; int port; int node_id; int node_id_set; const char* args[2]; }; // The options we understand. static struct argp_option options[] = { {"quiet", 'q', 0, 0, "Don't produce any output" }, {"silent", 's', 0, OPTION_ALIAS }, {"verbose", 'v', "level", 0, "Produce verbose output" }, #if ENABLE_DISCOVERY_CACHE {"cache", 'c', "enable", 0, "Use AVC model cache" }, #endif {"node", 'n', "id", 0, "Only expose mixer of a device on a specific node" }, {"port", 'p', "nr", 0, "IEEE1394 Port to use" }, { 0 } }; //------------------------------------------------------------- // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'q': case 's': arguments->silent = 1; break; case 'v': if (arg) { arguments->verbose = strtol( arg, &tail, 0 ); if ( errno ) { debugError( "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'c': if (arg) { arguments->use_cache = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'cache' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'p': if (arg) { arguments->port = strtol( arg, &tail, 0 ); if ( errno ) { debugError( "Could not parse 'port' argument\n" ); return ARGP_ERR_UNKNOWN; } } else { if ( errno ) { debugError("Could not parse 'port' argumen\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'n': if (arg) { arguments->node_id = strtol( arg, &tail, 0 ); if ( errno ) { debugError( "Could not parse 'node' argument\n" ); return ARGP_ERR_UNKNOWN; } arguments->node_id_set=1; } else { if ( errno ) { debugError("Could not parse 'node' argumen\n" ); return ARGP_ERR_UNKNOWN; } } break; case ARGP_KEY_ARG: if (state->arg_num >= 1) { // Too many arguments. argp_usage( state ); } arguments->args[state->arg_num] = arg; break; case ARGP_KEY_END: if (state->arg_num < 0) { // Not enough arguments. argp_usage( state ); } break; default: return ARGP_ERR_UNKNOWN; } return 0; } // Our argp parser. static struct argp argp = { options, parse_opt, args_doc, doc }; int exitfunction( int retval ) { debugOutput( DEBUG_LEVEL_NORMAL, "Debug output flushed...\n" ); flushDebugOutput(); return retval; } void preUpdateHandler() { debugOutput( DEBUG_LEVEL_NORMAL, "got pre-update notification...\n" ); // stop receiving dbus events since the control structure is going to // be changed dispatcher.leave(); } void postUpdateHandler() { debugOutput( DEBUG_LEVEL_NORMAL, "got post-update notification...\n" ); // the signal handlers registered by the elements should have taken // care of updating the control tree // signal that we can start receiving dbus events again sem_post(&run_sem); } int main( int argc, char **argv ) { struct arguments arguments; printf("-----------------------------------------------\n"); printf("FFADO Control DBUS service\n"); printf("Part of the FFADO project -- www.ffado.org\n"); printf("Version: %s\n", PACKAGE_VERSION); printf("(C) 2008-2021, Pieter Palmers and others\n"); printf("This program comes with ABSOLUTELY NO WARRANTY.\n"); printf("-----------------------------------------------\n\n"); // check the library version const char *libversion = ffado_get_version(); const char *progversion = PACKAGE_STRING; if(strcmp(libversion, progversion) != 0) { printf("Library version mismatch. (required: %s, present: %s)\n", progversion, libversion); printf("Please run this application against the exact corresponding library\n"); printf("it was compiled for. The most common cause for this is having more\n"); printf("than one version of libffado installed.\n\n"); return exitfunction(-1); } // Default values. arguments.silent = 0; arguments.verbose = DEBUG_LEVEL_NORMAL; arguments.use_cache = 1; arguments.port = 0; arguments.node_id = 0; arguments.node_id_set = 0; // if we don't specify a node, discover all arguments.args[0] = ""; arguments.args[1] = ""; setDebugLevel(arguments.verbose); // Parse our arguments; every option seen by `parse_opt' will // be reflected in `arguments'. if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { debugError("Could not parse command line\n" ); return exitfunction(-1); } printMessage(" Discovering devices...\n"); m_deviceManager = new DeviceManager(); if ( !m_deviceManager ) { debugError("Could not allocate device manager\n" ); return exitfunction(-1); } if ( !m_deviceManager->initialize() ) { debugError("Could not initialize device manager\n" ); delete m_deviceManager; return exitfunction(-1); } if ( arguments.verbose ) { m_deviceManager->setVerboseLevel(arguments.verbose); } if ( !m_deviceManager->discover(arguments.use_cache) ) { debugError("Could not discover devices\n" ); delete m_deviceManager; return exitfunction(-1); } // add pre-update handler Util::Functor* preupdate_functor = new Util::CallbackFunctor0< void (*)() > ( &preUpdateHandler, false ); if ( !preupdate_functor ) { debugFatal( "Could not create pre-update handler\n" ); return false; } if(!m_deviceManager->registerPreUpdateNotification(preupdate_functor)) { debugError("could not register pre-update notifier"); } // add post-update handler Util::Functor* postupdate_functor = new Util::CallbackFunctor0< void (*)() > ( &postUpdateHandler, false ); if ( !postupdate_functor ) { debugFatal( "Could not create post-update handler\n" ); return false; } if(!m_deviceManager->registerPostUpdateNotification(postupdate_functor)) { debugError("could not register post-update notifier"); } signal (SIGINT, sighandler); signal (SIGTERM, sighandler); DBus::_init_threading(); // set up DBUS stuff DBus::default_dispatcher = &dispatcher; DBus::Connection conn = DBus::Connection::SessionBus(); global_conn = &conn; conn.request_name("org.ffado.Control"); // lock the control tree such that it does not get modified while we build our view m_deviceManager->lockControl(); container = new DBusControl::Container(conn, "/org/ffado/Control/DeviceManager", NULL, *m_deviceManager); // unlock the control tree since the tree is built m_deviceManager->unlockControl(); printMessage("DBUS service running\n"); printMessage("press ctrl-c to stop it & exit\n"); while(run) { debugOutput( DEBUG_LEVEL_NORMAL, "dispatching...\n"); dispatcher.enter(); debugOutput( DEBUG_LEVEL_NORMAL, " dispatcher exited...\n"); sem_wait(&run_sem); debugOutput( DEBUG_LEVEL_NORMAL, " activity handled...\n"); } if(!m_deviceManager->unregisterPreUpdateNotification(preupdate_functor)) { debugError("could not unregister pre update notifier"); } delete preupdate_functor; if(!m_deviceManager->unregisterPostUpdateNotification(postupdate_functor)) { debugError("could not unregister post update notifier"); } delete postupdate_functor; delete container; signal (SIGINT, SIG_DFL); signal (SIGTERM, SIG_DFL); printMessage("server stopped\n"); delete m_deviceManager; return exitfunction(0); } libffado-2.4.5/support/dbus/test-dbus-server.cpp0000644000175000001440000001211714206145246021261 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include "controlserver.h" using namespace std; DECLARE_GLOBAL_DEBUG_MODULE; //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-dbus-server 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-dbus-server -- test server for the DBUS interface"; static char args_doc[] = ""; static struct argp_option options[] = { {"verbose", 'v', 0, 0, "Produce verbose output" }, { 0 } }; struct arguments { arguments() : verbose( false ) { args[0] = 0; } char* args[50]; bool verbose; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; // char* tail; switch (key) { case 'v': arguments->verbose = true; break; case ARGP_KEY_ARG: if (state->arg_num >= 50) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; break; case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// DBus::BusDispatcher dispatcher; void leave( int sig ) { dispatcher.leave(); } static const char* SERVER_NAME = "org.ffado.Control"; void start_server() { // test DBUS stuff DBus::default_dispatcher = &dispatcher; DBus::Connection conn = DBus::Connection::SessionBus(); conn.request_name(SERVER_NAME); // Control::Continuous c0("test0"); // c0.setVerboseLevel(DEBUG_LEVEL_VERBOSE); // DBusControl::Continuous fader(conn, "/org/ffado/Control/Test/Fader", c0); // Control::Container cont(NULL, "container1"); cont.setVerboseLevel(DEBUG_LEVEL_VERBOSE); // // Control::Container cont1("container2"); // cont1.setVerboseLevel(DEBUG_LEVEL_VERBOSE); // // cont.addElement(&cont1); // // Control::Continuous c1("test1"); // c1.setVerboseLevel(DEBUG_LEVEL_VERBOSE); // cont.addElement(&c1); // // Control::Continuous c2("test2"); // c2.setVerboseLevel(DEBUG_LEVEL_VERBOSE); // cont.addElement(&c2); // // Control::Continuous c3("test3"); // c3.setVerboseLevel(DEBUG_LEVEL_VERBOSE); // cont.addElement(&c3); // // Control::Continuous c4("test4"); // c4.setVerboseLevel(DEBUG_LEVEL_VERBOSE); // cont1.addElement(&c4); // // Control::Continuous c5("test5"); // c5.setVerboseLevel(DEBUG_LEVEL_VERBOSE); // cont1.addElement(&c5); // // Control::Discrete d1("test_discr1"); // d1.setVerboseLevel(DEBUG_LEVEL_VERBOSE); // cont1.addElement(&d1); // // Control::Enum e1("test_enum"); // e1.setVerboseLevel(DEBUG_LEVEL_VERBOSE); // cont1.addElement(&e1); // // Control::AttributeEnum a1("test_attrenum"); // a1.setVerboseLevel(DEBUG_LEVEL_VERBOSE); // cont1.addElement(&a1); // // Control::Text t1("test_text"); // t1.setVerboseLevel(DEBUG_LEVEL_VERBOSE); // cont1.addElement(&t1); // Note: create handlers AFTER all children are added // we do dynamic allocation such that we are sure // the container is deleted before the children become invalid DBusControl::Container *container = new DBusControl::Container(conn, "/org/ffado/Control/Test/Container", NULL, cont); dispatcher.enter(); delete container; } int main(int argc, char **argv) { signal(SIGTERM, leave); signal(SIGINT, leave); setDebugLevel(DEBUG_LEVEL_VERBOSE); // arg parsing argp_parse (&argp, argc, argv, 0, 0, &arguments); errno = 0; // char* tail; if (errno) { perror("argument parsing failed:"); return -1; } debugOutput(DEBUG_LEVEL_NORMAL, "DBUS test server\n"); DBus::_init_threading(); start_server(); debugOutput(DEBUG_LEVEL_NORMAL, "bye...\n"); return 0; } libffado-2.4.5/support/dbus/test-dbus.cpp0000644000175000001440000001034014206145246017751 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include "controlclient.h" /* Work around a bug in dbus-c++ 0.9.0 which prevents compilation under gcc7 */ #ifndef DBUS_HAS_RECURSIVE_MUTEX #define DBUS_HAS_RECURSIVE_MUTEX #endif #include static const char* SERVER_NAME = "org.ffado.Control"; static const char* SERVER_PATH = "/org/ffado/Control/Test/Fader"; using namespace std; DECLARE_GLOBAL_DEBUG_MODULE; //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-dbus 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-dbus -- test client for the DBUS interface"; static char args_doc[] = ""; static struct argp_option options[] = { {"verbose", 'v', 0, 0, "Produce verbose output" }, { 0 } }; struct arguments { arguments() : verbose( false ) { args[0] = 0; } char* args[50]; bool verbose; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; // char* tail; switch (key) { case 'v': arguments->verbose = true; break; case ARGP_KEY_ARG: if (state->arg_num >= 50) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; break; case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// DBus::BusDispatcher dispatcher; static const int THREADS = 1; static bool spin = true; void leave( int sig ) { spin = false; dispatcher.leave(); } void* worker_thread( void* ) { DBus::Connection conn = DBus::Connection::SessionBus(); DBusControl::ContinuousClient client(conn, SERVER_PATH, SERVER_NAME); int i=0; while(spin) { try { client.setValue(i++); } catch(...) { cout << "error on setValue()\n"; }; // try { // std::map< DBus::String, DBus::String > info = client.Info(); // cout << info["testset1"] << " - " << info["testset2"] << std::endl; // } catch(...) { // cout << "error on Info()\n"; // }; cout << "* " << i << "*\n"; sleep(1); } return NULL; } void run_client_tests() { DBus::default_dispatcher = &dispatcher; pthread_t thread; pthread_create(&thread, NULL, worker_thread, NULL); dispatcher.enter(); pthread_join(thread, NULL); } int main(int argc, char **argv) { signal(SIGTERM, leave); signal(SIGINT, leave); setDebugLevel(DEBUG_LEVEL_VERBOSE); // arg parsing argp_parse (&argp, argc, argv, 0, 0, &arguments); errno = 0; // char* tail; if (errno) { perror("argument parsing failed:"); return -1; } debugOutput(DEBUG_LEVEL_NORMAL, "DBUS test application\n"); DBus::_init_threading(); run_client_tests(); debugOutput(DEBUG_LEVEL_NORMAL, "bye...\n"); return 0; } libffado-2.4.5/support/dbus/control-interface.xml0000644000175000001440000002615212227312021021467 0ustar jwoitheusers libffado-2.4.5/support/dbus/.gitignore0000644000175000001440000000016112132617070017316 0ustar jwoitheuserscontrolclient-glue.h controlserver-glue.h ffado-dbus-server org.ffado.Control.service test-dbus test-dbus-server libffado-2.4.5/support/dbus/ffado-dbus-server.10000644000175000001440000000241611734344527020746 0ustar jwoitheusers.TH FFADO-DBUS-SERVER 1 27-Mar-2012 "ffado-dbus-server" .SH NAME dbus \- expose the mixer features of connected FFADO devices through DBus .SH SYNOPSIS .BI "ffado-dbus-server [OPTION...]" .sp .SH DESCRIPTION .B ffado-dbus-server exposes the mixer and control features of connected FFADO devices though a DBUS interface. The extent of support for controls on a given device depends entirely on the status of that device's FFADO driver. .PP By using DBus it is possible for control applications to be developed independently of FFADO. The DBus interface provided by .B ffado-dbus-server is used by .B ffado-mixer but there is no reason other implementations could not be written. .SH OPTIONS .TP .B "\-?, \-\-help, \-\-usage" Show brief usage information and exit .TP .B "\-V, \-\-version" Print the program version and exit .TP .B "\-q, \-q, \-\-quiet, \-\-silent" Don't produce any output .TP .B "\-n, \-\-node=ID" Only expose the mixer of a device on a specific node with an ID of .I ID .TP .B "\-p, \-\-port=NUM" Specify use of IEEE1394 port .I NUM .TP .B "\-v, \-\-verbose=LEVEL Produce verbose output. The higher the .I LEVEL the more verbose the messages. The some verbose messages may only be available of FFADO was compiled with debugging enabled. .SH SEE ALSO .BR ffado-mixer (1) libffado-2.4.5/support/dbus/org.ffado.Control.service.in0000644000175000001440000000010611114614546022604 0ustar jwoitheusers[D-BUS Service] Name=org.ffado.Control Exec=$BINDIR/ffado-dbus-server libffado-2.4.5/support/firmware/0000755000175000001440000000000014206145613016212 5ustar jwoitheuserslibffado-2.4.5/support/firmware/SConscript0000644000175000001440000000531214206145246020227 0ustar jwoitheusers# # Copyright (C) 2007-2008 Arnold Krille # Copyright (C) 2007-2008 Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os Import( 'env' ) env = env.Clone() env.AppendUnique( CPPPATH=["#/", "#/src"] ) if not env.GetOption( "clean" ): env.MergeFlags( "-lrt -lpthread" ) env.MergeFlags( env['LIBRAW1394_FLAGS'].decode() ) env.MergeFlags( env['LIBIEC61883_FLAGS'].decode() ) if not env['SERIALIZE_USE_EXPAT']: if 'LIBXML30_FLAGS' in env : env.MergeFlags( env['LIBXML30_FLAGS'].decode() ) if not('LIBXML30_FLAGS' in env) : env.MergeFlags( env['LIBXML26_FLAGS'].decode() ) else: env.PrependUnique( LIBS=["expat"] ) static_env = env.Clone() apps = { } installapps = [] manpages = [] env.PrependUnique( LIBPATH=env['build_base']+"src" ) env.PrependUnique( LIBS="ffado" ) if env['ENABLE_BEBOB']: apps["ffado-bridgeco-downloader"] = "downloader.cpp bridgeco-downloader.cpp" installapps += [ "ffado-bridgeco-downloader" ] manpages += [ "ffado-bridgeco-downloader.1" ] if env['ENABLE_FIREWORKS']: apps["ffado-fireworks-downloader"] = "downloader.cpp fireworks-downloader.cpp" installapps += [ "ffado-fireworks-downloader" ] manpages += [ "ffado-fireworks-downloader.1" ] if env['ENABLE_DICE']: apps["ffado-dice-firmware"] = "dice-firmware-utility.cpp" installapps += [ "ffado-dice-firmware" ] manpages += [ "ffado-dice-firmware.1" ] for app in apps.keys(): env.Program( target=app, source = env.Split( apps[app] ) ) if app.find( "test" ) == -1: env.Install( "$bindir", app ) for manpage in manpages: section = manpage.split(".")[1] dest = os.path.join("$mandir", "man"+section, manpage) env.InstallAs(source=manpage, target=dest) # static versions if static_env['BUILD_STATIC_TOOLS']: static_env.Append(LIBS=File('#/src/libffado.a')) for app in apps.keys(): static_app = app + "-static" static_env.Program( target=static_app, source = static_env.Split( apps[app] ) ) libffado-2.4.5/support/firmware/bridgeco-downloader.cpp0000644000175000001440000001545114206145246022640 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "downloader.h" #include "src/bebob/bebob_dl_mgr.h" #include "src/bebob/bebob_dl_bcd.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "version.h" #include #include #include #define MAGIC_THAT_SAYS_I_KNOW_WHAT_IM_DOING 0x001807198000LL using namespace std; //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "bridgeco-downloader 0.2"; const char *argp_program_bug_address = ""; const char *doc = "bridgeco-downloader -- firmware downloader application for BridgeCo devices\n\n" "OPERATION: GUID display\n" " GUID setguid NEW_GUID\n" " GUID firmware FILE\n" " GUID cne FILE\n" " GUID bcd FILE\n"; static struct argp_option _options[] = { {"verbose", 'v', "level", 0, "Produce verbose output" }, {"port", 'p', "PORT", 0, "Set port" }, {"force", 'f', 0, 0, "Force firmware download" }, {"noboot", 'b', 0, 0, "Do no start bootloader (bootloader is already running)" }, {"magic", 'm', "MAGIC", 0, "A magic number you have to obtain before this code will work." "Specifying it means that you accept the risks that come with this tool."}, { 0 } }; struct argp_option* options = _options; int main( int argc, char** argv ) { printf("-----------------------------------------------\n"); printf("BridgeCo BeBoB platform firmware downloader\n"); printf("Part of the FFADO project -- www.ffado.org\n"); printf("Version: %s\n", PACKAGE_VERSION); printf("(C) 2008, Daniel Wagner, Pieter Palmers\n"); printf("This program comes with ABSOLUTELY NO WARRANTY.\n"); printf("-----------------------------------------------\n\n"); // arg parsing argp_parse (argp, argc, argv, 0, 0, args); if (args->nargs < 2) { printDeviceList(); exit(0); } errno = 0; char* tail; int node_id = -1; fb_octlet_t guid = strtoll(args->args[0], &tail, 0); if (errno) { perror("argument parsing failed:"); return -1; } if(args->magic != MAGIC_THAT_SAYS_I_KNOW_WHAT_IM_DOING) { printf("Magic number not correct. Please specify the correct magic using the '-m' option.\n"); printf("Manipulating firmware can cause your device to magically stop working (a.k.a. 'bricking').\n"); printf("Specifying the magic number indicates that you accept the risks involved\n"); printf("with using this tool. The magic number can be found in the source code.\n\n"); return -1; } else { printf("YOU HAVE SPECIFIED THE CORRECT MAGIC NUMBER.\n"); printf("HENCE YOU ACCEPT THE RISKS INVOLVED.\n"); } Ieee1394Service service; if ( !service.initialize( args->port ) ) { cerr << "Could not initialize IEEE 1394 service" << endl; return -1; } service.setVerboseLevel( args->verbose ); for (int i = 0; i < service.getNodeCount(); i++) { ConfigRom configRom(service, i); configRom.initialize(); if (configRom.getGuid() == guid) node_id = configRom.getNodeId(); } if (node_id < 0) { cerr << "Could not find device with matching GUID" << endl; return -1; } service.setVerboseLevel( args->verbose ); BeBoB::BootloaderManager blMgr( service, node_id ); if ( args->force == 1 ) { blMgr.setForceOperations( true ); } blMgr.getConfigRom()->printConfigRom(); blMgr.printInfoRegisters(); if ( strcmp( args->args[1], "setguid" ) == 0 ) { if (!args->args[2] ) { cerr << "guid argument is missing" << endl; return -1; } char* tail; fb_octlet_t guid = strtoll(args->args[2], &tail, 0 ); if ( !blMgr.programGUID( guid ) ) { cerr << "Failed to set GUID" << endl; return -1; } else { cout << "new GUID programmed" << endl; } } else if ( strcmp( args->args[1], "firmware" ) == 0 ) { if (!args->args[2] ) { cerr << "FILE argument is missing" << endl; return -1; } std::string str( args->args[2] ); blMgr.setStartBootloader( args->no_bootloader_restart != 1 ); if ( !blMgr.downloadFirmware( str ) ) { cerr << "Failed to download firmware" << endl; return -1; } else { cout << "Firmware download was successful" << endl; cout << "Please reboot the device by removing the power and FireWire connections." << endl; } } else if ( strcmp( args->args[1], "cne" ) == 0 ) { if (!args->args[2] ) { cerr << "FILE argument is missing" << endl; return -1; } std::string str( args->args[2] ); if ( !blMgr.downloadCnE( str ) ) { cerr << "Failed to download CnE" << endl; return -1; } else { cout << "CnE download was successful" << endl; cout << "Please reboot the device by removing the power and FireWire connections." << endl; } } else if ( strcmp( args->args[1], "display" ) == 0 ) { // nothing to do } else if ( strcmp( args->args[1], "bcd" ) == 0 ) { if ( !args->args[2] ) { cerr << "FILE arguments is missing" << endl; return -1; } BeBoB::BCD* bcd = new BeBoB::BCD( args->args[2] ); if ( !bcd ) { cerr << "Could no open file " << args->args[2] << endl; return -1; } if ( !bcd->parse() ) { cerr << "Could not parse file " << args->args[2] << endl; return -1; } bcd->displayInfo(); delete bcd; } else { cout << "Unknown operation" << endl; } return 0; } libffado-2.4.5/support/firmware/dice-firmware-utility.cpp0000644000175000001440000001734014206145246023144 0ustar jwoitheusers/* * Copyright (C) 2012 by Peter Hinkel * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Implementation of the DICE firmware loader specification based upon * TCAT public available document v3.2.0.0 (2008-06-20) * */ #include "config.h" #include #include #include "debugmodule/debugmodule.h" #include "devicemanager.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/Configuration.h" #include "libcontrol/MatrixMixer.h" #include "libcontrol/CrossbarRouter.h" #include "dice/dice_avdevice.h" #include "dice/dice_firmware_loader.h" #include "dice/dice_eap.h" using namespace Dice; #include #include #include #include #include int run; static void sighandler(int sig) { run = 0; } using namespace std; using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 1000 //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "dice-firmware-utility v1.0"; const char *argp_program_bug_address = ""; static char doc[] = "dice-firmware-utility -- utility for flashing DICE device firmware\n\n" "Part of the FFADO project -- www.ffado.org\n" "Version: 1.0\n" "(C) 2012, Peter Hinkel\n" "This program comes with absolutely no warranty.\n\n" "OPERATIONS:\n" "test\n" " POKE = 1 (write addr), PEEK = 2 (read addr)\n" "verbose\n" " LEVEL = 0..8, 4 (default), disabled (-1)\n\n" "example: dice-firmware-utility -p 0 -f firmware.bin"; static char args_doc[] = ""; static struct argp_option options[] = { {"application", 'a', NULL, 0, "Show dice image application information"}, {"delete", 'd', "IMAGENAME", 0, "Delete image by name"}, {"dump", 'e', "FILENAME", 0, "Extract DICE flash into file"}, {"flash", 'f', "FILENAME", 0, "Write firmware file to DICE device"}, {"image-info", 'i', NULL, 0, "Show detailed information about each image within firmware"}, {"flash-info", 'm', NULL, 0, "Show information about flash memory"}, {"dice-info", 's', NULL, 0, "Show dice image vendor and product information"}, {"test", 't', "OPERATION", 0, "Test and Debug operation"}, {"node", 'n', "NODE", 0, "Set node"}, {"port", 'p', "PORT", 0, "Set port"}, {"verbose", 'v', "LEVEL", 0, "Verbosity for DEBUG output"}, {0} }; struct arguments { arguments() : nargs(0) , application(false) , del(false) , dump(false) , flash(false) , image_info(false) , flash_info(false) , dice_info(false) , test(0) , filename(NULL) , imgname(NULL) , node(-1) , port(-1) , verbose(4/*-1*/) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; bool application; bool del; bool dump; bool flash; bool image_info; bool flash_info; bool dice_info; int test; char* filename; char* imgname; int node; int port; int verbose; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'a': arguments->application = true; break; case 'd': arguments->del = true; arguments->imgname = arg; break; case 'e': arguments->dump = true; arguments->filename = arg; break; case 'f': arguments->flash = true; arguments->filename = arg; break; case 'i': arguments->image_info = true; break; case 'm': arguments->flash_info = true; break; case 's': arguments->dice_info = true; break; case 't': arguments->test = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'v': arguments->verbose = strtol(arg, &tail, 0); break; case 'p': arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'n': arguments->node = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: if(arguments->nargs<0) { printf("not enough arguments\n"); return -1; } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { if (argc<=1) { cout << "Try `" << argv[0] << " --help' or `" << argv[0] << " --usage' for more information." << endl; return -1; } run=1; signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { printMessage("Could not parse command line\n" ); exit(-1); } errno = 0; DeviceManager *m_deviceManager = new DeviceManager(); if ( !m_deviceManager ) { printMessage("Could not allocate device manager\n" ); return -1; } if ( arguments.verbose ) { m_deviceManager->setVerboseLevel(arguments.verbose); } if ( !m_deviceManager->initialize() ) { printMessage("Could not initialize device manager\n" ); delete m_deviceManager; return -1; } char s[1024]; if(arguments.node > -1) { snprintf(s, 1024, "hw:%d,%d", arguments.port, arguments.node); if ( !m_deviceManager->addSpecString(s) ) { printMessage("Could not add spec string %s to device manager\n", s ); delete m_deviceManager; return -1; } } else { snprintf(s, 1024, "hw:%d", arguments.port); if ( !m_deviceManager->addSpecString(s) ) { printMessage("Could not add spec string %s to device manager\n", s ); delete m_deviceManager; return -1; } } if ( !m_deviceManager->discover(false) ) { printMessage("Could not discover devices\n" ); delete m_deviceManager; return -1; } if(m_deviceManager->getAvDeviceCount() == 0) { printMessage("No devices found\n"); delete m_deviceManager; return -1; } Dice::Device* avDevice = dynamic_cast(m_deviceManager->getAvDeviceByIndex(0)); if(avDevice == NULL) { printMessage("Device is not a DICE device\n" ); delete m_deviceManager; return -1; } if (argc<=5) { if (arguments.application) { avDevice->showAppInfoFL(); } if (arguments.del) { avDevice->deleteImgFL(arguments.imgname); } if (arguments.dump) { avDevice->dumpFirmwareFL(arguments.filename); } if (arguments.flash) { avDevice->flashDiceFL(arguments.filename); } if (arguments.image_info) { avDevice->showImgInfoFL(); } if (arguments.flash_info) { avDevice->showFlashInfoFL(); } if (arguments.dice_info) { avDevice->showDiceInfoFL(); } if (arguments.test) { avDevice->testDiceFL(arguments.test); } } else { printMessage("Too many arguments..."); } // cleanup delete m_deviceManager; return 0; } libffado-2.4.5/support/firmware/downloader.cpp0000644000175000001440000000753414206145246021067 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "downloader.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "debugmodule/debugmodule.h" #include #include #include using namespace std; DECLARE_GLOBAL_DEBUG_MODULE; static char args_doc[] = "OPERATION [ARGUMENTS]"; static struct argp _argp = { options, parse_opt, args_doc, doc }; struct argp* argp = &_argp; static struct arguments _args = { {0}, }; struct arguments* args = &_args; error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; switch (key) { case 'v': if (arg) { arguments->verbose = strtol( arg, &tail, 0 ); if ( errno ) { debugError( "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'p': errno = 0; arguments->port = strtol(arg, &tail, 0); if (errno) { debugError("argument parsing failed: %s\n", strerror(errno)); return errno; } break; case 'g': errno = 0; arguments->guid = strtoll(arg, &tail, 0); if (errno) { debugError("argument parsing failed: %s\n", strerror(errno)); return errno; } break; case 'm': errno = 0; arguments->magic = strtoll(arg, &tail, 0); if (errno) { debugError("argument parsing failed: %s\n", strerror(errno)); return errno; } break; case 'f': arguments->force = 1; break; case 'b': arguments->no_bootloader_restart = 1; break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_NB_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs = state->arg_num; break; case ARGP_KEY_END: arguments->nargs = state->arg_num; break; default: return ARGP_ERR_UNKNOWN; } return 0; } void printDeviceList() { Ieee1394Service service; // switch off all messages since they mess up the list service.setVerboseLevel(0); if ( !service.initialize( args->port ) ) { cerr << "Could not initialize IEEE 1394 service" << endl; exit(-1); } cout << "Node id GUID Vendor - Model" << endl; for (int i = 0; i < service.getNodeCount(); i++) { ConfigRom crom(service, i); if (!crom.initialize()) break; cout << i << " " << " 0x" << crom.getGuidString() << " '" << crom.getVendorName() << "' - '" << crom.getModelName() << "'" << endl; } } libffado-2.4.5/support/firmware/downloader.h0000644000175000001440000000301314206145246020520 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef downloader_h #define downloader_h #include #include #define MAX_NB_ARGS 5 /* ------------------------------------- */ /* has to be provided by the application */ extern const char *doc; extern struct argp_option* options; /* ------------------------------------- */ struct arguments { char* args[MAX_NB_ARGS]; int nargs; short verbose; int port; int force; int no_bootloader_restart; uint64_t guid; uint64_t magic; }; extern struct arguments* args; extern struct argp* argp; error_t parse_opt( int key, char* arg, struct argp_state* state ); void printDeviceList(); #endif /* downloader_h */ libffado-2.4.5/support/firmware/ffado-fireworks-downloader.10000644000175000001440000000427514206145246023532 0ustar jwoitheusers.TH FFADO-FIREWORKS-DOWNLOADER 1 27-Mar-2012 "ffado-fireworks-downloader" .SH NAME ffado-fireworks-downloader \- firmware download application for Echo Fireworks devices .SH SYNOPSIS .BI "ffado-fireworks-downloader [OPTION...] OPERATION .sp .SH DESCRIPTION .B ffado-bridgeco-downloader permits firmware updates to be made to Echo Fireworks devices under Linux. .I OPERATION describes the action to be carried out. An .I OPERATION comprises one of the following keywords and any arguments required by that keyword. .TP .B list List devices on the FireWire bus. .TP .B display Display information about a device and its firmware. .TP .B upload FILE Upload the firmware contained in .I FILE to the device .TP .B download FILE START_ADDR LEN Download the flash contents from the device. .I LEN bytes will be downloaded starting at .I START_ADDR and written to the file .I FILE .TP .B verify FILE Verify that the firmware contained in the device corresponds to the one in .I FILE .TP .B session_display Show information about the session on the device. .TP .B session_info FILE Show information about the device session stored in .I FILE .TP .B session_download FILE Download the session content from the device into .I FILE .TP .B session_upload FILE Upload the session from .I FILE to the device. .sp .SH OPTIONS .TP .B "\-h, \-\-help" Display brief usage information and exit .TP .B "\-p, \-\-port=NUM" Specify use of IEEE1394 port .I NUM .TP .B "\-v, \-\-verbose=LEVEL" Produce verbose output. The higher the .I LEVEL the more verbose the messages. The some verbose messages may only be available of FFADO was compiled with debugging enabled. .TP .B "\-g, \-\-guid=GUID" Specify the GUID of the device to interact with. If this is not specified an attempt will be made to automatically detect a suitable device. .TP .B "\-m, \-\-magic=MAGIC" A magic number you have to obtain before this program will work. Specifying it on the command line means that you accept the risks which come with this tool. The magic number can be obtained in the source code. .sp .SH NOTES Manipulating firmware can cause your device to stop working. The FFADO project accepts no responsibility if this occurs as a result of using this tool. libffado-2.4.5/support/firmware/fireworks-downloader.cpp0000644000175000001440000004024514206145246023074 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "fbtypes.h" #include "downloader.h" #include "config.h" #include "src/fireworks/fireworks_device.h" #include "src/fireworks/fireworks_firmware.h" #include "src/fireworks/fireworks_session_block.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/Configuration.h" #include "debugmodule/debugmodule.h" #include "devicemanager.h" #include "version.h" #include #include #include #include #define MAGIC_THAT_SAYS_I_KNOW_WHAT_IM_DOING 0x001807198000LL using namespace FireWorks; DECLARE_GLOBAL_DEBUG_MODULE; //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "fireworks-downloader 0.3"; const char *argp_program_bug_address = ""; const char *doc = "fireworks-downloader -- firmware downloader application for ECHO Fireworks devices\n\n" "OPERATIONS:\n" " list\n" " Lists devices on the bus\n" " display\n" " Display information about a device and it's firmware\n" " info FILE\n" " Display information about the firmware contained in FILE\n" " upload FILE\n" " Upload the firmware contained in FILE to the device\n" " download FILE START_ADDR LEN\n" " Download the flash contents from the device to FILE\n" " Starts at address START_ADDR and reads LEN quadlets\n" " verify FILE\n" " Verify that the firmware contained in the device corresponds\n" " to the one contained in FILE\n" " session_display\n" " show information about the session on the device\n" " session_info FILE\n" " show information about the session in FILE\n" " session_download FILE\n" " Download the session content from the device to FILE\n" " session_upload FILE\n" " Upload the session from FILE to the device\n" ; static struct argp_option _options[] = { {"verbose", 'v', "LEVEL", 0, "Produce verbose output (set level 0-10)" }, {"guid", 'g', "GUID", 0, "GUID of the target device" }, {"port", 'p', "PORT", 0, "Port to use" }, {"magic", 'm', "MAGIC", 0, "A magic number you have to obtain before this code will work." "Specifying it means that you accept the risks that come with this tool."}, { 0 } }; struct argp_option* options = _options; int main( int argc, char** argv ) { using namespace std; printf("-----------------------------------------------\n"); printf("ECHO FireWorks platform firmware downloader\n"); printf("Part of the FFADO project -- www.ffado.org\n"); printf("Version: %s\n", PACKAGE_VERSION); printf("(C) 2008, Pieter Palmers\n"); printf("This program comes with ABSOLUTELY NO WARRANTY.\n"); printf("-----------------------------------------------\n\n"); memset(args, 0, sizeof(*args)); args->guid = 0xFFFFFFFFFFFFFFFFLL; argp_parse (argp, argc, argv, 0, 0, args); setDebugLevel(args->verbose); errno = 0; int node_id = -1; fb_octlet_t guid = args->guid; if(args->nargs < 1) { argp_help(argp, stderr, ARGP_HELP_USAGE | ARGP_HELP_LONG | ARGP_HELP_DOC, argv[0]); exit(-1); } if(args->magic != MAGIC_THAT_SAYS_I_KNOW_WHAT_IM_DOING) { printf("Magic number not correct. Please specify the correct magic using the '-m' option.\n"); printf("Manipulating firmware can cause your device to magically stop working (a.k.a. 'bricking').\n"); printf("Specifying the magic number indicates that you accept the risks involved\n"); printf("with using this tool. The magic number can be found in the source code.\n\n"); return -1; } else { printf("YOU HAVE SPECIFIED THE CORRECT MAGIC NUMBER.\n"); printf("HENCE YOU ACCEPT THE RISKS INVOLVED.\n"); } // first do the operations for which we don't need a device if ( strcmp( args->args[0], "info" ) == 0 ) { if (!args->args[1] ) { printMessage("FILE argument is missing\n"); return -1; } std::string str( args->args[1] ); // load the file Firmware f = Firmware(); f.setVerboseLevel( args->verbose ); if (!f.loadFile(str)) { printMessage("Could not load firmware\n"); return -1; } f.show(); return 0; } else if ( strcmp( args->args[0], "dumpinfo" ) == 0 ) { if (!args->args[1] ) { printMessage("FILE argument is missing\n"); return -1; } std::string str( args->args[1] ); // load the file Firmware f = Firmware(); f.setVerboseLevel( args->verbose ); if (!f.loadFile(str)) { printMessage("Could not load firmware\n"); return -1; } f.show(); f.dumpData(); return 0; } else if ( strcmp( args->args[0], "session_info" ) == 0 ) { if (!args->args[1] ) { printMessage("FILE argument is missing\n"); return -1; } std::string str( args->args[1] ); // load the file Session s = Session(); s.setVerboseLevel( args->verbose ); if (!s.loadFromFile(str)) { printMessage("Could not load session\n"); return -1; } s.show(); return 0; } else if ( strcmp( args->args[0], "list" ) == 0 ) { printDeviceList(); exit(0); } // we need a device, so find the specified device if (guid == 0xFFFFFFFFFFFFFFFFLL) { printMessage("No GUID specified\n"); exit(-1); } Ieee1394Service service; if ( !service.initialize( args->port ) ) { printMessage("Could not initialize IEEE 1394 service\n"); return -1; } service.setVerboseLevel( args->verbose ); for (int i = 0; i < service.getNodeCount(); i++) { ConfigRom configRom(service, i); configRom.initialize(); if (configRom.getGuid() == guid) { node_id = configRom.getNodeId(); break; } } if (node_id < 0) { printMessage("Could not find device with GUID 0x%016" PRIX64 "\n", guid); return -1; } ConfigRom *configRom = new ConfigRom(service, node_id ); if (configRom == NULL) { printMessage("Could not create ConfigRom\n"); return -1; } configRom->setVerboseLevel( args->verbose ); if ( !configRom->initialize() ) { printMessage( "Could not read config rom from device (node id %d).\n", node_id ); delete configRom; return -1; } Util::Configuration c; c.openFile( USER_CONFIG_FILE, Util::Configuration::eFM_ReadOnly ); c.openFile( SYSTEM_CONFIG_FILE, Util::Configuration::eFM_ReadOnly ); if ( !FireWorks::Device::probe(c, *configRom) ) { printMessage( "Device with node id %d is not an ECHO FireWorks device.\n", node_id ); delete configRom; return -1; } DeviceManager d = DeviceManager(); Device *dev = new Device(d, ffado_smartptr(configRom) ); if (dev == NULL) { printMessage("Could not create FireWorks::Device\n"); delete configRom; return -1; } dev->setVerboseLevel(args->verbose); if ( strcmp( args->args[0], "display" ) == 0 ) { // nothing to do if (args->verbose < DEBUG_LEVEL_INFO) { dev->setVerboseLevel(DEBUG_LEVEL_INFO); } dev->showDevice(); } else if (strcmp( args->args[0], "download" ) == 0) { if (args->nargs < 3) { printMessage("Address range not specified\n"); delete dev; return -1; } errno = 0; uint32_t start_addr = strtol(args->args[2], NULL, 0); if (errno) { printMessage("start address parsing failed: %s\n", strerror(errno)); delete dev; return errno; } uint32_t len = strtol(args->args[3], NULL, 0); if (errno) { printMessage("length parsing failed: %s\n", strerror(errno)); delete dev; return errno; } // create the firmware util class FirmwareUtil util = FirmwareUtil(*dev); util.setVerboseLevel( args->verbose ); Firmware f = util.getFirmwareFromDevice(start_addr, len); f.setVerboseLevel( args->verbose ); f.show(); f.dumpData(); printMessage("Saving to file not yet supported.\n"); } else if (strcmp( args->args[0], "verify" ) == 0) { if (!args->args[1] ) { printMessage("FILE argument is missing\n"); delete dev; return -1; } std::string str( args->args[1] ); printMessage("Verifying device versus file: %s\n", str.c_str()); printMessage(" loading file...\n"); // load the file Firmware ref = Firmware(); ref.setVerboseLevel( args->verbose ); if (!ref.loadFile(str)) { printMessage("Could not load firmware from file\n"); delete dev; return -1; } // get the flash position from the loaded file uint32_t start_addr = ref.getAddress(); uint32_t len = ref.getLength(); // create the firmware util class FirmwareUtil util = FirmwareUtil(*dev); util.setVerboseLevel( args->verbose ); printMessage(" reading device...\n"); // read the corresponding part of the device Firmware f = util.getFirmwareFromDevice(start_addr, len); f.setVerboseLevel( args->verbose ); f.show(); printMessage(" comparing...\n"); // compare the two images if(!(f == ref)) { printMessage(" => Verify failed. Device content not the same as file content.\n"); delete dev; return -1; } else { printMessage(" => Verify successful. Device content identical to file content.\n"); } } else if (strcmp( args->args[0], "upload" ) == 0) { if (!args->args[1] ) { printMessage("FILE argument is missing\n"); delete dev; return -1; } std::string str( args->args[1] ); // create the firmware util class FirmwareUtil util = FirmwareUtil(*dev); util.setVerboseLevel( args->verbose ); printMessage("Uploading %s to device\n", str.c_str()); printMessage(" loading file...\n"); // load the file Firmware ref = Firmware(); ref.setVerboseLevel( args->verbose ); if (!ref.loadFile(str)) { printMessage(" Could not load firmware from file\n"); delete dev; return -1; } printMessage(" checking file...\n"); if(!ref.isValid()) { printMessage(" Firmware not valid\n"); delete dev; return -1; } if(!util.isValidForDevice(ref)) { printMessage(" Firmware not valid for this device\n"); delete dev; return -1; } printMessage(" seems to be valid firmware for this device...\n"); // get the flash position from the loaded file uint32_t start_addr = ref.getAddress(); uint32_t len = ref.getLength(); printMessage(" lock flash...\n"); if (!dev->lockFlash(true)) { printMessage(" Could not lock flash\n"); delete dev; return -1; } printMessage(" erasing memory...\n"); if (!util.eraseBlocks(start_addr, len)) { printMessage(" Could not erase memory\n"); delete dev; return -1; } printMessage(" uploading to device...\n"); if (!util.writeFirmwareToDevice(ref)) { printMessage(" Could not write firmware to device\n"); delete dev; return -1; } printMessage(" unlock flash...\n"); if (!dev->lockFlash(false)) { printMessage(" Could not unlock flash\n"); delete dev; return -1; } // now verify printMessage(" verify...\n"); printMessage(" reading device...\n"); // read the corresponding part of the device Firmware f = util.getFirmwareFromDevice(start_addr, len); f.setVerboseLevel( args->verbose ); f.show(); printMessage(" comparing...\n"); // compare the two images if(!(f == ref)) { printMessage(" => Verify failed. Firmware upload failed.\n"); delete dev; return -1; } else { printMessage(" => Verify successful. Firmware upload successful.\n"); } } else if ( strcmp( args->args[0], "session_display" ) == 0 ) { // load the session Session s = Session(); s.setVerboseLevel( args->verbose ); if (!s.loadFromDevice(*dev)) { printMessage("Could not load session\n"); return -1; } s.show(); } else if ( strcmp( args->args[0], "session_download" ) == 0 ) { if (!args->args[1] ) { printMessage("FILE argument is missing\n"); delete dev; return -1; } std::string str( args->args[1] ); printMessage("Downloading session to file: %s\n", str.c_str()); printMessage(" loading session...\n"); // load the session Session s = Session(); s.setVerboseLevel( args->verbose ); if (!s.loadFromDevice(*dev)) { printMessage("Could not load session from device\n"); return -1; } printMessage(" saving session...\n"); if (!s.saveToFile(str)) { printMessage("Could not save session to file\n"); return -1; } } else if ( strcmp( args->args[0], "session_upload" ) == 0 ) { if (!args->args[1] ) { printMessage("FILE argument is missing\n"); delete dev; return -1; } std::string str( args->args[1] ); printMessage("Uploading session from file: %s\n", str.c_str()); printMessage(" loading session...\n"); // load the session Session s = Session(); s.setVerboseLevel( args->verbose ); if (!s.loadFromFile(str)) { printMessage("Could not load session from file\n"); return -1; } printMessage(" saving session...\n"); if (!s.saveToDevice(*dev)) { printMessage("Could not save session to device\n"); return -1; } } else { printMessage("Unknown operation\n"); } delete dev; return 0; } libffado-2.4.5/support/firmware/.gitignore0000644000175000001440000000011112132617070020170 0ustar jwoitheusersffado-bridgeco-downloader ffado-dice-firmware ffado-fireworks-downloader libffado-2.4.5/support/firmware/ffado-bridgeco-downloader.10000644000175000001440000000370111734344527023274 0ustar jwoitheusers.TH FFADO-BRIDGECO-DOWNLOADER 1 27-Mar-2012 "ffado-bridgeco-downloader" .SH NAME ffado-bridgeco-downloader \- firmware download application for BridgeCo devices .SH SYNOPSIS .BI "ffado-bridgeco-downloader [OPTION...] OPERATION .sp .SH DESCRIPTION .B ffado-bridgeco-downloader permits firmware updates to be made to BridgeCo (aka BeBoB) devices under Linux. .I OPERATION describes the action to be carried out. An .I OPERATION is introduced with the GUID of the device to operate on. This is followed by one of the following keywords and any arguments required by that keyword. .TP .B display Display information about the interface .TP .B setguid NEW_GUID Set the device's GUID to .I NEW_GUID .TP .B firmware FILE Download the firmware file .I FILE to the device. .TP .B cne FILE Download the CnE file .I FILE to the device. .TP .B bcd FILE Parse the BeBoB BCD file .I FILE and display information about the contents. .sp .SH OPTIONS .TP .B "\-?, \-\-help, \-\-usage" Display brief usage information and exit .TP .B "\-V, \-\-version" Show the program version and exit .TP .B "\-p, \-\-port=NUM" Specify use of IEEE1394 port .I NUM .TP .B "\-v, \-\-verbose=LEVEL" Produce verbose output. The higher the .I LEVEL the more verbose the messages. The some verbose messages may only be available of FFADO was compiled with debugging enabled. .TP .B "\-b, \-\-noboot" Do not start the bootloader. Use this if the bootloader is already running. .TP .B "\-f, \-\-force" Force firmware download in the event the program doesn't think it's a good idea. Use with caution. .TP .B "\-m, \-\-magic=MAGIC" A magic number you have to obtain before this program will work. Specifying it on the command line means that you accept the risks which come with this tool. The magic number can be obtained in the source code. .sp .SH NOTES Manipulating firmware can cause your device to stop working. The FFADO project accepts no responsibility if this occurs as a result of using this tool. libffado-2.4.5/support/firmware/ffado-dice-firmware.10000644000175000001440000000307611734344527022105 0ustar jwoitheusers.TH FFADO-DICE-FIRMWARE 1 27-Mar-2012 "ffado-dice-firmware" .SH NAME ffado-dice-firmware \- firmware download application for DICE-based devices .SH SYNOPSIS .BI "ffado-dice-firmware [OPTION...] .sp .SH DESCRIPTION .B ffado-dice-firmware permits firmware updates to be made to DICE-based devices under Linux. .sp .SH OPTIONS .TP .B "\-h, \-\-help" Display brief usage information and exit .TP .B "\-p, \-\-port=NUM" Specify use of IEEE1394 port .I NUM .TP .B "\-v, \-\-verbose=LEVEL" Produce verbose output. The higher the .I LEVEL the more verbose the messages. The some verbose messages may only be available of FFADO was compiled with debugging enabled. .TP .B "\-d, \-\-delete=IMAGENAME" Delete image by name. .TP .B "\-e, \-\-dump=FILENAME" Extract current DICE device flash contents into file .I FILENAME .TP .B "\-f, \-\-flash=FILENAME" Write firmware file .I FILENAME to DICE device. .TP .B "\-i, \-\-image-info" Show detailed information about each image within the firmware on the device. .TP .B "\-m, \-\-flash-info" Show information about the flash memory on the device. .TP .B "\-s, \-\-dice-info" Show DICE image vendor and product information. .TP .B "\-t, \-\-test OPERATION" Invoke a test/debug operation. Possible .I OPERATIONs are 1 (POKE, write to address) and 2 (PEEK, read from address). Refer to the source code for further information. .TP .B "\-n, \-\-node NODE" Specify the node to operate on. .sp .SH NOTES Manipulating firmware can cause your device to stop working. The FFADO project accepts no responsibility if this occurs as a result of using this tool. libffado-2.4.5/support/mixer-qt4/0000755000175000001440000000000014206145613016230 5ustar jwoitheuserslibffado-2.4.5/support/mixer-qt4/SConscript0000644000175000001440000000374214206145246020252 0ustar jwoitheusers#!/usr/bin/python # # Copyright (C) 2007-2009 Arnold Krille # Copyright (C) 2007-2008 Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os from string import Template Import( 'env' ) if env['BUILD_MIXER'] == 'true': e = env.Clone() pythonfiles = [ 'ffado/config.py' ] for root, dirs, files in os.walk( "ffado" ): for name in files: if name.endswith( '.pyc' ) or '.in' in name or name.startswith("."): continue pythonfiles.append( os.path.join( root, name ) ) e.ScanReplace( "ffado/config.py.in" ) e.Depends( "ffado/config.py", "#/SConstruct" ) for file in pythonfiles: e.InstallAs( os.path.join(e['pypkgdir'], file), file ) e.ScanReplace( "ffado-mixer.in" ) e.Depends( "ffado-mixer", "SConscript" ) e.Depends( "ffado-mixer", "#/SConstruct" ) e.Install( "$bindir", "ffado-mixer" ) e.ScanReplace( "ffado-mixer-profiler.in" ) e.Depends( "ffado-mixer-profiler", "SConscript" ) e.Depends( "ffado-mixer-profiler", "#/SConstruct" ) e.Install( "$sharedir/icons", "../xdg/hi64-apps-ffado.png" ) # Install the ffado-mixer manpage in section 1 dest = os.path.join("$mandir", "man1", "ffado-mixer.1") env.InstallAs(source="ffado-mixer.1", target=dest) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/0000755000175000001440000000000014206145613017307 5ustar jwoitheuserslibffado-2.4.5/support/mixer-qt4/ffado/configuration.py0000644000175000001440000001266014206145246022537 0ustar jwoitheusers# # Copyright (C) 2008-2009 by Arnold Krille # # 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 dbus import shlex import logging log = logging.getLogger('configparser') class DeviceList: def __init__( self, filename="" ): self.devices = list() if not filename == "": self.updateFromFile( filename ) def updateFromFile( self, filename, must_exist=False): filename = os.path.expanduser(filename) log.debug("DeviceList::updateFromFile(%s)" % filename) if not os.path.exists( filename ): if must_exist: log.error("cannot open file") return f = open( filename, "r" ) lex = Parser( f ) config = {} while lex.peaktoken() != lex.eof: tmp = lex.parsenamedvalue() if tmp != None: config[ tmp[0] ] = tmp[1] if "device_definitions" in config: for dev in config["device_definitions"]: self.addDevice( dev ) def getDeviceById( self, vendor, model ): if isinstance(vendor, dbus.Int32): vendor = str(int(vendor)) if isinstance(model, dbus.Int32): model = str(int(model)) log.debug("DeviceList::getDeviceById( %s, %s )" % (vendor, model )) for dev in self.devices: if int("%s" % dev['vendorid'], 0) == int("%s" % vendor, 0) and \ int("%s" % dev['modelid'], 0) == int("%s" % model, 0): return dev tmp = dict() self.devices.append( tmp ) return tmp def addDevice( self, device_dict ): log.debug("DeviceList::addDevice()") dev = self.getDeviceById( device_dict['vendorid'], device_dict['modelid'] ) dev.update( device_dict ) class Parser: def __init__( self, file ): self.lex = shlex.shlex( file ) self.eof = self.lex.eof def peaktoken( self ): token = self.lex.get_token() self.lex.push_token( token ) return token def parselist( self, level="" ): token = self.peaktoken() if token != "(": return self.lex.get_token() log.debug("%sWill parse list" % level) ret = [] token = self.peaktoken() while token != ")": ret.append( self.parsenamedvalue( level+" " ) ) token = self.peaktoken() log.debug("%slist is %s" % (level, str(ret))) self.lex.get_token() if self.peaktoken() == ",": log.debug("%sFound a delimiter" % level) self.lex.get_token() return ret def parsemap( self, level="" ): token = self.lex.get_token() if token != "{": return log.debug("%sWill parse map" % level) ret = {} token = self.peaktoken() while token != "}": #self.push_token( token ) tmp = self.parsenamedvalue( level+" " ) if tmp != None: ret[ tmp[0] ] = tmp[1] token = self.peaktoken() token = self.lex.get_token() log.debug("%sMap ended with '%s' and '%s'"% (level,token,self.peaktoken())) if self.peaktoken() in (",",";"): log.debug("%sFound a delimiter!" % level) self.lex.get_token() return ret def parsevalue( self, level="" ): token = self.lex.get_token() log.debug("%sparsevalue() called on token '%s'" % (level, token)) self.lex.push_token( token ) if token == "(": value = self.parselist( level+" " ) elif token == "{": value = self.parsemap( level+" " ) else: value = self.lex.get_token().replace( "\"", "" ) token = self.peaktoken() if token == ";": log.debug("%sFound a delimiter!" % level) self.lex.get_token() return value def parsenamedvalue( self, level="" ): token = self.lex.get_token() log.debug("%sparsenamedvalue() called on token '%s'" % (level, token)) if token == "{": self.lex.push_token( token ) return self.parsemap( level+" " ) if len(token) > 0 and token not in ("{","}","(",")",",",";"): log.debug("%sGot name '%s'" %(level,token)) name = token token = self.lex.get_token() if token in ("=",":"): #print( "%sWill parse value" % level ) value = self.parsevalue( level ) return (name,value) log.debug("%sparsenamedvalue() will return None!" % level) return # # Have kind of a test directly included... # if __name__ == "__main__": import sys file = sys.argv[1] log.setLevel(logging.DEBUG) devs = DeviceList( file ) print( devs.devices ) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/dbus_util.py0000644000175000001440000004713214206145246021664 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # 2007-2008 by Arnold Krille # # 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 ffado.config from ffado.configuration import * import dbus try: # First try the PyQt4 module name from dbus.mainloop.qt import DBusQtMainLoop except ImportError: from dbus.mainloop.pyqt5 import DBusQtMainLoop DBusQtMainLoop(set_as_default=True) import logging log = logging.getLogger('dbus') class ControlInterface: def __init__(self, servername, basepath): self.basepath=basepath self.servername=servername if ffado.config.bypassdbus: self.devices = DeviceList(ffado.config.SYSTEM_CONFIG_FILE) self.devices.updateFromFile(ffado.config.USER_CONFIG_FILE) else: self.bus=dbus.SessionBus() def setContignuous(self, subpath, v, idx=None): path = self.basepath + subpath if ffado.config.bypassdbus: log.info("bypassdbus set, would set Continuous %s on server %s" % (path, self.servername)) return try: dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Continuous') if idx == None: dev_cont.setValue(v) else: dev_cont.setValueIdx(idx,v) except: log.error("Failed to set Continuous %s on server %s" % (path, self.servername)) def getContignuous(self, subpath, idx=None): path = self.basepath + subpath if ffado.config.bypassdbus: log.info("bypassdbus set, would get Continuous %s on server %s" % (path, self.servername)) return 0 try: dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Continuous') if idx == None: return int(dev_cont.getValue()) else: return int(dev_cont.getValueIdx(idx)) except: log.error("Failed to get Continuous %s on server %s" % (path, self.servername)) return 0 def setDiscrete(self, subpath, v, idx=None): path = self.basepath + subpath if ffado.config.bypassdbus: log.info("bypassdbus set, would set Discrete %s on server %s" % (path, self.servername)) return try: dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Discrete') if idx == None: dev_cont.setValue(v) else: dev_cont.setValueIdx(v, idx) except: log.error("Failed to set Discrete %s on server %s" % (path, self.servername)) def getDiscrete(self, subpath, idx=None): path = self.basepath + subpath if ffado.config.bypassdbus: log.info("bypassdbus set, would get Discrete %s on server %s" % (path, self.servername)) return 0 try: dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Discrete') if idx == None: return int(dev_cont.getValue()) else: return int(dev_cont.getValueIdx(idx)) except: log.error("Failed to get Discrete %s on server %s" % (path, self.servername)) return 0 def setText(self, subpath, v): path = self.basepath + subpath if ffado.config.bypassdbus: log.info("DEBUG_BYPASSDBUS set, would set Text %s on server %s" % (path, self.servername)) return try: dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Text') dev_cont.setValue(v) except: log.error("Failed to set Text %s on server %s" % (path, self.servername)) def getText(self, subpath): path = self.basepath + subpath if ffado.config.bypassdbus: log.info("bypassdbus set, would get get Text %s on server %s" % (path, self.servername)) return "" try: dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Text') return dev_cont.getValue() except: log.error("Failed to get Text %s on server %s" % (path, self.servername)) return 0 def setMatrixMixerValue(self, subpath, row, col, v): path = self.basepath + subpath if ffado.config.bypassdbus: log.info("bypassdbus set, would set MatrixMixer %s on server %s" % (path, self.servername)) return try: dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.MatrixMixer') dev_cont.setValue(row, col, v) except: log.error("Failed to set MatrixMixer %s on server %s" % (path, self.servername)) def getMatrixMixerValue(self, subpath, row, col): path = self.basepath + subpath if ffado.config.bypassdbus: log.info("bypassdbus set, would get MatrixMixer %s on server %s" % (path, self.servername)) return 0 try: dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.MatrixMixer') return int(dev_cont.getValue(row, col)) except: log.error("Failed to get MatrixMixer %s on server %s" % (path, self.servername)) return 0 def enumSelect(self, subpath, v): path = self.basepath + subpath if ffado.config.bypassdbus: log.info("bypassdbus set, would select %s on server %s" % (path, self.servername)) return try: dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Enum') dev_cont.select(v) except: log.error("Failed to select %s on server %s" % (path, self.servername)) def enumSelected(self, subpath): path = self.basepath + subpath if ffado.config.bypassdbus: log.info("bypassdbus set, would get selected enum %s on server %s" % (path, self.servername)) return 0 try: dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Enum') return dev_cont.selected() except: log.error("Failed to get selected enum %s on server %s" % (path, self.servername)) return 0 def enumGetLabel(self, subpath, v): path = self.basepath + subpath if ffado.config.bypassdbus: log.info("bypassdbus set, would get enum label %s on server %s" % (path, self.servername)) return 0 try: dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Enum') return dev_cont.getEnumLabel(v) except: log.error("Failed to get enum label %s on server %s" % (path, self.servername)) return 0 def enumCount(self, subpath): path = self.basepath + subpath if ffado.config.bypassdbus: log.info("bypassdbus set, would get enum count %s on server %s" % (path, self.servername)) return 0 try: dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Enum') return dev_cont.count() except: log.error("Failed to get enum count %s on server %s" % (path, self.servername)) return 0 class DeviceManagerInterface: """ Implementation of the singleton """ def __init__(self, servername, basepath): self.basepath=basepath + '/DeviceManager' self.servername=servername if ffado.config.bypassdbus: self.devices = DeviceList(ffado.config.SYSTEM_CONFIG_FILE) self.devices.updateFromFile(ffado.config.USER_CONFIG_FILE) else: self.bus=dbus.SessionBus() self.dev = self.bus.get_object(self.servername, self.basepath) self.iface = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.Container') self.updateSignalHandlers = [] self.updateSignalHandlerArgs = {} self.preUpdateSignalHandlers = [] self.preUpdateSignalHandlerArgs = {} self.postUpdateSignalHandlers = [] self.postUpdateSignalHandlerArgs = {} self.destroyedSignalHandlers = [] self.destroyedSignalHandlerArgs = {} if ffado.config.bypassdbus: return # signal reception does not work yet since we need a mainloop for that # and qt3 doesn't provide one for python/dbus try: log.debug("connecting to: Updated on %s (server: %s)" % (self.basepath, self.servername)) self.dev.connect_to_signal("Updated", self.updateSignal, \ dbus_interface="org.ffado.Control.Element.Container") self.dev.connect_to_signal("PreUpdate", self.preUpdateSignal, \ dbus_interface="org.ffado.Control.Element.Container") self.dev.connect_to_signal("PostUpdate", self.postUpdateSignal, \ dbus_interface="org.ffado.Control.Element.Container") self.dev.connect_to_signal("Destroyed", self.destroyedSignal, \ dbus_interface="org.ffado.Control.Element.Container") except dbus.DBusException: traceback.print_exc() def registerPreUpdateCallback(self, callback, arg=None): if not callback in self.preUpdateSignalHandlers: self.preUpdateSignalHandlers.append(callback) # always update the argument self.preUpdateSignalHandlerArgs[callback] = arg def registerPostUpdateCallback(self, callback, arg=None): if not callback in self.postUpdateSignalHandlers: self.postUpdateSignalHandlers.append(callback) # always update the argument self.postUpdateSignalHandlerArgs[callback] = arg def registerUpdateCallback(self, callback, arg=None): if not callback in self.updateSignalHandlers: self.updateSignalHandlers.append(callback) # always update the argument self.updateSignalHandlerArgs[callback] = arg def registerDestroyedCallback(self, callback, arg=None): if not callback in self.destroyedSignalHandlers: self.destroyedSignalHandlers.append(callback) # always update the argument self.destroyedSignalHandlerArgs[callback] = arg def updateSignal(self): log.debug("Received update signal") for handler in self.updateSignalHandlers: arg = self.updateSignalHandlerArgs[handler] try: if arg: handler(arg) else: handler() except: log.error("Failed to execute handler %s" % handler) def preUpdateSignal(self): log.debug("Received pre-update signal") for handler in self.preUpdateSignalHandlers: arg = self.preUpdateSignalHandlerArgs[handler] try: if arg: handler(arg) else: handler() except: log.error("Failed to execute handler %s" % handler) def postUpdateSignal(self): log.debug("Received post-update signal") for handler in self.postUpdateSignalHandlers: arg = self.postUpdateSignalHandlerArgs[handler] try: if arg: handler(arg) else: handler() except: log.error("Failed to execute handler %s" % handler) def destroyedSignal(self): log.debug("Received destroyed signal") for handler in self.destroyedSignalHandlers: arg = self.destroyedSignalHandlerArgs[handler] try: if arg: handler(arg) else: handler() except: log.error("Failed to execute handler %s" % handler) def getNbDevices(self): if ffado.config.bypassdbus: return len(self.devices.devices) return self.iface.getNbElements() def getDeviceName(self, idx): if ffado.config.bypassdbus: return str(idx) return self.iface.getElementName(idx) class ConfigRomInterface: def __init__(self, servername, devicepath): self.basepath=devicepath + '/ConfigRom' self.servername=servername if ffado.config.bypassdbus: self.devices = DeviceList(ffado.config.SYSTEM_CONFIG_FILE) self.devices.updateFromFile(ffado.config.USER_CONFIG_FILE) self.idx = int(devicepath) else: self.bus=dbus.SessionBus() self.dev = self.bus.get_object(self.servername, self.basepath) self.iface = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.ConfigRomX') def getGUID(self): if ffado.config.bypassdbus: return str(self.idx) return self.iface.getGUID() def getVendorName(self): if ffado.config.bypassdbus: return self.devices.devices[self.idx]['vendorname'] return self.iface.getVendorName() def getModelName(self): if ffado.config.bypassdbus: return self.devices.devices[self.idx]['modelname'] return self.iface.getModelName() def getVendorId(self): if ffado.config.bypassdbus: return int(self.devices.devices[self.idx]['vendorid'], 16) return self.iface.getVendorId() def getModelId(self): if ffado.config.bypassdbus: return int(self.devices.devices[self.idx]['modelid'], 16) return self.iface.getModelId() def getUnitVersion(self): if ffado.config.bypassdbus: return 0 return self.iface.getUnitVersion() class ClockSelectInterface: def __init__(self, servername, devicepath): self.basepath=devicepath + '/Generic/ClockSelect' self.servername=servername if ffado.config.bypassdbus: self.devices = DeviceList(ffado.config.SYSTEM_CONFIG_FILE) self.devices.updateFromFile(ffado.config.USER_CONFIG_FILE) self.idx = devicepath else: self.bus=dbus.SessionBus() self.dev = self.bus.get_object(self.servername, self.basepath) self.iface = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.AttributeEnum') self.iface_element = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.Element') def count(self): if ffado.config.bypassdbus: return 1 return self.iface.count() def select(self, idx): if ffado.config.bypassdbus: return 1 return self.iface.select(idx) def selected(self): if ffado.config.bypassdbus: return True return self.iface.selected() def getEnumLabel(self, idx): if ffado.config.bypassdbus: return 'enumlabel ' + str(idx) return self.iface.getEnumLabel(idx) def attributeCount(self): if ffado.config.bypassdbus: return 1 return self.iface.attributeCount() def getAttributeValue(self, idx): if ffado.config.bypassdbus: return 1 return self.iface.getAttributeValue(idx) def getAttributeName(self, idx): if ffado.config.bypassdbus: return 'attrib ' + str(idx) return self.iface.getAttributeName(idx) def canChangeValue(self): if ffado.config.bypassdbus: return 1 return self.iface_element.canChangeValue() class EnumInterface: def __init__(self, servername, basepath): self.basepath = basepath self.servername = servername if ffado.config.bypassdbus: self.devices = DeviceList(ffado.config.SYSTEM_CONFIG_FILE) self.devices.updateFromFile(ffado.config.USER_CONFIG_FILE) else: self.bus = dbus.SessionBus() self.dev = self.bus.get_object(self.servername, self.basepath) self.iface = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.Enum') self.iface_element = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.Element') def count(self): if ffado.config.bypassdbus: return 1 return self.iface.count() def select(self, idx): if ffado.config.bypassdbus: return 1 return self.iface.select(idx) def selected(self): if ffado.config.bypassdbus: return True return self.iface.selected() def getEnumLabel(self, idx): if ffado.config.bypassdbus: # Can't include text here since some code uses int() to extract # a value from the enum label return '0' return self.iface.getEnumLabel(idx) def canChangeValue(self): if ffado.config.bypassdbus: return True return self.iface_element.canChangeValue() def devConfigChanged(self, idx): if ffado.config.bypassdbus: return True return self.iface.devConfigChanged(idx) class SamplerateSelectInterface(EnumInterface): def __init__(self, servername, devicepath): EnumInterface.__init__(self, servername, devicepath + '/Generic/SamplerateSelect') class StreamingStatusInterface(EnumInterface): def __init__(self, servername, devicepath): EnumInterface.__init__(self, servername, devicepath + '/Generic/StreamingStatus') class TextInterface: def __init__(self, servername, basepath): self.basepath=basepath self.servername=servername if ffado.config.bypassdbus: self.devices = DeviceList(ffado.config.SYSTEM_CONFIG_FILE) self.devices.updateFromFile(ffado.config.USER_CONFIG_FILE) else: self.bus=dbus.SessionBus() self.dev = self.bus.get_object( self.servername, self.basepath ) self.iface = dbus.Interface( self.dev, dbus_interface="org.ffado.Control.Element.Text" ) self.iface_element = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.Element') def text(self): if ffado.config.bypassdbus: return "text" return self.iface.getValue() def setText(self,text): if ffado.config.bypassdbus: return self.iface.setValue(text) def canChangeValue(self): if ffado.config.bypassdbus: return True return self.iface_element.canChangeValue() # vim: et libffado-2.4.5/support/mixer-qt4/ffado/ffadowindow.py0000644000175000001440000002715714206145246022206 0ustar jwoitheusers# Copyright (C) 2005-2008 by Pieter Palmers # 2007-2009 by Arnold Krille # # 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 ctypes import datetime import os from ffado.config import * import subprocess # from PyQt4.QtCore import QObject, QTimer, Qt # from PyQt4.QtGui import * from ffado.import_pyqt import * from ffado.dbus_util import * from ffado.panelmanager import PanelManager from ffado.logginghandler import * """Just a small helper to ask the retry-question without a blocking messagebox""" class StartDialog(QWidget): def __init__(self, parent): QWidget.__init__(self, parent) self.setObjectName("Restart Dialog") self.label = QLabel("Somehow the connection to the dbus-service of FFADO couldn't be established.

\nShall we take another try?",self) self.button = QPushButton("Retry", self) self.layout = QGridLayout(self) self.layout.setContentsMargins( 50, 10, 50, 10 ) self.layout.addWidget(self.label, 0, 0, Qt.AlignHCenter|Qt.AlignBottom) self.layout.addWidget(self.button, 1, 0, Qt.AlignHCenter|Qt.AlignTop) class FFADOWindow(QMainWindow): def __init__(self, parent): QMainWindow.__init__(self, parent) self.textlogger = QTextLogger(self) dock = QDockWidget("Log Messages",self) dock.setWidget(self.textlogger.textedit) logging.getLogger('').addHandler(self.textlogger) self.addDockWidget(Qt.BottomDockWidgetArea, dock) self.statuslogger = QStatusLogger(self, self.statusBar(), 20) logging.getLogger('').addHandler(self.statuslogger) self.settings = QtCore.QSettings(self) self.manager = PanelManager(self) self.manager.connectionLost.connect(self.connectToDBUS) filemenu = self.menuBar().addMenu("&File") self.openaction = QAction(QIcon.fromTheme("document-open"),"&Open", self) self.openaction.setShortcut(self.tr("Ctrl+O")) self.openaction.setEnabled(False) self.openaction.triggered.connect(self.manager.readSettings) filemenu.addAction(self.openaction) self.saveaction = QAction(QIcon.fromTheme("document-save-as"),"&Save as...", self) self.saveaction.setShortcut(self.tr("Ctrl+S")) self.saveaction.setEnabled(False) self.saveaction.triggered.connect(self.manager.saveSettings) filemenu.addAction(self.saveaction) self.quitaction = QAction(QIcon.fromTheme("application-exit"),"&Quit", self) self.quitaction.setShortcut(self.tr("Ctrl+q")) self.quitaction.triggered.connect(self.close) filemenu.addAction(self.quitaction) self.editmenu = self.menuBar().addMenu("&View") self.thememenu = self.editmenu.addMenu("Theme") themes = QStyleFactory.keys() self.menuTheme = {} for theme in themes: self.menuTheme[theme] = QAction(QIcon.fromTheme("preferences-desktop-theme"), theme, self ) self.menuTheme[theme].setCheckable(True) if (ffado_python3 and (self.style().objectName().lower() == theme.lower()) or not(ffado_python3) and (self.style().objectName().toLower() == theme.toLower() if ffado_pyqt_version == 4 else self.style().objectName().lower() == theme.lower())): self.menuTheme[theme].setDisabled(True) self.menuTheme[theme].setChecked(True) self.menuTheme[theme].triggered.connect(self.switchTheme ) self.thememenu.addAction( self.menuTheme[theme] ) conftheme = self.settings.value("window/theme", "ukui-dark") contains = False for theme in self.menuTheme: if theme.__str__() == conftheme: contains = True break if contains: for theme in self.menuTheme: if theme.__str__() != conftheme: self.menuTheme[theme].setChecked(False) self.menuTheme[theme].setDisabled(False) for theme in self.menuTheme: if theme.__str__() == conftheme: self.menuTheme[theme].setDisabled(True) QApplication.setStyle(QStyleFactory.create(theme)) self.settings.setValue("window/theme", theme.__str__()) self.updateaction = QAction(QIcon.fromTheme("view-refresh"),"&Update Mixer Panels", self) self.updateaction.setEnabled(False) self.updateaction.triggered.connect(self.manager.updatePanels) self.editmenu.addAction(self.updateaction) self.refreshaction = QAction(QIcon.fromTheme("view-refresh"),"&Refresh Current Panels", self) self.refreshaction.triggered.connect(self.manager.refreshPanels) self.editmenu.addAction(self.refreshaction) self.editmenu.addSeparator() self.devices = {} helpmenu = self.menuBar().addMenu( "&Help" ) self.aboutaction = QAction(QIcon.fromTheme("help-about"), "About &FFADO", self ) self.aboutaction.triggered.connect(self.aboutFFADO) helpmenu.addAction( self.aboutaction ) self.aboutqtaction = QAction(QIcon.fromTheme("help-about"), "About &Qt", self ) self.aboutqtaction.triggered.connect(QApplication.instance().aboutQt) helpmenu.addAction( self.aboutqtaction ) log.info( "Starting up" ) QTimer.singleShot( 1, self.tryStartDBUSServer ) def __del__(self): log.info("__del__") del self.manager log.info("__del__ finished") def switchTheme(self, checked) : for theme in self.menuTheme : if not self.menuTheme[theme].isEnabled() : self.menuTheme[theme].setChecked(False) self.menuTheme[theme].setDisabled(False) for theme in self.menuTheme : if self.menuTheme[theme].isChecked() : self.menuTheme[theme].setDisabled(True) QApplication.setStyle(QStyleFactory.create(theme)) self.settings.setValue("window/theme", theme.__str__()) def closeEvent(self, event): log.info("closeEvent()") event.accept() def connectToDBUS(self): log.info("connectToDBUS") try: self.setupDeviceManager() except dbus.DBusException as ex: log.error("Could not communicate with the FFADO DBus service...") if not hasattr(self,"retry"): self.retry = StartDialog(self) self.retry.button.clicked.connect(self.tryStartDBUSServer) if hasattr(self, "retry"): self.manager.setParent(None) self.setCentralWidget(self.retry) self.retry.setEnabled(True) def tryStartDBUSServer(self): try: self.setupDeviceManager() except dbus.DBusException as ex: if hasattr(self, "retry"): self.retry.setEnabled(False) subprocess.Popen(['ffado-dbus-server', '-v3'], close_fds=True).pid QTimer.singleShot(5000, self.connectToDBUS) def setupDeviceManager(self): devmgr = DeviceManagerInterface(FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH) self.manager.setManager(devmgr) if hasattr(self, "retry"): self.retry.setParent(None) self.setCentralWidget(self.manager) self.updateaction.setEnabled(True) def aboutFFADO(self): QMessageBox.about( self, "About FFADO", """

ffado.org

{ffado_version}

FFADO is the new approach to have FireWire audio on Linux.

© 2006-2021 by the FFADO developers
ffado is licensed under the GPLv3, for the full license text see www.gnu.org/licenses or the LICENSE.* files shipped with ffado.

FFADO developers are:

  • Pieter Palmers
  • Daniel Wagner
  • Jonathan Woithe
  • Arnold Krille
  • Philippe Carriere
  • Takashi Sakamoto
with contributions from:
  • Adrian Knoth
  • Stefan Richter
  • Jano Svitok
  • Pander Musubi
""".format(ffado_version=get_ffado_version(), thisyear=datetime.datetime.now().year)) def get_ffado_version(): try: # call the C function ffado_get_version() to figure out the version lib = ctypes.cdll.LoadLibrary('libffado.so') func = ctypes.CFUNCTYPE(ctypes.c_char_p) ffado_get_version = func(('ffado_get_version', lib)) return ffado_get_version() except: return "libffado" def get_lock(process_name): import socket import sys global lock_socket lock_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) try: lock_socket.bind('\0' + process_name) # Lock acquired except socket.error: print( 'ffado-mixer instance is already running' ) sys.exit() def ffadomain(args): #set up logging import logging logging.basicConfig( datefmt="%H:%M:%S", format="%(asctime)s %(name)-16s %(levelname)-8s %(message)s" ) if DEBUG: debug_level = logging.DEBUG else: debug_level = logging.INFO get_lock('ffado-mixer') # Very simple command line option parser if (len(args) > 1) and (args[1] == "-b" or args[1] == "--bypassdbus"): ffado.config.bypassdbus = True # main loggers: logging.getLogger('main').setLevel(debug_level) logging.getLogger('dbus').setLevel(debug_level) logging.getLogger('registration').setLevel(debug_level) logging.getLogger('panelmanager').setLevel(debug_level) logging.getLogger('configparser').setLevel(logging.INFO) # widgets: logging.getLogger('matrixmixer').setLevel(debug_level) logging.getLogger('crossbarrouter').setLevel(debug_level) # mixers: logging.getLogger('audiofire').setLevel(debug_level) logging.getLogger('bridgeco').setLevel(debug_level) logging.getLogger('edirolfa101').setLevel(debug_level) logging.getLogger('edirolfa66').setLevel(debug_level) logging.getLogger('motu').setLevel(debug_level) logging.getLogger('rme').setLevel(debug_level) logging.getLogger('phase24').setLevel(debug_level) logging.getLogger('phase88').setLevel(debug_level) logging.getLogger('quatafire').setLevel(debug_level) logging.getLogger('saffirebase').setLevel(debug_level) logging.getLogger('saffire').setLevel(debug_level) logging.getLogger('saffirepro').setLevel(debug_level) logging.getLogger('global').setLevel(debug_level) log = logging.getLogger('main') log.debug("Using %s with Qt: %s PyQt: %s" % (get_ffado_version(), QtCore.QT_VERSION_STR, QtCore.PYQT_VERSION_STR)) app = QApplication(args) app.setWindowIcon( QIcon( SHAREDIR + "/icons/hi64-apps-ffado.png" ) ) app.setOrganizationName("FFADO") app.setOrganizationDomain("ffado.org") app.setApplicationName("ffado-mixer") mainwindow = FFADOWindow(None) # rock & roll mainwindow.show() return app.exec_() if __name__ == "__main__": import sys sys.exit(ffadomain(sys.argv)) # # vim: ts=4 sw=4 et libffado-2.4.5/support/mixer-qt4/ffado/import_pyqt.py0000644000175000001440000000401714206145246022254 0ustar jwoitheusers# # Copyright (C) 2017 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 . # # This module handles the importing of PyQt modules for both PyQt4 and PyQt5 # under Python2 or Python3. If Python3 is installed it is assumed that # PyQt5 is in use (this is reasonable because PyQt5 is what everyone wants # to use under Python3). Otherwise (that is, under Python2), an import of # PyQt4 is tried first; if an import error occurs then PyQt5 is assumed. # # All modules used by any part of ffado-mixer are imported. This greatly # simplifies the process. Otherwise the modules to import would be delivered # by string variables, and there isn't a supported way to do this across # Python2 and Python3. import sys ffado_python3 = sys.version_info >= (3,) if ffado_python3: ffado_pyqt_version = 5 else: try: from PyQt4 import QtGui ffado_pyqt_version = 4 except ImportError: ffado_pyqt_version = 5 if ffado_pyqt_version == 4: from PyQt4 import QtGui, QtCore, Qt, uic from PyQt4.QtCore import QByteArray, QObject, QTimer, Qt, pyqtSignal, QString, pyqtSlot from PyQt4.QtGui import * else: from PyQt5 import QtGui, Qt, QtCore, Qt, QtWidgets, uic from PyQt5.QtCore import QByteArray, QObject, pyqtSignal, pyqtSlot, QTimer, Qt from PyQt5.QtGui import * from PyQt5.QtWidgets import * libffado-2.4.5/support/mixer-qt4/ffado/logginghandler.py0000644000175000001440000000446114206145246022654 0ustar jwoitheusers# # Copyright (C) 2008 by Arnold Krille # # 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 . # # from PyQt4.QtGui import QTextEdit, QAbstractSlider, QColor # from PyQt4.QtCore import QObject, pyqtSignal, QString from ffado.import_pyqt import * import logging log = logging.getLogger('logginghandler') class QStatusLogger( QObject, logging.Handler ): log = pyqtSignal(QString if ffado_pyqt_version == 4 else str, int, name='log') def __init__( self, parent, statusbar, level=logging.NOTSET ): QObject.__init__( self, parent ) logging.Handler.__init__( self, level ) self.setFormatter( logging.Formatter( "%(name)s: %(message)s" ) ) self.log.connect(statusbar.showMessage) def emit( self, record ): self.log.emit('%s: %s'.format(record.name, record.getMessage()), 5000) class QTextLogger( logging.Handler ): def __init__( self, parent, level=logging.NOTSET ): logging.Handler.__init__( self, level ) self.textedit = QTextEdit( parent ) self.textedit.setReadOnly( True ) self.textedit.setAcceptRichText( True ) def emit( self, record ): color = QColor( "#000000" ) if record.levelno > 20: color = QColor( "#ffff00" ) if record.levelno > 30: color = QColor( "#ff0000" ) if record.levelno <= 10: color = QColor( "#808080" ) self.textedit.setTextColor( color ) tmp = "%s %s: %s" % (record.asctime, record.name, record.getMessage()) self.textedit.append( tmp ) self.textedit.verticalScrollBar().triggerAction( QAbstractSlider.SliderToMaximum ) # # vim: et # libffado-2.4.5/support/mixer-qt4/ffado/mixer/0000755000175000001440000000000014206145613020433 5ustar jwoitheuserslibffado-2.4.5/support/mixer-qt4/ffado/mixer/Saffire_Pro14_monitoring.ui0000644000175000001440000006400314206145246025610 0ustar jwoitheusers SaffirePro14Monitoring 0 0 806 724 Saffire Pro 40 Monitoring false Global Settings Hardware H/W true Dim Level Qt::AlignCenter -87 0 Qt::Vertical false QSlider::TicksBothSides 10 true Activate Dim true Mute true Line/Out Settings Qt::Horizontal 40 20 Mono true Qt::Horizontal 40 20 Qt::Horizontal 40 20 Mono true Qt::Horizontal 40 20 75 true 1 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 2 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 3 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 4 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true Qt::Vertical 20 40 true Line/In Switches 1 Line buttonGroup Inst buttonGroup 2 Line buttonGroup_2 Inst buttonGroup_2 3 Lo buttonGroup_3 Hi buttonGroup_3 4 Lo buttonGroup_4 Hi buttonGroup_4 libffado-2.4.5/support/mixer-qt4/ffado/mixer/Saffire_Pro24_monitoring.ui0000644000175000001440000010557214206145246025620 0ustar jwoitheusers SaffirePro24Monitoring 0 0 969 620 Saffire Pro 40 Monitoring false Global Settings Hardware H/W true Dim Level Qt::AlignCenter -87 0 Qt::Vertical false QSlider::TicksBothSides 10 true Activate Dim true Mute true GlobalDim GlobalMute HWSwitch GlobalSetLabel Line/Out Settings Qt::Horizontal 40 20 Mono true Qt::Horizontal 40 20 Qt::Horizontal 40 20 Mono true Qt::Horizontal 40 20 Qt::Horizontal 40 20 Mono true Qt::Horizontal QSizePolicy::Expanding 40 20 75 true 1 -40 0 Qt::Vertical QSlider::TicksBothSides 5 0 0 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 2 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 3 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 4 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 5 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 6 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true true Line/In Switches QLayout::SetDefaultConstraint 1 Line buttonGroup Inst buttonGroup 2 Line buttonGroup_2 Inst buttonGroup_2 3 Lo buttonGroup_3 Hi buttonGroup_3 4 Lo buttonGroup_4 Hi buttonGroup_4 Qt::Vertical 20 40 libffado-2.4.5/support/mixer-qt4/ffado/mixer/Saffire_Pro26_monitoring.ui0000644000175000001440000005760214206145246025622 0ustar jwoitheusers SaffirePro26Monitoring 0 0 789 666 Saffire Pro 26 Monitoring false Global Settings Hardware H/W true Dim Level Qt::AlignCenter -87 0 Qt::Vertical false QSlider::TicksBothSides 10 true Activate Dim true Mute true Line/Out Settings 75 true 1 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 2 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 3 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 4 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 5 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 6 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true libffado-2.4.5/support/mixer-qt4/ffado/mixer/Saffire_Pro40_monitoring.ui0000644000175000001440000013534214206145246025614 0ustar jwoitheusers SaffirePro40Monitoring 0 0 1188 658 Saffire Pro 40 Monitoring false Global Settings Hardware H/W true Dim Level Qt::AlignCenter -87 0 Qt::Vertical false QSlider::TicksBothSides 10 true Activate Dim true Mute true Line/Out Settings Qt::Horizontal 40 20 Mono true Qt::Horizontal 40 20 Qt::Horizontal 40 20 Mono true Qt::Horizontal 40 20 Qt::Horizontal 40 20 Mono true Qt::Horizontal 40 20 Qt::Horizontal 40 20 Mono true Qt::Horizontal 40 20 Qt::Horizontal 40 20 Mono true Qt::Horizontal 40 20 75 true 1 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 2 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 3 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 4 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 5 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 6 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 7 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 8 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true 75 true 9 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true false Mute Mu true Group Dim GD true Group Mute GM true 75 true 10 -40 0 Qt::Vertical QSlider::TicksBothSides 5 UnActivate UA true Mute Mu true Group Dim GD true Group Mute GM true true ADAT as SPDIF Qt::Vertical 20 40 libffado-2.4.5/support/mixer-qt4/ffado/mixer/audiofire.py0000644000175000001440000004302514206145246022762 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtCore import Qt, QTimer # from PyQt4.QtGui import QWidget, QHBoxLayout, QVBoxLayout # from PyQt4.QtGui import QGroupBox, QTabWidget, QLabel # from PyQt4.QtGui import QPushButton, QToolButton, QSpacerItem, QSizePolicy from ffado.import_pyqt import * from ffado.config import * import logging log = logging.getLogger('audiofire') class AfMonitorWidget(QWidget): def __init__(self,parent = None): QWidget.__init__(self,parent) uicLoad("ffado/mixer/audiofire_strip", self) class AfSettingsWidget(QWidget): def __init__(self,parent = None): QWidget.__init__(self,parent) uicLoad("ffado/mixer/audiofire_settings", self) class AudioFire(QWidget): def __init__(self,parent = None): QWidget.__init__(self,parent) log.debug("Init AudioFire mixer window") def getDisplayTitle(self): modelId = self.configrom.getModelId() if modelId == 0x0AF2: return "AudioFire2" if modelId == 0x0AF4: return "AudioFire4" if modelId == 0x0AF8: return "AudioFire8" if modelId == 0x0AF9: return "AudioFirePre8" if modelId == 0x0AF12: return "AudioFire12" return "Generic FireWorks" def updateMatrixButton(self,a0): sender = self.sender() if a0: state = 1 else: state = 0 log.debug("set %s %d %d to %d" % ( self.MatrixButtonControls[sender][0], self.MatrixButtonControls[sender][1], self.MatrixButtonControls[sender][2], state)) self.hw.setMatrixMixerValue(self.MatrixButtonControls[sender][0], self.MatrixButtonControls[sender][1], self.MatrixButtonControls[sender][2], state) def updateMatrixRotary(self,a0): sender = self.sender() vol = a0 log.debug("set %s %d %d to %d" % ( self.MatrixRotaryControls[sender][0], self.MatrixRotaryControls[sender][1], self.MatrixRotaryControls[sender][2], vol)) self.hw.setMatrixMixerValue(self.MatrixRotaryControls[sender][0], self.MatrixRotaryControls[sender][1], self.MatrixRotaryControls[sender][2], vol) def updateMatrixVolume(self,a0): sender = self.sender() vol = a0 #vol = 0x01000000-vol log.debug("set %s %d %d to %d" % ( self.MatrixVolumeControls[sender][0], self.MatrixVolumeControls[sender][1], self.MatrixVolumeControls[sender][2], vol)) self.hw.setMatrixMixerValue(self.MatrixVolumeControls[sender][0], self.MatrixVolumeControls[sender][1], self.MatrixVolumeControls[sender][2], vol) def updateVolume(self,a0): sender = self.sender() vol = a0 #vol = 0x01000000-vol log.debug("set %s to %d" % ( self.VolumeControls[sender][0], vol)) self.hw.setContignuous(self.VolumeControls[sender][0], vol) def updateSelector(self,a0): sender = self.sender() if a0: state = 1 else: state = 0 log.debug("set %s to %d" % ( self.SelectorControls[sender][0], state)) self.hw.setDiscrete(self.SelectorControls[sender][0], state) def updateTrigger(self): sender = self.sender() log.debug("trigger %s" % (self.TriggerControls[sender][0])) self.hw.setDiscrete(self.TriggerControls[sender][0], 1) def updateSPDIFmodeControl(self,a0): sender = self.sender() if a0: state = 1 else: state = 0 if state: log.debug("set %s to %d" % ( self.SPDIFmodeControls[sender][0], self.SPDIFmodeControls[sender][1])) self.hw.setDiscrete(self.SPDIFmodeControls[sender][0], self.SPDIFmodeControls[sender][1]) def updateDigIfaceControl(self, a0): sender = self.sender() state = a0 # 0/2/3 is available but GUI set 0/1/2 if a0 > 0: state += 1 log.debug("set %s to %d" % ( self.DigIfaceControls[sender][0], state)) self.hw.setDiscrete(self.DigIfaceControls[sender][0], state) def updatePlbkRouteControl(self, src): sender = self.sender() path = self.PlbkRouteControls[sender][0] sink = self.PlbkRouteControls[sender][1] self.hw.setDiscrete(path, sink, src) self.setStreamLabel(src, sink) def setStreamLabel(self, src, sink): pos = src * 2 + 1 self.StreamMonitors[sink].lblName.setText("Playback %d/%d" % (pos, pos + 1)) def buildMixer(self): log.debug("Building mixer") self.MatrixButtonControls={} self.MatrixRotaryControls={} self.MatrixVolumeControls={} self.VolumeControls={} self.SelectorControls={} self.SPDIFmodeControls={} self.TriggerControls={} self.DigIfaceControls={} self.PlbkRouteControls={} self.StreamMonitors=[] nb_pys_out = self.hw.getDiscrete("/HwInfo/PhysicalAudioOutCount") nb_pys_in = self.hw.getDiscrete("/HwInfo/PhysicalAudioInCount") outputtabslayout = QHBoxLayout( self ) outputtabs = QTabWidget(self) outputtabslayout.addWidget( outputtabs, 1 ) for outpair in range(int(nb_pys_out/2)): tab = QWidget( outputtabs ) tablayout = QHBoxLayout( tab ) grpMonitor = QGroupBox(tab) tablayout.addWidget(grpMonitor) grpPlayback = QGroupBox(tab) tablayout.addWidget(grpPlayback) grpOutput = QGroupBox(tab) tablayout.addWidget(grpOutput) grpMonitor.setTitle("Monitor") grpPlayback.setTitle("Playback") grpOutput.setTitle("Output") # monitor controls grpMonitorLayout = QHBoxLayout() grpMonitor.setLayout(grpMonitorLayout); output_id = outpair * 2 for inpair in range(int(nb_pys_in/2)): # create GUI elements strip = AfMonitorWidget( grpMonitor ) grpMonitorLayout.addWidget( strip, 1 ) input_id = inpair*2 strip.lblName.setText("In %d/%d" % (input_id+1, input_id+2)) # add the elements to the control structure self.MatrixButtonControls[strip.btnMute0] = ['/Mixer/MonitorMute', input_id, output_id] self.MatrixButtonControls[strip.btnMute1] = ['/Mixer/MonitorMute', input_id + 1, output_id + 1] self.MatrixButtonControls[strip.btnSolo0] = ['/Mixer/MonitorSolo', input_id, output_id] self.MatrixButtonControls[strip.btnSolo1] = ['/Mixer/MonitorSolo', input_id + 1, output_id + 1] self.MatrixRotaryControls[strip.rotPan0] = ['/Mixer/MonitorPan', input_id, output_id] self.MatrixRotaryControls[strip.rotPan1] = ['/Mixer/MonitorPan', input_id + 1, output_id + 1] self.MatrixVolumeControls[strip.sldGain0] = ['/Mixer/MonitorGain', input_id, output_id] self.MatrixVolumeControls[strip.sldGain1] = ['/Mixer/MonitorGain', input_id + 1, output_id + 1] # playback grpPlaybackLayout = QHBoxLayout() grpPlayback.setLayout(grpPlaybackLayout); strip = AfMonitorWidget( grpPlayback ) grpPlaybackLayout.addWidget(strip, 1) strip.lblName.setText("Playback %d/%d" % (output_id+1, output_id+2)) self.StreamMonitors.append(strip) self.VolumeControls[strip.sldGain0] = ["/Mixer/PC%dGain" % (output_id)] self.VolumeControls[strip.sldGain1] = ["/Mixer/PC%dGain" % (output_id+1)] self.SelectorControls[strip.btnMute0] = ["/Mixer/PC%dMute" % (output_id)] self.SelectorControls[strip.btnMute1] = ["/Mixer/PC%dMute" % (output_id+1)] self.SelectorControls[strip.btnSolo0] = ["/Mixer/PC%dSolo" % (output_id)] self.SelectorControls[strip.btnSolo1] = ["/Mixer/PC%dSolo" % (output_id+1)] # fix up mixer strip gui strip.rotPan0.hide() strip.rotPan1.hide() # output grpOutputLayout = QHBoxLayout() grpOutput.setLayout(grpOutputLayout); strip = AfMonitorWidget( grpOutput ) grpOutputLayout.addWidget(strip, 1) strip.lblName.setText("Output %d/%d" % (output_id+1, output_id+2)) self.VolumeControls[strip.sldGain0] = ["/Mixer/OUT%dGain" % (output_id)] self.VolumeControls[strip.sldGain1] = ["/Mixer/OUT%dGain" % (output_id+1)] self.SelectorControls[strip.btnMute0] = ["/Mixer/OUT%dMute" % (output_id)] self.SelectorControls[strip.btnMute1] = ["/Mixer/OUT%dMute" % (output_id+1)] self.SelectorControls[strip.btnSolo0] = ["/Mixer/OUT%dNominal" % (output_id)] self.SelectorControls[strip.btnSolo1] = ["/Mixer/OUT%dNominal" % (output_id+1)] # fix up mixer strip gui strip.btnSolo0.setText("Pad") strip.btnSolo1.setText("Pad") strip.rotPan0.hide() strip.rotPan1.hide() # add the tab outputtabs.addTab( tab, "Out %d/%d" % (output_id+1, output_id+2)) # add an input config tab tab = QWidget( outputtabs ) tablayout = QHBoxLayout( tab ) for inpair in range(nb_pys_in): # create GUI elements log.debug("strip") grpInput = QGroupBox(tab) tablayout.addWidget(grpInput) grpInput.setTitle("In %d" % (inpair+1)) grpInputLayout = QVBoxLayout() grpInput.setLayout(grpInputLayout); label = QLabel( grpInput ) grpInputLayout.addWidget( label ) label.setText("In %d" % (inpair+1)) label.setAlignment(Qt.AlignCenter) btn = QToolButton( grpInput ) grpInputLayout.addWidget( btn ) btn.setText("Pad") btn.setCheckable(True) self.SelectorControls[btn] = ["/Mixer/IN%dNominal" % (inpair)] spacer = QSpacerItem(1,1,QSizePolicy.Minimum,QSizePolicy.Expanding) grpInputLayout.addItem(spacer) outputtabs.addTab( tab, "Input") # add an settings tab tab = QWidget( outputtabs ) tablayout = QHBoxLayout( tab ) outputtabs.addTab( tab, "Settings") settings = AfSettingsWidget( tab ) has_sw_phantom = self.hw.getDiscrete("/HwInfo/PhantomPower") if has_sw_phantom: self.SelectorControls[settings.btnPhantom] = ["/PhantomPower"] else: settings.btnPhantom.hide() has_opt_iface = self.hw.getDiscrete('/HwInfo/OpticalInterface') if has_opt_iface: self.DigIfaceControls[settings.cmbDigIface] = ['/DigitalInterface'] else: settings.DigIface.hide() has_plbk_route = self.hw.getDiscrete('/HwInfo/PlaybackRouting') if has_plbk_route: self.PlbkRouteControls[settings.cmbRoute1] = ['/PlaybackRouting', 0] self.PlbkRouteControls[settings.cmbRoute2] = ['/PlaybackRouting', 1] self.PlbkRouteControls[settings.cmbRoute3] = ['/PlaybackRouting', 2] if self.configrom.getModelId() == 0x0AF2: label_route2 = 'Headphone Out 1/2' else: label_route2 = 'Analog Out 3/4' settings.labelRoute2.setText(label_route2) else: settings.PlbkRoute.hide() self.TriggerControls[settings.btnSaveSettings] = ["/SaveSettings"] self.TriggerControls[settings.btnIdentify] = ["/Identify"] if self.configrom.getModelId() == 0x0AF12: settings.spdifMode.hide() else: self.SPDIFmodeControls[settings.radioConsumer] = ["/SpdifMode", 0] self.SPDIFmodeControls[settings.radioProfessional] = ["/SpdifMode", 1] # Store a reference to the "save" button for later manipulation self.btnSaveSettings = settings.btnSaveSettings def polledUpdate(self): ss = self.streamingstatus.selected() # Only alter controls sensitive to the streaming state when the # streaming state has changed. if (ss != self.streaming_state): ss_txt = self.streamingstatus.getEnumLabel(ss) # The device doesn't cope very well if "save settings" is done # while streaming is active self.btnSaveSettings.setEnabled(ss_txt=='Idle') self.streaming_state = ss def initValues(self): log.debug("Init values") for ctrl, info in self.MatrixVolumeControls.items(): vol = self.hw.getMatrixMixerValue(self.MatrixVolumeControls[ctrl][0], self.MatrixVolumeControls[ctrl][1], self.MatrixVolumeControls[ctrl][2]) #vol = 0x01000000-vol log.debug("%s volume is %d" % (ctrl.objectName() , vol)) ctrl.setValue(vol) # connect the UI element ctrl.valueChanged.connect(self.updateMatrixVolume) for ctrl, info in self.MatrixButtonControls.items(): state = self.hw.getMatrixMixerValue(self.MatrixButtonControls[ctrl][0], self.MatrixButtonControls[ctrl][1], self.MatrixButtonControls[ctrl][2]) log.debug("%s state is %d" % (ctrl.objectName() , state)) if state: ctrl.setChecked(True) else: ctrl.setChecked(False) # connect the UI element ctrl.clicked.connect(self.updateMatrixButton) for ctrl, info in self.MatrixRotaryControls.items(): vol = self.hw.getMatrixMixerValue(self.MatrixRotaryControls[ctrl][0], self.MatrixRotaryControls[ctrl][1], self.MatrixRotaryControls[ctrl][2]) log.debug("%s value is %d" % (ctrl.objectName(), vol)) ctrl.setValue(vol) # connect the UI element ctrl.valueChanged.connect(self.updateMatrixRotary) for ctrl, info in self.VolumeControls.items(): vol = self.hw.getContignuous(self.VolumeControls[ctrl][0]) #vol = 0x01000000-vol log.debug("%s volume is %d" % (ctrl.objectName() , vol)) ctrl.setValue(vol) # connect the UI element ctrl.valueChanged.connect(self.updateVolume) for ctrl, info in self.SelectorControls.items(): state = self.hw.getDiscrete(self.SelectorControls[ctrl][0]) log.debug("%s state is %d" % (ctrl.objectName() , state)) if state: ctrl.setChecked(True) else: ctrl.setChecked(False) # connect the UI element ctrl.clicked.connect(self.updateSelector) for ctrl, info in self.TriggerControls.items(): # connect the UI element ctrl.clicked.connect(self.updateTrigger) for ctrl, info in self.SPDIFmodeControls.items(): state = self.hw.getDiscrete(self.SPDIFmodeControls[ctrl][0]) log.debug("%s state is %d" % (ctrl.objectName() , state)) if state == self.SPDIFmodeControls[ctrl][1]: ctrl.setChecked(True) else: ctrl.setChecked(False) # connect the UI element ctrl.toggled.connect(self.updateSPDIFmodeControl) for ctrl, info in self.DigIfaceControls.items(): state = self.hw.getDiscrete(self.DigIfaceControls[ctrl][0]) # 0/2/3 is available but GUI set 0/1/2 if state > 0: state -= 1 ctrl.setCurrentIndex(state) ctrl.activated.connect(self.updateDigIfaceControl) for ctrl, info in self.PlbkRouteControls.items(): sink = self.PlbkRouteControls[ctrl][1] src = self.hw.getDiscrete(self.PlbkRouteControls[ctrl][0], sink) ctrl.setCurrentIndex(src) self.setStreamLabel(src, sink) ctrl.activated.connect(self.updatePlbkRouteControl) self.update_timer = QTimer(self) self.update_timer.timeout.connect(self.polledUpdate) self.update_timer.start(1000) self.streaming_state = -1 # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/audiofire_settings.ui0000644000175000001440000001374714206145246024677 0ustar jwoitheusers Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. AfSettingsWidget 0 0 534 207 Save Settings to Device S/PDIF Mode Consumer Professional Phantom Power true Identify Device Digital Interface SPDIF Coaxial SPDIF Optical ADAT Optical Playback Routing Analog Out 1/2 Stream Playback 1/2 Stream Playback 3/4 Stream Playback 5/6 Headphone Out 1/2 Stream Playback 1/2 Stream Playback 3/4 Stream Playback 5/6 Digital Out 1/2 Stream Playback 1/2 Stream Playback 3/4 Stream Playback 5/6 qPixmapFromMimeSource radioConsumer radioProfessional cmbDigIface cmbRoute1 cmbRoute2 cmbRoute3 btnPhantom btnSaveSettings btnIdentify libffado-2.4.5/support/mixer-qt4/ffado/mixer/audiofire_strip.ui0000644000175000001440000001556014206145246024173 0ustar jwoitheusers Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. AfMonitorWidget 0 0 86 230 0 0 Name Qt::AlignCenter false 0 0 30 30 16777215 50 255 0 0 30 30 16777215 50 255 0 10 0 50 0 16777216 1048576 262144 Qt::Vertical 1048576 0 10 0 16777216 1048576 262144 Qt::Vertical 1048576 0 0 S true Qt::ToolButtonTextOnly 0 0 M true Qt::ToolButtonTextOnly 0 0 M true Qt::ToolButtonTextOnly 0 0 S true Qt::ToolButtonTextOnly qPixmapFromMimeSource rotPan0 rotPan1 btnSolo0 btnSolo1 btnMute0 btnMute1 sldGain0 sldGain1 libffado-2.4.5/support/mixer-qt4/ffado/mixer/bcoaudio5.ui0000644000175000001440000003236614206145246022660 0ustar jwoitheusers Copyright (C) 2005-2008 by Daniel Wagner This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. BCoAudio5ControlUI 0 0 335 356 BridgeCo Audio 5 Control Output 3/4 Source false Line 3/4 Mix SPDIF Input 3/4 Qt::AlignCenter false S/PDIF Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 Output 1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 3/4 Qt::AlignCenter false Cross 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 A Qt::AlignCenter false B Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 40 20 qPixmapFromMimeSource sldInput12 sliderMoved(int) BCoAudio5ControlUI setVolumeIn12(int) 20 20 20 20 sldInput34 sliderMoved(int) BCoAudio5ControlUI setVolumeIn34(int) 20 20 20 20 sldInputSPDIF sliderMoved(int) BCoAudio5ControlUI setVolumeInSPDIF(int) 20 20 20 20 sldOutput12 sliderMoved(int) BCoAudio5ControlUI setVolumeOut12(int) 20 20 20 20 sldOutput34 sliderMoved(int) BCoAudio5ControlUI setVolumeOut34(int) 20 20 20 20 sldCrossA sliderMoved(int) BCoAudio5ControlUI setCrossA(int) 20 20 20 20 sldCrossB sliderMoved(int) BCoAudio5ControlUI setCrossB(int) 20 20 20 20 comboMixSource activated(int) BCoAudio5ControlUI setComboMixSource(int) 20 20 20 20 libffado-2.4.5/support/mixer-qt4/ffado/mixer/bcoaudio5control.py0000644000175000001440000000644014206145246024266 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Daniel Wagner # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QWidget from ffado.import_pyqt import * from ffado.config import * import logging log = logging.getLogger('bridgeco') class BCoAudio5Control(QWidget): def __init__(self,parent = None): QWidget.__init__(self,parent) uicLoad("ffado/mixer/bcoaudio5", self) self.VolumeControls={ 'in_line12' : ['/Mixer/Feature_Volume_1', self.sldInput12], 'in_line34' : ['/Mixer/Feature_Volume_2', self.sldInput34], 'in_spdif' : ['/Mixer/Feature_Volume_3', self.sldInputSPDIF], 'out_line12' : ['/Mixer/Feature_Volume_6', self.sldOutput12], 'out_line34' : ['/Mixer/Feature_Volume_7', self.sldOutput34], 'cross_a' : ['/Mixer/Feature_Volume_4', self.sldCrossA], 'cross_b' : ['/Mixer/Feature_Volume_5', self.sldCrossB], } self.ComboControls={ 'line34source': ['/Mixer/Selector_1', self.comboMixSource], } def setComboMixSource(self,a0): self.setSelector('line34source', a0) def setVolumeIn12(self,a0): self.setVolume('in_line12', a0) def setVolumeIn34(self,a0): self.setVolume('in_line34', a0) def setVolumeInSPDIF(self,a0): self.setVolume('in_spdif', a0) def setVolumeOut12(self,a0): self.setVolume('out_line12', a0) def setVolumeOut34(self,a0): self.setVolume('out_line34', a0) def setCrossA(self,a0): self.setVolume('cross_a', a0) def setCrossB(self,a0): self.setVolume('cross_b', a0) def setVolume(self,a0,a1): name = a0 vol = -a1 log.debug("setting %s volume to %d" % (name, vol)) self.hw.setContignuous(self.VolumeControls[name][0], vol) def setSelector(self,a0,a1): name = a0 state = a1 log.debug("setting %s state to %d" % (name, state)) self.hw.setDiscrete(self.ComboControls[name][0], state) # verify state = self.hw.getDiscrete(self.ComboControls[name][0]) self.hw.setDiscrete(self.ComboControls[name][0], state) def initValues(self): for name, ctrl in self.VolumeControls.items(): vol = self.hw.getContignuous(ctrl[0]) log.debug("%s volume is %d" % (name , vol)) ctrl[1].setValue(-vol) for name, ctrl in self.ComboControls.items(): state = self.hw.getDiscrete(ctrl[0]) log.debug("%s state is %d" % (name , state)) ctrl[1].setCurrentIndex( state ) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/dummy.py0000644000175000001440000000211114206145246022135 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QWidget from ffado.import_pyqt import * from ffado.config import * class Dummy(QWidget): def __init__(self,parent = None): QWidget.__init__(self,parent) uicLoad("ffado/mixer/dummy", self) def initValues(self): pass # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/dummy.ui0000644000175000001440000000373414206145246022136 0ustar jwoitheusers Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. DummyMixerUI 0 0 309 290 Dummy Panel This panel is merely a placeholder for devices that don't have a mixer panel (yet). Qt::AlignVCenter true Qt::Horizontal QSizePolicy::Expanding 40 20 qPixmapFromMimeSource libffado-2.4.5/support/mixer-qt4/ffado/mixer/edirolfa101.ui0000644000175000001440000012032114206145246023002 0ustar jwoitheusers Copyright (C) 2005-2008 by Daniel Wagner This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. EdirolFa101ControlUI 0 0 681 193 Edirol FA-101 0 0 Monitor Input Mixer 1 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 0 0 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 16 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 2 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 3 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 4 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 5 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 0 80 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 6 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 7 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 0 80 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 8 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 9 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 0 80 -32767 0 10000 1000 -2 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 10 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32768 32512 Qt::Horizontal true qPixmapFromMimeSource sldInput1 sliderMoved(int) EdirolFa101ControlUI setVolumeIn1(int) 20 20 20 20 sldInput2 sliderMoved(int) EdirolFa101ControlUI setVolumeIn2(int) 20 20 20 20 sldInput3 sliderMoved(int) EdirolFa101ControlUI setVolumeIn3(int) 20 20 20 20 sldInput4 sliderMoved(int) EdirolFa101ControlUI setVolumeIn4(int) 20 20 20 20 sldInput5 sliderMoved(int) EdirolFa101ControlUI setVolumeIn5(int) 20 20 20 20 sldInput6 sliderMoved(int) EdirolFa101ControlUI setVolumeIn6(int) 20 20 20 20 sldInput7 sliderMoved(int) EdirolFa101ControlUI setVolumeIn7(int) 20 20 20 20 sldInput8 sliderMoved(int) EdirolFa101ControlUI setVolumeIn8(int) 20 20 20 20 sldInput9 sliderMoved(int) EdirolFa101ControlUI setVolumeIn9(int) 20 20 20 20 sldInput10 sliderMoved(int) EdirolFa101ControlUI setVolumeIn10(int) 20 20 20 20 sldBal1 sliderMoved(int) EdirolFa101ControlUI setBalanceIn1(int) 20 20 20 20 sldBal2 sliderMoved(int) EdirolFa101ControlUI setBalanceIn2(int) 20 20 20 20 sldBal3 sliderMoved(int) EdirolFa101ControlUI setBalanceIn3(int) 20 20 20 20 sldBal4 sliderMoved(int) EdirolFa101ControlUI setBalanceIn4(int) 20 20 20 20 sldBal5 sliderMoved(int) EdirolFa101ControlUI setBalanceIn5(int) 20 20 20 20 sldBal6 sliderMoved(int) EdirolFa101ControlUI setBalanceIn6(int) 20 20 20 20 sldBal7 sliderMoved(int) EdirolFa101ControlUI setBalanceIn7(int) 20 20 20 20 sldBal8 sliderMoved(int) EdirolFa101ControlUI setBalanceIn8(int) 20 20 20 20 sldBal9 sliderMoved(int) EdirolFa101ControlUI setBalanceIn9(int) 20 20 20 20 sldBal10 sliderMoved(int) EdirolFa101ControlUI setVolumeIn10(int) 20 20 20 20 libffado-2.4.5/support/mixer-qt4/ffado/mixer/edirolfa101control.py0000644000175000001440000001242414206145246024422 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Daniel Wagner # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QWidget from ffado.import_pyqt import * from ffado.config import * import logging log = logging.getLogger('edirolfa101') class EdirolFa101Control(QWidget): def __init__(self, parent = None): QWidget.__init__(self, parent) uicLoad("ffado/mixer/edirolfa101", self) def setVolumeIn1(self, vol): self.setValue('vol1', vol) def setVolumeIn2(self, vol): self.setValue('vol2', vol) def setVolumeIn3(self, vol): self.setValue('vol3', vol) def setVolumeIn4(self, vol): self.setValue('vol4', vol) def setVolumeIn5(self, vol): self.setValue('vol5', vol) def setVolumeIn6(self, vol): self.setValue('vol6', vol) def setVolumeIn7(self, vol): self.setValue('vol7', vol) def setVolumeIn8(self, vol): self.setValue('vol8', vol) def setVolumeIn9(self, vol): self.setValue('vol9', vol) def setVolumeIn10(self,vol): self.setValue('vol10', vol) def setBalanceIn1(self, bal): self.setValue('bal1', bal) def setBalanceIn2(self, bal): self.setValue('bal2', bal) def setBalanceIn3(self, bal): self.setValue('bal3', bal) def setBalanceIn4(self, bal): self.setValue('bal4', bal) def setBalanceIn5(self, bal): self.setValue('bal5', bal) def setBalanceIn6(self, bal): self.setValue('bal6', bal) def setBalanceIn7(self, bal): self.setValue('bal7', bal) def setBalanceIn8(self, bal): self.setValue('bal8', bal) def setBalanceIn9(self, bal): self.setValue('bal9', bal) def setBalanceIn10(self,bal): self.setValue('bal10', bal) def getIndexByName(self, name): index = int(name.lstrip('volba')) - 1 streamingMap = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] nonStreamingMap = [ 9, 10, 1, 2, 3, 4, 5, 6, 7, 8 ] if self.is_streaming: index = streamingMap[index] else: index = nonStreamingMap[index] return index def getWidgetByName(self, name): index = self.getIndexByName(name) widgetName = '' if name[0:3] == 'vol': widgetName = 'sldInput%d' % (index) else: widgetName = 'sldBal%d' % (index) return getattr(self, widgetName) def getFeatureByName(self, name): index = self.getIndexByName(name) featureName = '' if name[0:3] == 'vol': featureName = '/Mixer/Feature_Volume_%d' % ((index + 1) / 2) else: featureName = '/Mixer/Feature_LRBalance_%d' % ((index + 1) / 2) return featureName def getChannelIndexByName(self, name): index = self.getIndexByName(name) return ((index - 1) % 2) + 1 def setValue(self, name, val): log.debug("setting %s to %d" % (name, val)) self.updateStreamingState() feature = self.getFeatureByName(name) widget = self.getWidgetByName(name) channel = self.getChannelIndexByName(name) self.hw.setContignuous(feature, val, idx = channel) def updateStreamingState(self): ss = self.streamingstatus.selected() ss_txt = self.streamingstatus.getEnumLabel(ss) if ss_txt != 'Idle': self.is_streaming = True else: self.is_streaming = False def initValues(self): self.updateStreamingState() for i in range(1, 11): name = 'vol%d' % i feature = self.getFeatureByName(name) widget = self.getWidgetByName(name) channel = self.getChannelIndexByName(name) val = self.hw.getContignuous(feature, idx = channel) log.debug("%s value is %d" % (name , val)) widget.setValue(val) for i in range(1, 11): name = 'bal%d' % i feature = self.getFeatureByName(name) widget = self.getWidgetByName(name) channel = self.getChannelIndexByName(name) val = self.hw.getContignuous(feature, idx = channel) # Workaround: The current value is not properly initialized # on the device and returns after bootup always 0. # Though we happen to know what the correct value should # be therefore we overwrite the 0 if channel == 1: val = 32512 else: val = -32768 log.debug("%s value is %d" % (name , val)) widget.setValue(val) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/edirolfa66.ui0000644000175000001440000006105014206145246022737 0ustar jwoitheusers Copyright (C) 2005-2008 by Daniel Wagner This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. EdirolFa66ControlUI 0 0 421 193 Edirol FA-66 0 0 Monitor Input Mixer 1 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 0 0 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 16 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 2 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 3 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 4 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 5 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 0 80 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true Qt::Horizontal QSizePolicy::Expanding 40 20 6 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 0 10000 1000 0 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 -32767 32512 Qt::Horizontal true qPixmapFromMimeSource sldInput1 sliderMoved(int) EdirolFa66ControlUI setVolumeIn1(int) 20 20 20 20 sldInput2 sliderMoved(int) EdirolFa66ControlUI setVolumeIn2(int) 20 20 20 20 sldInput3 sliderMoved(int) EdirolFa66ControlUI setVolumeIn3(int) 20 20 20 20 sldInput4 sliderMoved(int) EdirolFa66ControlUI setVolumeIn4(int) 20 20 20 20 sldInput5 sliderMoved(int) EdirolFa66ControlUI setVolumeIn5(int) 20 20 20 20 sldInput6 sliderMoved(int) EdirolFa66ControlUI setVolumeIn6(int) 20 20 20 20 sldBal1 sliderMoved(int) EdirolFa66ControlUI setBalanceIn1(int) 20 20 20 20 sldBal2 sliderMoved(int) EdirolFa66ControlUI setBalanceIn2(int) 20 20 20 20 sldBal3 sliderMoved(int) EdirolFa66ControlUI setBalanceIn3(int) 20 20 20 20 sldBal4 sliderMoved(int) EdirolFa66ControlUI setBalanceIn4(int) 20 20 20 20 sldBal5 sliderMoved(int) EdirolFa66ControlUI setBalanceIn5(int) 20 20 20 20 sldBal6 sliderMoved(int) EdirolFa66ControlUI setBalanceIn6(int) 20 20 20 20 libffado-2.4.5/support/mixer-qt4/ffado/mixer/edirolfa66control.py0000644000175000001440000000730714206145246024360 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Daniel Wagner # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QWidget from ffado.import_pyqt import * from ffado.config import * import logging log = logging.getLogger('edirolfa66') class EdirolFa66Control(QWidget): def __init__(self, parent = None): QWidget.__init__(self, parent) uicLoad("ffado/mixer/edirolfa66", self) self.VolumeControls = { # feature name, channel, qt slider 'vol1' : ['/Mixer/Feature_Volume_1', 1, self.sldInput1], 'vol2' : ['/Mixer/Feature_Volume_1', 2, self.sldInput2], 'vol3' : ['/Mixer/Feature_Volume_2', 1, self.sldInput3], 'vol4' : ['/Mixer/Feature_Volume_2', 2, self.sldInput4], 'vol5' : ['/Mixer/Feature_Volume_3', 1, self.sldInput5], 'vol6' : ['/Mixer/Feature_Volume_3', 2, self.sldInput6], 'bal1' : ['/Mixer/Feature_LRBalance_1', 1, self.sldBal1], 'bal2' : ['/Mixer/Feature_LRBalance_1', 2, self.sldBal2], 'bal3' : ['/Mixer/Feature_LRBalance_2', 1, self.sldBal3], 'bal4' : ['/Mixer/Feature_LRBalance_2', 2, self.sldBal4], 'bal5' : ['/Mixer/Feature_LRBalance_3', 1, self.sldBal5], 'bal6' : ['/Mixer/Feature_LRBalance_3', 2, self.sldBal6], } def setVolumeIn1(self, vol): self.setValue('vol1', vol) def setVolumeIn2(self, vol): self.setValue('vol2', vol) def setVolumeIn3(self, vol): self.setValue('vol3', vol) def setVolumeIn4(self, vol): self.setValue('vol4', vol) def setVolumeIn5(self, vol): self.setValue('vol5', vol) def setVolumeIn6(self, vol): self.setValue('vol6', vol) def setBalanceIn1(self, bal): self.setValue('bal1', bal) def setBalanceIn2(self, bal): self.setValue('bal2', bal) def setBalanceIn3(self, bal): self.setValue('bal3', bal) def setBalanceIn4(self, bal): self.setValue('bal4', bal) def setBalanceIn5(self, bal): self.setValue('bal5', bal) def setBalanceIn6(self, bal): self.setValue('bal6', bal) def setValue(self, name, val): ctrl = self.VolumeControls[name] log.debug("setting %s to %d" % (name, val)) self.hw.setContignuous(ctrl[0], val, idx = ctrl[1]) def initValues(self): for name, ctrl in self.VolumeControls.items(): val = self.hw.getContignuous(ctrl[0], idx = ctrl[1]) log.debug("%s value is %d" % (name , val)) # Workaround: The current value is not properly initialized # on the device and returns after bootup always 0. # Though we happen to know what the correct value should # be therefore we overwrite the 0 if name[0:3] == 'bal' and val == 0: if ctrl[1] == 1: val = 32512 else: val = -32768 ctrl[2].setValue(val) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/generic_dice_eap.py0000644000175000001440000001265014206145246024240 0ustar jwoitheusers# # Copyright (C) 2009-2010 by Arnold Krille # 2013 by Philippe Carriere # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4 import QtGui, QtCore, Qt # from PyQt4.QtGui import QWidget, QGridLayout, QTabWidget, QScrollArea from ffado.import_pyqt import * import dbus from ffado.widgets.matrixmixer import MatrixMixer from ffado.widgets.crossbarrouter import * from ffado.config import * class Generic_Dice_EAP(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.layout = QGridLayout(self) self.setLayout(self.layout) self.tabs = QTabWidget(self) self.tabs.setTabPosition(QTabWidget.West) self.layout.addWidget(self.tabs) def buildMixer(self): #print( self.hw ) #print( self.hw.getText("/Generic/Nickname") ) self.mixer = MatrixMixer(self.hw.servername, self.hw.basepath+"/EAP/MatrixMixer", self, "Columns_are_outputs", -1, None, None, False, QTabWidget.North, QTabWidget.Rounded) self.tabs.addTab(self.mixer, "Mixer") self.router_scrollarea = self.buildRouter(self.hw.servername, self.hw.basepath+"/EAP/Router") self.tabs.addTab(self.router_scrollarea, "Crossbar Router") def buildRouter(self, servername, path): self.router = CrossbarRouter(servername, path, self) self.router.MixerRoutingChanged.connect(self.mixer.updateRouting) scrollarea = QScrollArea(self.tabs) scrollarea.setWidgetResizable(True) scrollarea.setWidget(self.router) return scrollarea def onSamplerateChange(self): # Router configuration is samplerate dependent for DICE EAP devices # Destroy and redraw the crossbar router view when called n = self.tabs.indexOf(self.router_scrollarea) self.tabs.removeTab(n) self.router.destroy() self.router_scrollarea.destroy() self.router_scrollarea = self.buildRouter(self.hw.servername, self.hw.basepath+"/EAP/Router") self.tabs.insertTab(n, self.router_scrollarea, "Crossbar Router") self.tabs.setCurrentWidget(self.router_scrollarea) self.mixer.updateRouting() def saveSettings(self, indent): saveString = [] idt = indent + " " saveString.append('%s\n' % indent) saveString.extend(self.mixer.saveSettings(idt)) # Do not forget to mention the adopted rule for matrix columns mixer # This might be useful for future import function saveString.append("%s \n" % indent) saveString.append("%s Columns_are_outputs\n" % indent) saveString.append("%s \n" % indent) saveString.append('%s\n' % indent) saveString.append('%s\n' % indent) saveString.extend(self.router.saveSettings(idt)) saveString.append('%s\n' % indent) return saveString def readSettings(self, readString): try: idxb = readString.index('') idxe = readString.index('') except Exception: log.debug("No mixer settings found") idxb = -1 idxe = -1 if idxb >= 0: if idxe > idxb + 1: stringMixer = [] for s in readString[idxb+1:idxe]: stringMixer.append(s) # When trying to import from a different device, the rule for column interpretation is # not necessarily the same try: idx = stringMixer.index('') except Exception: log.debug('Do not know how to handle column versus input/output') idx = -1 transpose_coeff = False if idx >=0: if stringMixer[idx+1].find("Columns_are_outputs") == -1: log.debug('Transposing the matrix coefficient; you are importing, are not you ?') transpose_coeff = True if self.mixer.readSettings(stringMixer, transpose_coeff): log.debug("Mixer settings modified") del stringMixer try: idxb = readString.index('') idxe = readString.index('') except Exception: log.debug("No router settings found") idxb = -1 idxe = -1 if idxb >= 0: if idxe > idxb + 1: stringRouter = [] for s in readString[idxb+1:idxe]: stringRouter.append(s) if self.router.readSettings(stringRouter): log.debug("Router settings modified") del stringRouter #def getDisplayTitle(self): # return "Saffire PRO40/PRO24 Mixer" # # vim: et ts=4 sw=4 libffado-2.4.5/support/mixer-qt4/ffado/mixer/globalmixer.py0000644000175000001440000002453414206145246023324 0ustar jwoitheusers# # Copyright (C) 2008 by Arnold Krille # 2013 by Philippe Carriere # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtCore import QObject, pyqtSlot # from PyQt4.QtGui import QWidget, QMessageBox from ffado.import_pyqt import * from ffado.config import * import logging log = logging.getLogger('global') class GlobalMixer(QWidget): def __init__(self, parent, name=None): QWidget.__init__(self, parent) uicLoad("ffado/mixer/globalmixer", self) self.setName(name) def setName(self,name): if name is not None: self.lblName.setText(name) self.lblName.show() else: self.lblName.hide() @pyqtSlot(int) def on_clocksource_activated( self, clock ): #log.debug("updateClockSource( " + str(clock) + " )") if self.clockselect.canChangeValue(): self.clockselect.select( clock ) else: msg = QMessageBox() msg.question( msg, "Error", \ "Clock source change not permitted. Is streaming active?", \ QMessageBox.Ok ) self.clocksource.setEnabled(False) return selected = self.clockselect.selected() if selected != clock: clockname = self.clockselect.getEnumLabel( clock ) msg = QMessageBox() msg.question( msg, "Failed to select clock source", \ "Could not select %s as clock source." % clockname, \ QMessageBox.Ok ) self.clocksource.setCurrentIndex( selected ) @pyqtSlot(int) def on_samplerate_activated( self, sr ): log.debug("on_samplerate_activated( " + str(sr) + " )") # If there's no clock, don't bother trying to set the sample rate if (self.no_clock == 1): return if self.samplerateselect.canChangeValue(): self.samplerateselect.select( sr ) if 'onSamplerateChange' in dir(self): self.onSamplerateChange() log.debug("Mixer configuration updated") else: msg = QMessageBox() msg.question( msg, "Error", \ "Sample rate change not permitted. Is streaming active?", \ QMessageBox.Ok ) self.samplerate.setEnabled(False) return selected = self.samplerateselect.selected() if selected != sr: srname = self.samplerateselect.getEnumLabel( sr ) msg = QMessageBox() msg.question( msg, "Failed to select sample rate", \ "Could not select %s as samplerate." % srname, \ QMessageBox.Ok ) self.samplerate.setCurrentIndex( selected ) @pyqtSlot() def on_txtNickname_returnPressed( self ): if self.nickname.canChangeValue(): asciiData = self.txtNickname.text().toAscii() self.nickname.setText( asciiData.data() ) else: self.txtNickname.setText( self.nickname.text() ) def refreshSampleRates( self ): no_clock_status = 0 n_rates = self.samplerateselect.count() if (n_rates<1 or self.samplerateselect.getEnumLabel(0)=="0"): no_clock_status = 1; # Except for changes to the "no clock" status (where the number of # "frequencies" can remain at 1), the following test won't account # for cases where the frequency list changes but the total number of # frequencies remains the same. If a device comes along for which # this is a problem, an alternative approach will be needed. if (no_clock_status!=self.no_clock or n_rates!=self.num_rates): self.no_clock = 0; self.num_rates = n_rates self.samplerate.clear() for i in range( self.num_rates ): label = self.samplerateselect.getEnumLabel( i ) if (label == "0"): label = "No clock found"; self.no_clock = 1; self.samplerate.insertItem( self.num_rates, label ) if (self.no_clock != 1): self.samplerate.setCurrentIndex( self.samplerateselect.selected() ) else: self.samplerate.setCurrentIndex(0); def initValues( self ): #print( "GlobalMixer::initValues()" ) nb_clocks = self.clockselect.count() for i in range( nb_clocks ): self.clocksource.insertItem( nb_clocks, self.clockselect.getEnumLabel( i ) ) self.clocksource.setCurrentIndex( self.clockselect.selected() ) self.no_clock = 0; self.num_rates = -1; self.refreshSampleRates(); self.txtNickname.setText( self.nickname.text() ) self.samplerate.setEnabled(self.samplerateselect.canChangeValue()) self.clocksource.setEnabled(self.clockselect.canChangeValue()) if self.nickname.canChangeValue(): self.txtNickname.setEnabled(True) else: self.txtNickname.setEnabled(False) self.streaming_status = self.streamingstatus.selected() def polledUpdate(self): self.samplerate.setEnabled(self.samplerateselect.canChangeValue()) self.clocksource.setEnabled(self.clockselect.canChangeValue()) self.txtNickname.setEnabled(self.nickname.canChangeValue()) ss = self.streamingstatus.selected() ss_txt = self.streamingstatus.getEnumLabel(ss) if ss_txt == 'Idle': self.chkStreamIn.setChecked(False) self.chkStreamOut.setChecked(False) elif ss_txt == 'Sending': self.chkStreamIn.setChecked(False) self.chkStreamOut.setChecked(True) elif ss_txt == 'Receiving': self.chkStreamIn.setChecked(True) self.chkStreamOut.setChecked(False) elif ss_txt == 'Both': self.chkStreamIn.setChecked(True) self.chkStreamOut.setChecked(True) if (ss!=self.streaming_status and ss_txt!='Idle'): sr = self.samplerate.currentIndex() self.samplerate.setCurrentIndex( self.samplerateselect.selected() ) # Check (and update) if device configuration needs to be updated if ( self.samplerateselect.devConfigChanged(sr) ): log.debug("Samplerate modified by external client") if 'onSamplerateChange' in dir(self): self.onSamplerateChange() log.debug("Mixer configuration updated ") else: log.debug("Mixer configuration need to be updated but no means is provided") self.streaming_status = ss # Allow for devices whose sample rates can change dynamically (for # example, in response to changes in external clock frequency) self.refreshSampleRates(); def saveSettings(self, indent): saveString = [] saveString.append('%s\n' % indent) saveString.append('%s ' % indent + str(self.txtNickname.text()) + '\n') saveString.append('%s\n' % indent) saveString.append('%s\n' % indent) saveString.append('%s ' % indent + str(self.clockselect.getEnumLabel(self.clockselect.selected())) + '\n') saveString.append('%s\n' % indent) saveString.append('%s\n' % indent) saveString.append('%s ' % indent + str(self.samplerateselect.getEnumLabel(self.samplerateselect.selected())) + ' \n') saveString.append('%s\n' % indent) return saveString def readSettings(self, readString): # Nickname try: idx = readString.index('') except Exception: print( "No nickname found" ) idx = -1 if idx >= 0: nickname = readString[idx+1] if self.nickname.canChangeValue(): self.txtNickname.setText(nickname) self.on_txtNickname_returnPressed() log.debug("Nickname changed for %s" % nickname) # Clock try: idx = readString.index('') except Exception: print( "No clock found" ) idx = -1 if idx >= 0: clock = readString[idx+1] nb_clocks = self.clockselect.count() clockLabel = [] for i in range( nb_clocks ): clockLabel.append(self.clockselect.getEnumLabel(i)) try: idxclock = clockLabel.index(clock) except Exception: print( "No %s clock found" % clock ) idxclock = -1 if idxclock >= 0: self.on_clocksource_activated(idxclock) self.clocksource.setCurrentIndex(self.clockselect.selected()) log.debug("Clock set to index %d (%s)" % (idxclock, clock)) del clockLabel # Samplerate try: idx = readString.index('') except Exception: print( "Samplerate not found" ) idx = -1 if idx >= 0: samplerate = readString[idx+1] nb_srate = self.samplerateselect.count() srateLabel = [] for i in range( nb_srate ): srateLabel.append(self.samplerateselect.getEnumLabel(i)) try: idxsrate = srateLabel.index(samplerate) except Exception: print( "No %s samplerate found" % samplerate ) idxsrate = -1 if idxsrate >= 0: self.on_samplerate_activated(idxsrate) self.samplerate.setCurrentIndex(self.samplerateselect.selected()) log.debug("Samplerate set to index %d (%s)" % (idxsrate, samplerate)) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/globalmixer.ui0000644000175000001440000001163714206145246023311 0ustar jwoitheusers Copyright (C) 2008 by Arnold Krille This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. GlobalMixerUi 0 0 591 79 0 0 Global Mixer Options 75 true TextLabel Clock Source: false clocksource 1 0 Stream Status Qt::AlignCenter Qt::Horizontal QSizePolicy::Expanding 330 10 Nickname: false txtNickname 100 0 Sample Rate: false samplerate true 1 0 false Qt::RightToLeft Outgoing false Qt::RightToLeft Incoming qPixmapFromMimeSource libffado-2.4.5/support/mixer-qt4/ffado/mixer/mackieonyx.py0000644000175000001440000000212314206145246023154 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QWidget from ffado.import_pyqt import * from ffado.config import * class MackieOnyx(QWidget): def __init__(self,parent = None): QWidget.__init__(self,parent) uicLoad("ffado/mixer/mackieonyx", self) def initValues(self): pass # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/mackieonyx.ui0000644000175000001440000000304414206145246023144 0ustar jwoitheusers Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. MackieOnyxMixerUI 0 0 166 238 Mackie Onyx Mixer Control No additional controls are available for the Mackie Onyx Mixer Qt::AlignVCenter true qPixmapFromMimeSource libffado-2.4.5/support/mixer-qt4/ffado/mixer/maudio_bebob.py0000644000175000001440000006030514206145246023422 0ustar jwoitheusers# # Copyright (c) 2013 by Takashi Sakamoto # Copyright (C) 2005-2008 by Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtCore import Qt # from PyQt4.QtGui import QSizePolicy, QHBoxLayout, QVBoxLayout, QGroupBox # from PyQt4.QtGui import QWidget, QTabWidget, QLabel, QSlider, QToolButton from ffado.import_pyqt import * from math import log10 from ffado.config import * import logging log = logging.getLogger('MAudioBeBoB') class MAudio_BeBoB_Input_Widget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) uicLoad("ffado/mixer/maudio_bebob_input", self) class MAudio_BeBoB_Output_Widget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) uicLoad("ffado/mixer/maudio_bebob_output", self) class MAudio_BeBoB(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) info = { 0x0000000a: (0, "Ozonic"), 0x00010062: (1, "FireWire Solo"), 0x00010060: (2, "FireWire Audiophile"), 0x00010046: (3, "FireWire 410"), 0x00010071: (4, "FireWire 1814"), 0x00010091: (4, "ProjectMix I/O"), } labels = ( { "inputs": ("Analog 1/2", "Analog 3/4", "Stream 1/2", "Stream 3/4"), "mixers": ("Mixer 1/2", "Mixer 3/4"), "outputs": ("Analog 1/2", "Analog 3/4") }, { "inputs": ("Analog 1/2", "Digital 1/2", "Stream 1/2", "Stream 3/4"), "mixers": ("Mixer 1/2", "Mixer 3/4"), "outputs": ("Analog 1/2", "Digital 1/2") }, { "inputs": ("Analog 1/2", "Digital 1/2", "Stream 1/2", "Stream 3/4", "Stream 5/6"), "mixers": ("Mixer 1/2", "Mixer 3/4", "Mixer 5/6", "Aux 1/2"), "outputs": ("Analog 1/2", "Analog 3/4", "Digital 1/2", "Headphone 1/2") }, { "inputs": ("Analog 1/2", "Digital 1/2", "Stream 1/2", "Stream 3/4", "Stream 5/6", "Stream 7/8", "Stream 9/10"), "mixers": ("Mixer 1/2", "Mixer 3/4", "Mixer 5/6", "Mixer 7/8", "Mixer 9/10", "Aux 1/2"), "outputs": ("Analog 1/2", "Analog 3/4", "Analog 5/6", "Analog 7/8", "Digital 1/2", "Headphone 1/2") }, { "inputs": ("Analog 1/2", "Analog 3/4", "Analog 5/6", "Analog 7/8", "Stream 1/2", "Stream 3/4", "S/PDIF 1/2", "ADAT 1/2", "ADAT 3/4", "ADAT 5/6", "ADAT 7/8"), "mixers": ("Mixer 1/2", "Mixer 3/4", "Aux 1/2"), "outputs": ("Analog 1/2", "Analog 3/4", "Headphone 1/2", "Headphone 3/4") } ) # hardware inputs and stream playbacks # format: function_id/channel_idx/panning-able # NOTE: function_id = channel_idx = panning-able = labels["inputs"] inputs = ( ( (0x03, 0x04, 0x01, 0x02), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)), (True, True, False, False) ), ( (0x01, 0x02, 0x04, 0x03), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)), (True, True, False, False) ), ( (0x04, 0x05, 0x01, 0x02, 0x03), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)), (True, True, False, False, False) ), ( (0x03, 0x04, 0x02, 0x01, 0x01, 0x01, 0x01), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x03, 0x04), (0x05, 0x06), (0x07, 0x08)), (True, True, False, False, False, False, False) ), ( (0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x05, 0x06, 0x07, 0x08, 0x09), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)), (True, True, True, True, False, False, True, True, True, True, True) ) ) # jack sources except for headphone # format: function_id/source id # NOTE: "function_id" = labels["output"] - "Headphone 1/2/3/4" # NOTE: "source_id" = labels["mixer"] jack_src = ( None, None, ((0x01, 0x02, 0x03), (0x00, 0x00, 0x00, 0x01)), ((0x02, 0x03, 0x04, 0x05, 0x06), (0x00, 0x00, 0x00, 0x00, 0x00, 0x01)), ((0x03, 0x04), (0x00, 0x01, 0x02)) ) # headphone sources # format: sink id/source id # NOTE: "source_id" = labels["mixer"] hp_src = ( None, None, ((0x04,), (0x00, 0x01, 0x02, 0x03)), ((0x07,), (0x02, 0x03, 0x04, 0x05, 0x06, 0x07)), ((0x01, 0x02), (0x01, 0x02, 0x04)) ) # hardware outputs # format: function id # NOTE: "function_id" = labels["output"] outputs = ( (0x05, 0x06), (0x02, 0x03), (0x0c, 0x0d, 0x0e, 0x0f), (0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f), (0x0c, 0x0d, 0x0f, 0x10) ) # Mixer inputs/outputs # format: function_id/out_stereo_channel_id/in_id/in_stereo_channel_id # NOTE: function_id = out_stereo_channel_id = labels["mixers"] # NOTE: in_id = in_stereo_channel_id = labels["inputs"] mixers = ( ( (0x01, 0x02), ((0x01, 0x02), (0x01, 0x02)), (0x02, 0x03, 0x00, 0x01), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)) ), ( (0x01, 0x01), ((0x01, 0x02), (0x03, 0x04)), (0x00, 0x01, 0x03, 0x02), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)) ), ( (0x01, 0x02, 0x03, 0x04), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)), (0x03, 0x04, 0x00, 0x01, 0x02), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)) ), ( (0x01, 0x01, 0x01, 0x01, 0x01, 0x07), ((0x01, 0x02), (0x03, 0x04), (0x05, 0x06), (0x07, 0x08), (0x09, 0x0a), (0x01, 0x02)), (0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x03, 0x04), (0x05, 0x06), (0x07, 0x08)) ), ( (0x01, 0x02, 0x03), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02)), (0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x04, 0x04), ((0x01, 0x02), (0x03, 0x04), (0x05, 0x06), (0x07, 0x08), (0x01, 0x02), (0x03, 0x04), (0x01, 0x02), (0x01, 0x02), (0x03, 0x04), (0x05, 0x06), (0x07, 0x08)) ) ) # Aux mixer inputs/outputs # format: function_id/input_id/input_stereo_channel_id # NOTE: input_id = labels["inputs"] aux = ( None, None, ( 0x0b, (0x09, 0x0a, 0x06, 0x07, 0x08), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)) ), ( 0x09, (0x07, 0x08, 0x06, 0x05, 0x05, 0x05, 0x05), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x03, 0x04), (0x05, 0x06), (0x07, 0x08)) ), ( 0x0e, (0x13, 0x14, 0x15, 0x16, 0x11, 0x12, 0x17, 0x18, 0x19, 0x1a, 0x1b), ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)) ) ) def getDisplayTitle(self): model = self.configrom.getModelId() return self.info[model][1] def buildMixer(self): self.Selectors = {} self.Pannings = {} self.Volumes = {} self.Mutes = {} self.Mixers = {} self.FW410HP = 0 model = self.configrom.getModelId() if model not in self.info: return self.id = self.info[model][0] tabs_layout = QHBoxLayout(self) tabs = QTabWidget(self) self.addInputTab(tabs) self.addMixTab(tabs) if self.aux[self.id] is not None: self.addAuxTab(tabs) self.addOutputTab(tabs) tabs_layout.addWidget(tabs) def addInputTab(self, tabs): tab_input = QWidget(self) tabs.addTab(tab_input, "In") tab_input_layout = QHBoxLayout() tab_input.setLayout(tab_input_layout) in_labels = self.labels[self.id]["inputs"] in_ids = self.inputs[self.id][0] in_pan = self.inputs[self.id][2] for i in range(len(in_ids)): l_idx = self.inputs[self.id][1][i][0] r_idx = self.inputs[self.id][1][i][1] widget = MAudio_BeBoB_Input_Widget(tab_input) tab_input_layout.addWidget(widget) widget.name.setText(in_labels[i]) self.Volumes[widget.l_sld] = ( "/Mixer/Feature_Volume_%d" % in_ids[i], l_idx, widget.r_sld, r_idx, widget.link ) self.Volumes[widget.r_sld] = ( "/Mixer/Feature_Volume_%d" % in_ids[i], r_idx, widget.l_sld, l_idx, widget.link ) self.Mutes[widget.mute] = (widget.l_sld, widget.r_sld) if not in_pan[i]: widget.l_pan.setDisabled(True) widget.r_pan.setDisabled(True) else: self.Pannings[widget.l_pan] = ( "/Mixer/Feature_LRBalance_%d" % in_ids[i], l_idx ) self.Pannings[widget.r_pan] = ( "/Mixer/Feature_LRBalance_%d" % in_ids[i], r_idx ) tab_input_layout.addStretch() def addMixTab(self, tabs): tab_mix = QWidget(self) tabs.addTab(tab_mix, "Mix") tab_layout = QHBoxLayout() tab_mix.setLayout(tab_layout) in_labels = self.labels[self.id]["inputs"] in_idxs = self.inputs[self.id][0] mix_labels = self.labels[self.id]["mixers"] mix_idxs = self.mixers[self.id][0] for i in range(len(mix_idxs)): if mix_labels[i] == 'Aux 1/2': continue grp = QGroupBox(tab_mix) grp_layout = QVBoxLayout() grp.setLayout(grp_layout) tab_layout.addWidget(grp) label = QLabel(grp) grp_layout.addWidget(label) label.setText(mix_labels[i]) label.setAlignment(Qt.AlignCenter) label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) for j in range(len(in_idxs)): mix_in_id = self.mixers[self.id][2][j] button = QToolButton(grp) grp_layout.addWidget(button) button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) button.setText('%s In' % in_labels[j]) button.setCheckable(True) self.Mixers[button] = ( "/Mixer/EnhancedMixer_%d" % mix_idxs[i], mix_in_id, j, i ) grp_layout.addStretch() tab_layout.addStretch() def addAuxTab(self, tabs): #local functions def addLinkButton(parent, layout): button = QToolButton(parent) layout.addWidget(button) button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) button.setText('Link') button.setCheckable(True) return button def addMuteButton(parent, layout): button = QToolButton(parent) layout.addWidget(button) button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) button.setText('Mute') button.setCheckable(True) return button # local processing tab_aux = QWidget(self) tabs.addTab(tab_aux, "Aux") layout = QHBoxLayout() tab_aux.setLayout(layout) aux_label = self.labels[self.id]["mixers"][-1] aux_id = self.aux[self.id][0] aux_in_labels = self.labels[self.id]["inputs"] aux_in_ids = self.aux[self.id][1] for i in range(len(aux_in_ids)): in_ch_l = self.aux[self.id][2][i][0] in_ch_r = self.aux[self.id][2][i][1] grp = QGroupBox(tab_aux) grp_layout = QVBoxLayout() grp.setLayout(grp_layout) layout.addWidget(grp) label = QLabel(grp) grp_layout.addWidget(label) label.setText("%s\nIn" % aux_in_labels[i]) label.setAlignment(Qt.AlignCenter) grp_sld = QGroupBox(grp) grp_sld_layout = QHBoxLayout() grp_sld.setLayout(grp_sld_layout) grp_layout.addWidget(grp_sld) l_sld = QSlider(grp_sld) grp_sld_layout.addWidget(l_sld) r_sld = QSlider(grp_sld) grp_sld_layout.addWidget(r_sld) button = addLinkButton(grp, grp_layout) self.Volumes[l_sld] = ( "/Mixer/Feature_Volume_%d" % aux_in_ids[i], in_ch_l, r_sld, in_ch_r, button ) self.Volumes[r_sld] = ( "/Mixer/Feature_Volume_%d" % aux_in_ids[i], in_ch_r, l_sld, in_ch_l, button ) button = addMuteButton(grp, grp_layout) self.Mutes[button] = (l_sld, r_sld) grp = QGroupBox(tab_aux) grp_layout = QVBoxLayout() grp.setLayout(grp_layout) layout.addWidget(grp) label = QLabel(grp) grp_layout.addWidget(label) label.setText("%s\nOut" % aux_label) label.setAlignment(Qt.AlignCenter) grp_sld = QGroupBox(grp) grp_sld_layout = QHBoxLayout() grp_sld.setLayout(grp_sld_layout) grp_layout.addWidget(grp_sld) l_sld = QSlider(grp_sld) grp_sld_layout.addWidget(l_sld) r_sld = QSlider(grp_sld) grp_sld_layout.addWidget(r_sld) button = addLinkButton(grp, grp_layout) self.Volumes[l_sld] = ( "/Mixer/Feature_Volume_%d" % aux_id, 1, r_sld, 2, button ) self.Volumes[r_sld] = ( "/Mixer/Feature_Volume_%d" % aux_id, 2, l_sld, 1, button ) button = addMuteButton(grp, grp_layout) self.Mutes[button] = (l_sld, r_sld) layout.addStretch() def addOutputTab(self, tabs): tab_out = QWidget(self) tabs.addTab(tab_out, "Out") layout = QHBoxLayout() tab_out.setLayout(layout) out_labels = self.labels[self.id]["outputs"] out_ids = self.outputs[self.id] mixer_labels = self.labels[self.id]["mixers"] if self.jack_src[self.id] is None: for i in range(len(out_ids)): label = QLabel(tab_out) layout.addWidget(label) label.setText("%s Out is fixed to %s Out" % (mixer_labels[i], out_labels[i])) return mixer_ids = self.jack_src[self.id][1] for i in range(len(out_ids)): out_label = out_labels[i] if out_label.find('Headphone') >= 0: continue out_id = self.jack_src[self.id][0][i] widget = MAudio_BeBoB_Output_Widget(tab_out) layout.addWidget(widget) widget.name.setText(out_label) self.Volumes[widget.l_sld] = ( "/Mixer/Feature_Volume_%d" % out_ids[i], 1, widget.r_sld, 2, widget.link ) self.Volumes[widget.r_sld] = ( "/Mixer/Feature_Volume_%d" % out_ids[i], 2, widget.l_sld, 1, widget.link ) self.Mutes[widget.mute] = (widget.l_sld, widget.r_sld) self.Selectors[widget.cmb_src] = ("/Mixer/Selector_%d" % out_id, ) for j in range(len(mixer_ids)): if i != j and j != len(mixer_ids) - 1: continue widget.cmb_src.addItem("%s Out" % mixer_labels[j], mixer_ids[j]) # add headphone hp_idx = 0 for i in range(len(out_ids)): out_label = out_labels[i] if out_label.find('Headphone') < 0: continue hp_label = self.labels[self.id]["outputs"][i] hp_id = self.hp_src[self.id][0][hp_idx] hp_idx += 1 mixer_labels = self.labels[self.id]["mixers"] widget = MAudio_BeBoB_Output_Widget(tab_out) layout.addWidget(widget) widget.name.setText(hp_label) mixer_labels = self.labels[self.id]["mixers"] mixer_ids = self.mixers[self.id][0] self.Volumes[widget.l_sld] = ( "/Mixer/Feature_Volume_%d" % out_ids[i], 1, widget.r_sld, 2, widget.link ) self.Volumes[widget.r_sld] = ( "/Mixer/Feature_Volume_%d" % out_ids[i], 2, widget.l_sld, 1, widget.link ) self.Mutes[widget.mute] = (widget.l_sld, widget.r_sld) for i in range(len(mixer_ids)): widget.cmb_src.addItem("%s Out" % mixer_labels[i], mixer_ids[i]) if self.id != 3: self.Selectors[widget.cmb_src] = ("/Mixer/Selector_%d" % hp_id, ) else: widget.cmb_src.activated.connect(self.update410HP) self.FW410HP = widget.cmb_src layout.addStretch() def initValues(self): for ctl, params in list(self.Selectors.items()): path = params[0] state = self.hw.getDiscrete(path) ctl.setCurrentIndex(state) ctl.activated.connect(self.updateSelector) # Right - Center - Left # 0x8000 - 0x0000 - 0x0001 - 0x7FFE # ..., -1, 0, +1, ... for ctl, params in list(self.Pannings.items()): path = params[0] idx = params[1] curr = self.hw.getContignuous(path, idx) state = -(curr / 0x7FFE) * 50 + 50 ctl.setValue(state) ctl.valueChanged.connect(self.updatePanning) for ctl, params in list(self.Volumes.items()): path = params[0] idx = params[1] p_idx = params[3] link = params[4] db = self.hw.getContignuous(path, idx) vol = self.db2vol(db) ctl.setValue(vol) ctl.valueChanged.connect(self.updateVolume) # to activate link button, a pair is checked twice, sign... pair_db = self.hw.getContignuous(path, p_idx) if pair_db == db: link.setChecked(True) for ctl, params in list(self.Mutes.items()): ctl.clicked.connect(self.updateMute) for ctl, params in list(self.Mixers.items()): path = params[0] in_id = params[1] mix_in_idx = params[2] mix_out_idx = params[3] in_ch_l = self.mixers[self.id][3][mix_in_idx][0] out_ch_l = self.mixers[self.id][1][mix_out_idx][0] # see /libffado/src/bebob/bebob_mixer.cpp mux_id = self.getMultiplexedId(in_id, in_ch_l, out_ch_l) curr = self.hw.getContignuous(path, mux_id) if curr == 0: state = True else: state = False ctl.setChecked(state) ctl.clicked.connect(self.updateMixer) if self.id == 3: self.read410HP() # helper functions def vol2db(self, vol): return (log10(vol + 1) - 2) * 16384 def db2vol(self, db): return pow(10, db / 16384 + 2) - 1 def getMultiplexedId(self, in_id, in_ch_l, out_ch_l): # see /libffado/src/bebob/bebob_mixer.cpp return (in_id << 8) | (in_ch_l << 4) | (out_ch_l << 0) def updateSelector(self, state): sender = self.sender() path = self.Selectors[sender][0] log.debug("set %s to %d" % (path, state)) self.hw.setDiscrete(path, state) def updatePanning(self, state): sender = self.sender() path = self.Pannings[sender][0] idx = self.Pannings[sender][1] value = (state - 50) * 0x7FFE / -50 log.debug("set %s for %d to %d(%d)" % (path, idx, value, state)) self.hw.setContignuous(path, value, idx) def updateVolume(self, vol): sender = self.sender() path = self.Volumes[sender][0] idx = self.Volumes[sender][1] pair = self.Volumes[sender][2] link = self.Volumes[sender][4] db = self.vol2db(vol) log.debug("set %s for %d to %d(%d)" % (path, idx, db, vol)) self.hw.setContignuous(path, db, idx) if link.isChecked(): pair.setValue(vol) # device remeber gain even if muted def updateMute(self, state): sender = self.sender() l_sld = self.Mutes[sender][0] r_sld = self.Mutes[sender][1] if state: db = 0x8000 else: db = 0x0000 for w in [l_sld, r_sld]: path = self.Volumes[w][0] self.hw.setContignuous(path, db) w.setDisabled(state) def updateMixer(self, checked): if checked: state = 0x0000 else: state = 0x8000 sender = self.sender() path = self.Mixers[sender][0] in_id = self.Mixers[sender][1] mix_in_idx = self.Mixers[sender][2] mix_out_idx = self.Mixers[sender][3] in_ch_l = self.mixers[self.id][3][mix_in_idx][0] out_ch_l = self.mixers[self.id][1][mix_out_idx][0] mux_id = self.getMultiplexedId(in_id, in_ch_l, out_ch_l) log.debug("set %s for 0x%04X(%d/%d/%d) to %d" % (path, mux_id, in_id, in_ch_l, out_ch_l, state)) self.hw.setContignuous(path, state, mux_id) def read410HP(self): path = "/Mixer/Selector_7" sel = self.hw.getDiscrete(path) if sel > 0: enbl = 5 else: enbl = -1 path = "/Mixer/EnhancedMixer_7" for i in range(5): in_id = 0 in_ch_l = i * 2 + 1 out_ch_l = 1 mux_id = self.getMultiplexedId(in_id, in_ch_l, out_ch_l) state = self.hw.getContignuous(path, mux_id) if enbl < 0 and state == 0: enbl = i else: self.hw.setContignuous(path, 0x8000, mux_id) # if inconsistency between Selector and Mixer, set AUX as default if enbl == -1: self.hw.setDiscrete('/Mixer/Selector_7', 1) enbl = 5 self.FW410HP.setCurrentIndex(enbl) def update410HP(self, state): # each output from mixer can be multiplexed in headphone # but here they are exclusive because of GUI simpleness, sigh... path = "/Mixer/EnhancedMixer_7" for i in range(5): in_id = 0 in_ch_l = i * 2 + 1 out_ch_l = 1 mux_id = self.getMultiplexedId(in_id, in_ch_l, out_ch_l) if i == state: value = 0x0000 else: value = 0x8000 self.hw.setContignuous(path, value, mux_id) # Mixer/Aux is selectable exclusively path = "/Mixer/Selector_7" sel = (state == 5) self.hw.setDiscrete(path, sel) libffado-2.4.5/support/mixer-qt4/ffado/mixer/motu.py0000644000175000001440000013240014206145246021773 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # Copyright (C) 2008 by Jonathan Woithe # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtCore import Qt # from PyQt4.QtGui import QWidget, QApplication from ffado.import_pyqt import * from ffado.config import * import logging log = logging.getLogger('motu') # Model defines. These must agree with what is used in motu_avdevice.h. MOTU_MODEL_NONE = 0x0000 MOTU_MODEL_828mkII = 0x0001 MOTU_MODEL_TRAVELER = 0x0002 MOTU_MODEL_ULTRALITE = 0x0003 MOTU_MODEL_8PRE = 0x0004 MOTU_MODEL_828MkI = 0x0005 MOTU_MODEL_896HD = 0x0006 MOTU_MODEL_828mk3 = 0x0007 MOTU_MODEL_ULTRALITEmk3 = 0x0008 MOTU_MODEL_ULTRALITEmk3_HYB = 0x0009 MOTU_MODEL_TRAVELERmk3 = 0x000a MOTU_MODEL_896HDmk3 = 0x000b class Motu(QWidget): def __init__(self,parent = None): QWidget.__init__(self,parent) uicLoad("ffado/mixer/motu", self) self.init() def init(self): # For matrix mixer controls (channel faders, pans, solos, mutes) the # first index (the row) is the zero-based mix number while the # second index (the column) is the zero-based channel number. The # third index gives the control of the stereo pair of the control # used as the key. The order of the channel enumeration must agree # with that used when creating the dbus controls within # motu_avdevice.cpp. self.ChannelFaders={ self.mix1ana1_fader: ['/Mixer/fader', 0, 0, self.mix1ana2_fader], self.mix1ana2_fader: ['/Mixer/fader', 0, 1, self.mix1ana1_fader], self.mix1ana3_fader: ['/Mixer/fader', 0, 2, self.mix1ana4_fader], self.mix1ana4_fader: ['/Mixer/fader', 0, 3, self.mix1ana3_fader], self.mix1ana5_fader: ['/Mixer/fader', 0, 4, self.mix1ana6_fader], self.mix1ana6_fader: ['/Mixer/fader', 0, 5, self.mix1ana5_fader], self.mix1ana7_fader: ['/Mixer/fader', 0, 6, self.mix1ana8_fader], self.mix1ana8_fader: ['/Mixer/fader', 0, 7, self.mix1ana7_fader], self.mix1aes1_fader: ['/Mixer/fader', 0, 8, self.mix1aes2_fader], self.mix1aes2_fader: ['/Mixer/fader', 0, 9, self.mix1aes1_fader], self.mix1spdif1_fader: ['/Mixer/fader', 0, 10, self.mix1spdif2_fader], self.mix1spdif2_fader: ['/Mixer/fader', 0, 11, self.mix1spdif1_fader], self.mix1adat1_fader: ['/Mixer/fader', 0, 12, self.mix1adat2_fader], self.mix1adat2_fader: ['/Mixer/fader', 0, 13, self.mix1adat1_fader], self.mix1adat3_fader: ['/Mixer/fader', 0, 14, self.mix1adat4_fader], self.mix1adat4_fader: ['/Mixer/fader', 0, 15, self.mix1adat3_fader], self.mix1adat5_fader: ['/Mixer/fader', 0, 16, self.mix1adat6_fader], self.mix1adat6_fader: ['/Mixer/fader', 0, 17, self.mix1adat5_fader], self.mix1adat7_fader: ['/Mixer/fader', 0, 18, self.mix1adat8_fader], self.mix1adat8_fader: ['/Mixer/fader', 0, 19, self.mix1adat7_fader], self.mix2ana1_fader: ['/Mixer/fader', 1, 0, self.mix2ana2_fader], self.mix2ana2_fader: ['/Mixer/fader', 1, 1, self.mix2ana1_fader], self.mix2ana3_fader: ['/Mixer/fader', 1, 2, self.mix2ana4_fader], self.mix2ana4_fader: ['/Mixer/fader', 1, 3, self.mix2ana3_fader], self.mix2ana5_fader: ['/Mixer/fader', 1, 4, self.mix2ana6_fader], self.mix2ana6_fader: ['/Mixer/fader', 1, 5, self.mix2ana5_fader], self.mix2ana7_fader: ['/Mixer/fader', 1, 6, self.mix2ana8_fader], self.mix2ana8_fader: ['/Mixer/fader', 1, 7, self.mix2ana7_fader], self.mix2aes1_fader: ['/Mixer/fader', 1, 8, self.mix2aes2_fader], self.mix2aes2_fader: ['/Mixer/fader', 1, 9, self.mix2aes1_fader], self.mix2spdif1_fader: ['/Mixer/fader', 1, 10, self.mix2spdif2_fader], self.mix2spdif2_fader: ['/Mixer/fader', 1, 11, self.mix2spdif1_fader], self.mix2adat1_fader: ['/Mixer/fader', 1, 12, self.mix2adat2_fader], self.mix2adat2_fader: ['/Mixer/fader', 1, 13, self.mix2adat1_fader], self.mix2adat3_fader: ['/Mixer/fader', 1, 14, self.mix2adat4_fader], self.mix2adat4_fader: ['/Mixer/fader', 1, 15, self.mix2adat3_fader], self.mix2adat5_fader: ['/Mixer/fader', 1, 16, self.mix2adat6_fader], self.mix2adat6_fader: ['/Mixer/fader', 1, 17, self.mix2adat5_fader], self.mix2adat7_fader: ['/Mixer/fader', 1, 18, self.mix2adat8_fader], self.mix2adat8_fader: ['/Mixer/fader', 1, 19, self.mix2adat7_fader], self.mix3ana1_fader: ['/Mixer/fader', 2, 0, self.mix3ana2_fader], self.mix3ana2_fader: ['/Mixer/fader', 2, 1, self.mix3ana1_fader], self.mix3ana3_fader: ['/Mixer/fader', 2, 2, self.mix3ana4_fader], self.mix3ana4_fader: ['/Mixer/fader', 2, 3, self.mix3ana3_fader], self.mix3ana5_fader: ['/Mixer/fader', 2, 4, self.mix3ana6_fader], self.mix3ana6_fader: ['/Mixer/fader', 2, 5, self.mix3ana5_fader], self.mix3ana7_fader: ['/Mixer/fader', 2, 6, self.mix3ana8_fader], self.mix3ana8_fader: ['/Mixer/fader', 2, 7, self.mix3ana7_fader], self.mix3aes1_fader: ['/Mixer/fader', 2, 8, self.mix3aes2_fader], self.mix3aes2_fader: ['/Mixer/fader', 2, 9, self.mix3aes1_fader], self.mix3spdif1_fader: ['/Mixer/fader', 2, 10, self.mix3spdif2_fader], self.mix3spdif2_fader: ['/Mixer/fader', 2, 11, self.mix3spdif1_fader], self.mix3adat1_fader: ['/Mixer/fader', 2, 12, self.mix3adat2_fader], self.mix3adat2_fader: ['/Mixer/fader', 2, 13, self.mix3adat1_fader], self.mix3adat3_fader: ['/Mixer/fader', 2, 14, self.mix3adat4_fader], self.mix3adat4_fader: ['/Mixer/fader', 2, 15, self.mix3adat3_fader], self.mix3adat5_fader: ['/Mixer/fader', 2, 16, self.mix3adat6_fader], self.mix3adat6_fader: ['/Mixer/fader', 2, 17, self.mix3adat5_fader], self.mix3adat7_fader: ['/Mixer/fader', 2, 18, self.mix3adat8_fader], self.mix3adat8_fader: ['/Mixer/fader', 2, 19, self.mix3adat7_fader], self.mix4ana1_fader: ['/Mixer/fader', 3, 0, self.mix4ana2_fader], self.mix4ana2_fader: ['/Mixer/fader', 3, 1, self.mix4ana1_fader], self.mix4ana3_fader: ['/Mixer/fader', 3, 2, self.mix4ana4_fader], self.mix4ana4_fader: ['/Mixer/fader', 3, 3, self.mix4ana3_fader], self.mix4ana5_fader: ['/Mixer/fader', 3, 4, self.mix4ana6_fader], self.mix4ana6_fader: ['/Mixer/fader', 3, 5, self.mix4ana5_fader], self.mix4ana7_fader: ['/Mixer/fader', 3, 6, self.mix4ana8_fader], self.mix4ana8_fader: ['/Mixer/fader', 3, 7, self.mix4ana7_fader], self.mix4aes1_fader: ['/Mixer/fader', 3, 8, self.mix4aes2_fader], self.mix4aes2_fader: ['/Mixer/fader', 3, 9, self.mix4aes1_fader], self.mix4spdif1_fader: ['/Mixer/fader', 3, 10, self.mix4spdif2_fader], self.mix4spdif2_fader: ['/Mixer/fader', 3, 11, self.mix4spdif1_fader], self.mix4adat1_fader: ['/Mixer/fader', 3, 12, self.mix4adat2_fader], self.mix4adat2_fader: ['/Mixer/fader', 3, 13, self.mix4adat1_fader], self.mix4adat3_fader: ['/Mixer/fader', 3, 14, self.mix4adat4_fader], self.mix4adat4_fader: ['/Mixer/fader', 3, 15, self.mix4adat3_fader], self.mix4adat5_fader: ['/Mixer/fader', 3, 16, self.mix4adat6_fader], self.mix4adat6_fader: ['/Mixer/fader', 3, 17, self.mix4adat5_fader], self.mix4adat7_fader: ['/Mixer/fader', 3, 18, self.mix4adat8_fader], self.mix4adat8_fader: ['/Mixer/fader', 3, 19, self.mix4adat7_fader], } self.Faders={ self.mix1_fader: ['/Mixer/Mix1/Mix_fader'], self.mix2_fader: ['/Mixer/Mix2/Mix_fader'], self.mix3_fader: ['/Mixer/Mix3/Mix_fader'], self.mix4_fader: ['/Mixer/Mix4/Mix_fader'], self.mainout_fader: ['/Mixer/Mainout_fader'], self.phones_fader: ['/Mixer/Phones_fader'], } self.ChannelControls={ self.mix1ana1_pan: ['/Mixer/pan', 0, 0], self.mix1ana2_pan: ['/Mixer/pan', 0, 1], self.mix1ana3_pan: ['/Mixer/pan', 0, 2], self.mix1ana4_pan: ['/Mixer/pan', 0, 3], self.mix1ana5_pan: ['/Mixer/pan', 0, 4], self.mix1ana6_pan: ['/Mixer/pan', 0, 5], self.mix1ana7_pan: ['/Mixer/pan', 0, 6], self.mix1ana8_pan: ['/Mixer/pan', 0, 7], self.mix1aes1_pan: ['/Mixer/pan', 0, 8], self.mix1aes2_pan: ['/Mixer/pan', 0, 9], self.mix1spdif1_pan: ['/Mixer/pan', 0, 10], self.mix1spdif2_pan: ['/Mixer/pan', 0, 11], self.mix1adat1_pan: ['/Mixer/pan', 0, 12], self.mix1adat2_pan: ['/Mixer/pan', 0, 13], self.mix1adat3_pan: ['/Mixer/pan', 0, 14], self.mix1adat4_pan: ['/Mixer/pan', 0, 15], self.mix1adat5_pan: ['/Mixer/pan', 0, 16], self.mix1adat6_pan: ['/Mixer/pan', 0, 17], self.mix1adat7_pan: ['/Mixer/pan', 0, 18], self.mix1adat8_pan: ['/Mixer/pan', 0, 19], self.mix2ana1_pan: ['/Mixer/pan', 1, 0], self.mix2ana2_pan: ['/Mixer/pan', 1, 1], self.mix2ana3_pan: ['/Mixer/pan', 1, 2], self.mix2ana4_pan: ['/Mixer/pan', 1, 3], self.mix2ana5_pan: ['/Mixer/pan', 1, 4], self.mix2ana6_pan: ['/Mixer/pan', 1, 5], self.mix2ana7_pan: ['/Mixer/pan', 1, 6], self.mix2ana8_pan: ['/Mixer/pan', 1, 7], self.mix2aes1_pan: ['/Mixer/pan', 1, 8], self.mix2aes2_pan: ['/Mixer/pan', 1, 9], self.mix2spdif1_pan: ['/Mixer/pan', 1, 10], self.mix2spdif2_pan: ['/Mixer/pan', 1, 11], self.mix2adat1_pan: ['/Mixer/pan', 1, 12], self.mix2adat2_pan: ['/Mixer/pan', 1, 13], self.mix2adat3_pan: ['/Mixer/pan', 1, 14], self.mix2adat4_pan: ['/Mixer/pan', 1, 15], self.mix2adat5_pan: ['/Mixer/pan', 1, 16], self.mix2adat6_pan: ['/Mixer/pan', 1, 17], self.mix2adat7_pan: ['/Mixer/pan', 1, 18], self.mix2adat8_pan: ['/Mixer/pan', 1, 19], self.mix3ana1_pan: ['/Mixer/pan', 2, 0], self.mix3ana2_pan: ['/Mixer/pan', 2, 1], self.mix3ana3_pan: ['/Mixer/pan', 2, 2], self.mix3ana4_pan: ['/Mixer/pan', 2, 3], self.mix3ana5_pan: ['/Mixer/pan', 2, 4], self.mix3ana6_pan: ['/Mixer/pan', 2, 5], self.mix3ana7_pan: ['/Mixer/pan', 2, 6], self.mix3ana8_pan: ['/Mixer/pan', 2, 7], self.mix3aes1_pan: ['/Mixer/pan', 2, 8], self.mix3aes2_pan: ['/Mixer/pan', 2, 9], self.mix3spdif1_pan: ['/Mixer/pan', 2, 10], self.mix3spdif2_pan: ['/Mixer/pan', 2, 11], self.mix3adat1_pan: ['/Mixer/pan', 2, 12], self.mix3adat2_pan: ['/Mixer/pan', 2, 13], self.mix3adat3_pan: ['/Mixer/pan', 2, 14], self.mix3adat4_pan: ['/Mixer/pan', 2, 15], self.mix3adat5_pan: ['/Mixer/pan', 2, 16], self.mix3adat6_pan: ['/Mixer/pan', 2, 17], self.mix3adat7_pan: ['/Mixer/pan', 2, 18], self.mix3adat8_pan: ['/Mixer/pan', 2, 19], self.mix4ana1_pan: ['/Mixer/pan', 3, 0], self.mix4ana2_pan: ['/Mixer/pan', 3, 1], self.mix4ana3_pan: ['/Mixer/pan', 3, 2], self.mix4ana4_pan: ['/Mixer/pan', 3, 3], self.mix4ana5_pan: ['/Mixer/pan', 3, 4], self.mix4ana6_pan: ['/Mixer/pan', 3, 5], self.mix4ana7_pan: ['/Mixer/pan', 3, 6], self.mix4ana8_pan: ['/Mixer/pan', 3, 7], self.mix4aes1_pan: ['/Mixer/pan', 3, 8], self.mix4aes2_pan: ['/Mixer/pan', 3, 9], self.mix4spdif1_pan: ['/Mixer/pan', 3, 10], self.mix4spdif2_pan: ['/Mixer/pan', 3, 11], self.mix4adat1_pan: ['/Mixer/pan', 3, 12], self.mix4adat2_pan: ['/Mixer/pan', 3, 13], self.mix4adat3_pan: ['/Mixer/pan', 3, 14], self.mix4adat4_pan: ['/Mixer/pan', 3, 15], self.mix4adat5_pan: ['/Mixer/pan', 3, 16], self.mix4adat6_pan: ['/Mixer/pan', 3, 17], self.mix4adat7_pan: ['/Mixer/pan', 3, 18], self.mix4adat8_pan: ['/Mixer/pan', 3, 19], } self.Controls={ self.ana1_trimgain: ['/Mixer/Control/Ana1_trimgain'], self.ana2_trimgain: ['/Mixer/Control/Ana2_trimgain'], self.ana3_trimgain: ['/Mixer/Control/Ana3_trimgain'], self.ana4_trimgain: ['/Mixer/Control/Ana4_trimgain'], self.ana5_trimgain: ['/Mixer/Control/Ana5_trimgain'], self.ana6_trimgain: ['/Mixer/Control/Ana6_trimgain'], self.ana7_trimgain: ['/Mixer/Control/Ana7_trimgain'], self.ana8_trimgain: ['/Mixer/Control/Ana8_trimgain'], self.spdif1_trimgain: ['/Mixer/Control/Spdif1_trimgain'], self.spdif2_trimgain: ['/Mixer/Control/Spdif2_trimgain'], } self.ChannelBinarySwitches={ self.mix1ana1_mute: ['/Mixer/mute', 0, 0], self.mix1ana2_mute: ['/Mixer/mute', 0, 1], self.mix1ana3_mute: ['/Mixer/mute', 0, 2], self.mix1ana4_mute: ['/Mixer/mute', 0, 3], self.mix1ana5_mute: ['/Mixer/mute', 0, 4], self.mix1ana6_mute: ['/Mixer/mute', 0, 5], self.mix1ana7_mute: ['/Mixer/mute', 0, 6], self.mix1ana8_mute: ['/Mixer/mute', 0, 7], self.mix1aes1_mute: ['/Mixer/mute', 0, 8], self.mix1aes2_mute: ['/Mixer/mute', 0, 9], self.mix1spdif1_mute: ['/Mixer/mute', 0, 10], self.mix1spdif2_mute: ['/Mixer/mute', 0, 11], self.mix1adat1_mute: ['/Mixer/mute', 0, 12], self.mix1adat2_mute: ['/Mixer/mute', 0, 13], self.mix1adat3_mute: ['/Mixer/mute', 0, 14], self.mix1adat4_mute: ['/Mixer/mute', 0, 15], self.mix1adat5_mute: ['/Mixer/mute', 0, 16], self.mix1adat6_mute: ['/Mixer/mute', 0, 17], self.mix1adat7_mute: ['/Mixer/mute', 0, 18], self.mix1adat8_mute: ['/Mixer/mute', 0, 19], self.mix1ana1_solo: ['/Mixer/solo', 0, 0], self.mix1ana2_solo: ['/Mixer/solo', 0, 1], self.mix1ana3_solo: ['/Mixer/solo', 0, 2], self.mix1ana4_solo: ['/Mixer/solo', 0, 3], self.mix1ana5_solo: ['/Mixer/solo', 0, 4], self.mix1ana6_solo: ['/Mixer/solo', 0, 5], self.mix1ana7_solo: ['/Mixer/solo', 0, 6], self.mix1ana8_solo: ['/Mixer/solo', 0, 7], self.mix1aes1_solo: ['/Mixer/solo', 0, 8], self.mix1aes2_solo: ['/Mixer/solo', 0, 9], self.mix1spdif1_solo: ['/Mixer/solo', 0, 10], self.mix1spdif2_solo: ['/Mixer/solo', 0, 11], self.mix1adat1_solo: ['/Mixer/solo', 0, 12], self.mix1adat2_solo: ['/Mixer/solo', 0, 13], self.mix1adat3_solo: ['/Mixer/solo', 0, 14], self.mix1adat4_solo: ['/Mixer/solo', 0, 15], self.mix1adat5_solo: ['/Mixer/solo', 0, 16], self.mix1adat6_solo: ['/Mixer/solo', 0, 17], self.mix1adat7_solo: ['/Mixer/solo', 0, 18], self.mix1adat8_solo: ['/Mixer/solo', 0, 19], self.mix2ana1_mute: ['/Mixer/mute', 1, 0], self.mix2ana2_mute: ['/Mixer/mute', 1, 1], self.mix2ana3_mute: ['/Mixer/mute', 1, 2], self.mix2ana4_mute: ['/Mixer/mute', 1, 3], self.mix2ana5_mute: ['/Mixer/mute', 1, 4], self.mix2ana6_mute: ['/Mixer/mute', 1, 5], self.mix2ana7_mute: ['/Mixer/mute', 1, 6], self.mix2ana8_mute: ['/Mixer/mute', 1, 7], self.mix2aes1_mute: ['/Mixer/mute', 1, 8], self.mix2aes2_mute: ['/Mixer/mute', 1, 9], self.mix2spdif1_mute: ['/Mixer/mute', 1, 10], self.mix2spdif2_mute: ['/Mixer/mute', 1, 11], self.mix2adat1_mute: ['/Mixer/mute', 1, 12], self.mix2adat2_mute: ['/Mixer/mute', 1, 13], self.mix2adat3_mute: ['/Mixer/mute', 1, 14], self.mix2adat4_mute: ['/Mixer/mute', 1, 15], self.mix2adat5_mute: ['/Mixer/mute', 1, 16], self.mix2adat6_mute: ['/Mixer/mute', 1, 17], self.mix2adat7_mute: ['/Mixer/mute', 1, 18], self.mix2adat8_mute: ['/Mixer/mute', 1, 19], self.mix2ana1_solo: ['/Mixer/solo', 1, 0], self.mix2ana2_solo: ['/Mixer/solo', 1, 1], self.mix2ana3_solo: ['/Mixer/solo', 1, 2], self.mix2ana4_solo: ['/Mixer/solo', 1, 3], self.mix2ana5_solo: ['/Mixer/solo', 1, 4], self.mix2ana6_solo: ['/Mixer/solo', 1, 5], self.mix2ana7_solo: ['/Mixer/solo', 1, 6], self.mix2ana8_solo: ['/Mixer/solo', 1, 7], self.mix2aes1_solo: ['/Mixer/solo', 1, 8], self.mix2aes2_solo: ['/Mixer/solo', 1, 9], self.mix2spdif1_solo: ['/Mixer/solo', 1, 10], self.mix2spdif2_solo: ['/Mixer/solo', 1, 11], self.mix2adat1_solo: ['/Mixer/solo', 1, 12], self.mix2adat2_solo: ['/Mixer/solo', 1, 13], self.mix2adat3_solo: ['/Mixer/solo', 1, 14], self.mix2adat4_solo: ['/Mixer/solo', 1, 15], self.mix2adat5_solo: ['/Mixer/solo', 1, 16], self.mix2adat6_solo: ['/Mixer/solo', 1, 17], self.mix2adat7_solo: ['/Mixer/solo', 1, 18], self.mix2adat8_solo: ['/Mixer/solo', 1, 19], self.mix3ana1_mute: ['/Mixer/mute', 2, 0], self.mix3ana2_mute: ['/Mixer/mute', 2, 1], self.mix3ana3_mute: ['/Mixer/mute', 2, 2], self.mix3ana4_mute: ['/Mixer/mute', 2, 3], self.mix3ana5_mute: ['/Mixer/mute', 2, 4], self.mix3ana6_mute: ['/Mixer/mute', 2, 5], self.mix3ana7_mute: ['/Mixer/mute', 2, 6], self.mix3ana8_mute: ['/Mixer/mute', 2, 7], self.mix3aes1_mute: ['/Mixer/mute', 2, 8], self.mix3aes2_mute: ['/Mixer/mute', 2, 9], self.mix3spdif1_mute: ['/Mixer/mute', 2, 10], self.mix3spdif2_mute: ['/Mixer/mute', 2, 11], self.mix3adat1_mute: ['/Mixer/mute', 2, 12], self.mix3adat2_mute: ['/Mixer/mute', 2, 13], self.mix3adat3_mute: ['/Mixer/mute', 2, 14], self.mix3adat4_mute: ['/Mixer/mute', 2, 15], self.mix3adat5_mute: ['/Mixer/mute', 2, 16], self.mix3adat6_mute: ['/Mixer/mute', 2, 17], self.mix3adat7_mute: ['/Mixer/mute', 2, 18], self.mix3adat8_mute: ['/Mixer/mute', 2, 19], self.mix3ana1_solo: ['/Mixer/solo', 2, 0], self.mix3ana2_solo: ['/Mixer/solo', 2, 1], self.mix3ana3_solo: ['/Mixer/solo', 2, 2], self.mix3ana4_solo: ['/Mixer/solo', 2, 3], self.mix3ana5_solo: ['/Mixer/solo', 2, 4], self.mix3ana6_solo: ['/Mixer/solo', 2, 5], self.mix3ana7_solo: ['/Mixer/solo', 2, 6], self.mix3ana8_solo: ['/Mixer/solo', 2, 7], self.mix3aes1_solo: ['/Mixer/solo', 2, 8], self.mix3aes2_solo: ['/Mixer/solo', 2, 9], self.mix3spdif1_solo: ['/Mixer/solo', 2, 10], self.mix3spdif2_solo: ['/Mixer/solo', 2, 11], self.mix3adat1_solo: ['/Mixer/solo', 2, 12], self.mix3adat2_solo: ['/Mixer/solo', 2, 13], self.mix3adat3_solo: ['/Mixer/solo', 2, 14], self.mix3adat4_solo: ['/Mixer/solo', 2, 15], self.mix3adat5_solo: ['/Mixer/solo', 2, 16], self.mix3adat6_solo: ['/Mixer/solo', 2, 17], self.mix3adat7_solo: ['/Mixer/solo', 2, 18], self.mix3adat8_solo: ['/Mixer/solo', 2, 19], self.mix4ana1_mute: ['/Mixer/mute', 3, 0], self.mix4ana2_mute: ['/Mixer/mute', 3, 1], self.mix4ana3_mute: ['/Mixer/mute', 3, 2], self.mix4ana4_mute: ['/Mixer/mute', 3, 3], self.mix4ana5_mute: ['/Mixer/mute', 3, 4], self.mix4ana6_mute: ['/Mixer/mute', 3, 5], self.mix4ana7_mute: ['/Mixer/mute', 3, 6], self.mix4ana8_mute: ['/Mixer/mute', 3, 7], self.mix4aes1_mute: ['/Mixer/mute', 3, 8], self.mix4aes2_mute: ['/Mixer/mute', 3, 9], self.mix4spdif1_mute: ['/Mixer/mute', 3, 10], self.mix4spdif2_mute: ['/Mixer/mute', 3, 11], self.mix4adat1_mute: ['/Mixer/mute', 3, 12], self.mix4adat2_mute: ['/Mixer/mute', 3, 13], self.mix4adat3_mute: ['/Mixer/mute', 3, 14], self.mix4adat4_mute: ['/Mixer/mute', 3, 15], self.mix4adat5_mute: ['/Mixer/mute', 3, 16], self.mix4adat6_mute: ['/Mixer/mute', 3, 17], self.mix4adat7_mute: ['/Mixer/mute', 3, 18], self.mix4adat8_mute: ['/Mixer/mute', 3, 19], self.mix4ana1_solo: ['/Mixer/solo', 3, 0], self.mix4ana2_solo: ['/Mixer/solo', 3, 1], self.mix4ana3_solo: ['/Mixer/solo', 3, 2], self.mix4ana4_solo: ['/Mixer/solo', 3, 3], self.mix4ana5_solo: ['/Mixer/solo', 3, 4], self.mix4ana6_solo: ['/Mixer/solo', 3, 5], self.mix4ana7_solo: ['/Mixer/solo', 3, 6], self.mix4ana8_solo: ['/Mixer/solo', 3, 7], self.mix4aes1_solo: ['/Mixer/solo', 3, 8], self.mix4aes2_solo: ['/Mixer/solo', 3, 9], self.mix4spdif1_solo: ['/Mixer/solo', 3, 10], self.mix4spdif2_solo: ['/Mixer/solo', 3, 11], self.mix4adat1_solo: ['/Mixer/solo', 3, 12], self.mix4adat2_solo: ['/Mixer/solo', 3, 13], self.mix4adat3_solo: ['/Mixer/solo', 3, 14], self.mix4adat4_solo: ['/Mixer/solo', 3, 15], self.mix4adat5_solo: ['/Mixer/solo', 3, 16], self.mix4adat6_solo: ['/Mixer/solo', 3, 17], self.mix4adat7_solo: ['/Mixer/solo', 3, 18], self.mix4adat8_solo: ['/Mixer/solo', 3, 19], } self.BinarySwitches={ self.mix1_mute: ['/Mixer/Mix1/Mix_mute'], self.mix2_mute: ['/Mixer/Mix2/Mix_mute'], self.mix3_mute: ['/Mixer/Mix3/Mix_mute'], self.mix4_mute: ['/Mixer/Mix4/Mix_mute'], self.ana1_pad: ['/Mixer/Control/Ana1_pad'], self.ana2_pad: ['/Mixer/Control/Ana2_pad'], self.ana3_pad: ['/Mixer/Control/Ana3_pad'], self.ana4_pad: ['/Mixer/Control/Ana4_pad'], self.ana5_pad: ['/Mixer/Control/Ana5_pad'], self.ana6_pad: ['/Mixer/Control/Ana6_pad'], self.ana7_pad: ['/Mixer/Control/Ana7_pad'], self.ana8_pad: ['/Mixer/Control/Ana8_pad'], self.ana1_invert: ['/Mixer/Control/Ana1_invert'], self.ana2_invert: ['/Mixer/Control/Ana2_invert'], self.ana3_invert: ['/Mixer/Control/Ana3_invert'], self.ana4_invert: ['/Mixer/Control/Ana4_invert'], self.ana5_invert: ['/Mixer/Control/Ana5_invert'], self.ana6_invert: ['/Mixer/Control/Ana6_invert'], self.ana7_invert: ['/Mixer/Control/Ana7_invert'], self.ana8_invert: ['/Mixer/Control/Ana8_invert'], self.spdif1_invert: ['/Mixer/Control/Spdif1_invert'], self.spdif2_invert: ['/Mixer/Control/Spdif2_invert'], self.ana1_level: ['/Mixer/Control/Ana1_level'], self.ana2_level: ['/Mixer/Control/Ana2_level'], self.ana3_level: ['/Mixer/Control/Ana3_level'], self.ana4_level: ['/Mixer/Control/Ana4_level'], self.ana5_level: ['/Mixer/Control/Ana5_level'], self.ana6_level: ['/Mixer/Control/Ana6_level'], self.ana7_level: ['/Mixer/Control/Ana7_level'], self.ana8_level: ['/Mixer/Control/Ana8_level'], self.ana1_boost: ['/Mixer/Control/Ana1_boost'], self.ana2_boost: ['/Mixer/Control/Ana2_boost'], self.ana3_boost: ['/Mixer/Control/Ana3_boost'], self.ana4_boost: ['/Mixer/Control/Ana4_boost'], self.ana5_boost: ['/Mixer/Control/Ana5_boost'], self.ana6_boost: ['/Mixer/Control/Ana6_boost'], self.ana7_boost: ['/Mixer/Control/Ana7_boost'], self.ana8_boost: ['/Mixer/Control/Ana8_boost'], } self.Selectors={ self.mix1_dest: ['/Mixer/Mix1/Mix_dest'], self.mix2_dest: ['/Mixer/Mix2/Mix_dest'], self.mix3_dest: ['/Mixer/Mix3/Mix_dest'], self.mix4_dest: ['/Mixer/Mix4/Mix_dest'], self.phones_src: ['/Mixer/Control/Phones_src'], self.optical_in_mode: ['/Mixer/Control/OpticalIn_mode'], self.optical_out_mode: ['/Mixer/Control/OpticalOut_mode'], self.meter_src_ctrl: ['/Mixer/Control/Meter_src'], self.aesebu_meter_ctrl: ['/Mixer/Control/Meter_aesebu_src'], self.peakhold_time_ctrl:['/Mixer/Control/Meter_peakhold_time'], self.cliphold_time_ctrl:['/Mixer/Control/Meter_cliphold_time'], } # Other mixer variables self.is_streaming = 0 self.sample_rate = 0 self.model = MOTU_MODEL_NONE # public slot: channel faders within a matrix mixer def updateChannelFader(self, a0): sender = self.sender() vol = a0 log.debug("setting %s for mix %d channel %d to %d" % (self.ChannelFaders[sender][0], self.ChannelFaders[sender][1], self.ChannelFaders[sender][2], vol)) self.hw.setMatrixMixerValue(self.ChannelFaders[sender][0], self.ChannelFaders[sender][1], self.ChannelFaders[sender][2], vol) # Using the ctrl modifier key makes stereo pairs move in unison if (QApplication.keyboardModifiers() == Qt.ControlModifier): pair = self.ChannelFaders[sender][3] pair.setValue(vol) # public slot: a multivalue control within a matrix mixer def updateChannelControl(self, a0): sender = self.sender() val = a0 log.debug("setting %s for mix %d channel %d to %d" % (self.ChannelControls[sender][0], self.ChannelControls[sender][1], self.ChannelControls[sender][2], val)) self.hw.setMatrixMixerValue(self.ChannelControls[sender][0], self.ChannelControls[sender][1], self.ChannelControls[sender][2], val) # public slot: a generic single multivalue control def updateControl(self, a0): sender = self.sender() val = a0 log.debug("setting %s control to %d" % (self.Controls[sender][0], val)) self.hw.setDiscrete(self.Controls[sender][0], val) # public slot: a binary switch within a matrix mixer def updateChannelBinarySwitch(self, a0): sender = self.sender() val=a0 log.debug("setting %s for mix %d channel %d switch to %d" % (self.ChannelBinarySwitches[sender][0], self.ChannelBinarySwitches[sender][1], self.ChannelBinarySwitches[sender][2], val)) self.hw.setMatrixMixerValue(self.ChannelBinarySwitches[sender][0], self.ChannelBinarySwitches[sender][1], self.ChannelBinarySwitches[sender][2], val) # public slot: generic single binary switch def updateBinarySwitch(self, a0): sender = self.sender() val=a0 log.debug("setting %s switch to %d" % (self.BinarySwitches[sender][0], val)) self.hw.setDiscrete(self.BinarySwitches[sender][0], val) # public slot: a faders (not in a matrix mixer) def updateFader(self, a0): sender = self.sender() vol = a0 log.debug("setting %s mix fader to %d" % (self.Faders[sender][0], vol)) self.hw.setDiscrete(self.Faders[sender][0], vol) # public slot: selectors (eg: mix destination controls) def updateSelector(self, a0): sender = self.sender() dest=a0 log.debug("setting %s selector to %d" % (self.Selectors[sender][0], dest)) self.hw.setDiscrete(self.Selectors[sender][0], dest) # public slots: mix output controls def set_mix1_dest(self,a0): self.setMixDest('mix1', a0) def setSelector(self,a0,a1): name=a0 state = a1 log.debug("setting %s state to %d" % (name, state)) self.hw.setDiscrete(self.SelectorControls[name][0], state) # Hide and disable a control def disable_hide(self,widget): widget.hide() widget.setEnabled(False) # Set destination lists for the 8pre. MainOut is index 2 and for the # moment we assume the ADAT options follow that. def set_8pre_dest_list(self, list): list.setItemText(2, "MainOut") list.setItemText(3, "ADAT 1-2") list.setItemText(4, "ADAT 3-4") list.setItemText(5, "ADAT 5-6") list.setItemText(6, "ADAT 7-8") list.setMaxCount(7) def initValues_g1(self): # Set up widgets for generation-1 devices (only the 828mk1 for now). # For now disable all mix faders and analog controls since we don't # know how to control them (and it's not clear that they are even # present in the hardware). self.disable_hide(self.mixtab) self.disable_hide(self.masterbox) self.disable_hide(self.analog_settings_box) # The 828mk1 didn't have meter controls self.disable_hide(self.meter_src) self.disable_hide(self.aesebu_meter) self.disable_hide(self.peakhold_time) self.disable_hide(self.cliphold_time) # For the moment hide this control too since it's not clear how # it might be used to interact with the 828mk1. Ultimately we # may be able to reset its items and use it for the monitor source # selector. self.disable_hide(self.phones_src) self.disable_hide(self.phones_src_frame) def initValues_g2(self): # Set up widgets for generation-2 devices # The 828Mk2 has separate Mic inputs but no AES/EBU, so use the # AES/EBU mixer controls as "Mic" controls. If a device comes along # with both mic and AES inputs this approach will have to be # re-thought. # Doing this means that on the 828Mk2, the mixer matrix elements # used for AES/EBU on other models are used for the Mic channels. # So long as the MixerChannels_828Mk2 definition in # motu_avdevice.cpp defines the mic channels immediately after the 8 # analog channels we'll be right. Note that we don't need to change # the matrix lookup tables (self.ChannelFaders etc) because the QT # controls are still named *aesebu*. if (self.model == MOTU_MODEL_828mkII): self.mix1_tab.setTabText(1, "Mic inputs"); self.mix2_tab.setTabText(1, "Mic inputs"); self.mix3_tab.setTabText(1, "Mic inputs"); self.mix4_tab.setTabText(1, "Mic inputs"); else: # Only the Traveler and 896HD have AES/EBU inputs, so disable the AES/EBU # tab for all other models. if (self.model!=MOTU_MODEL_TRAVELER and self.model!=MOTU_MODEL_896HD): self.mix1_tab.setTabEnabled(1, False) self.mix2_tab.setTabEnabled(1, False) self.mix3_tab.setTabEnabled(1, False) self.mix4_tab.setTabEnabled(1, False) # All models except the 896HD and 8pre have SPDIF inputs. if (self.model==MOTU_MODEL_8PRE or self.model==MOTU_MODEL_896HD): self.mix1_tab.setTabEnabled(2, False); self.mix2_tab.setTabEnabled(2, False); self.mix3_tab.setTabEnabled(2, False); self.mix4_tab.setTabEnabled(2, False); # Devices without AES/EBU inputs/outputs (currently all except the # Traveler and 896HD) have dedicated "MainOut" outputs instead. # AES/EBU is normally ID 6 in the destination lists and "MainOut" # displaces it on non-AES/EBU models. The 896HD has both AES/EBU # and MainOut which complicates this; it uses ID 6 for MainOut and # ID 7 (nominally SPDIF) for AES/EBU. Therefore change ID 6 to # "MainOut" for everything but the Traveler, and set ID 7 (nominally # SPDIF) to AES/EBU for the 896HD. if (self.model != MOTU_MODEL_TRAVELER): self.mix1_dest.setItemText(6, "MainOut") self.mix2_dest.setItemText(6, "MainOut") self.mix3_dest.setItemText(6, "MainOut") self.mix4_dest.setItemText(6, "MainOut") self.phones_src.setItemText(6, "MainOut") if (self.model == MOTU_MODEL_896HD): self.mix1_dest.setItemText(7, "AES/EBU") self.mix2_dest.setItemText(7, "AES/EBU") self.mix3_dest.setItemText(7, "AES/EBU") self.mix4_dest.setItemText(7, "AES/EBU") self.phones_src.setItemText(7, "AES/EBU") # The Ultralite doesn't have ADAT channels (or any optical ports at # all) if (self.model == MOTU_MODEL_ULTRALITE): self.mix1_tab.setTabEnabled(3, False) # ADAT page self.mix2_tab.setTabEnabled(3, False) # ADAT page self.mix3_tab.setTabEnabled(3, False) # ADAT page self.mix4_tab.setTabEnabled(3, False) # ADAT page self.optical_in_mode.setEnabled(False) self.optical_out_mode.setEnabled(False) # The 896HD and 8pre don't have optical SPDIF (aka Toslink) capability if (self.model==MOTU_MODEL_896HD or self.model==MOTU_MODEL_8PRE): self.optical_in_mode.removeItem(2) self.optical_out_mode.removeItem(2) # The 8pre doesn't have software phones/main fader controls if (self.model==MOTU_MODEL_8PRE): self.disable_hide(self.mainout_fader) self.disable_hide(self.phones_fader) self.disable_hide(self.masterbox) # The 8pre's destination list is rather different to all other # models. if (self.model==MOTU_MODEL_8PRE): self.set_8pre_dest_list(self.mix1_dest) self.set_8pre_dest_list(self.mix2_dest) self.set_8pre_dest_list(self.mix3_dest) self.set_8pre_dest_list(self.mix4_dest) self.set_8pre_dest_list(self.phones_src) # Only the 896HD has meter controls if (self.model != MOTU_MODEL_896HD): self.disable_hide(self.meter_src) self.disable_hide(self.aesebu_meter) self.disable_hide(self.peakhold_time) self.disable_hide(self.cliphold_time) # Some controls must be disabled if the device is streaming if (self.is_streaming): log.debug("Disabling controls which require inactive streaming") self.optical_in_mode.setEnabled(False) self.optical_out_mode.setEnabled(False) # Some channels aren't available at higher sampling rates if (self.sample_rate > 96000): log.debug("Disabling controls not present above 96 kHz") self.mix1_tab.setTabEnabled(3, False) # ADAT self.mix1_tab.setTabEnabled(2, False) # SPDIF self.mix1_tab.setTabEnabled(1, False) # AES/EBU self.mix2_tab.setTabEnabled(3, False) # ADAT self.mix2_tab.setTabEnabled(2, False) # SPDIF self.mix2_tab.setTabEnabled(1, False) # AES/EBU self.mix3_tab.setTabEnabled(3, False) # ADAT self.mix3_tab.setTabEnabled(2, False) # SPDIF self.mix3_tab.setTabEnabled(1, False) # AES/EBU self.mix4_tab.setTabEnabled(3, False) # ADAT self.mix4_tab.setTabEnabled(2, False) # SPDIF self.mix4_tab.setTabEnabled(1, False) # AES/EBU if (self.sample_rate > 48000): log.debug("Disabling controls not present above 48 kHz") self.mix1adat5.setEnabled(False) self.mix1adat6.setEnabled(False) self.mix1adat7.setEnabled(False) self.mix1adat8.setEnabled(False) self.mix2adat5.setEnabled(False) self.mix2adat6.setEnabled(False) self.mix2adat7.setEnabled(False) self.mix2adat8.setEnabled(False) self.mix3adat5.setEnabled(False) self.mix3adat6.setEnabled(False) self.mix3adat7.setEnabled(False) self.mix3adat8.setEnabled(False) self.mix4adat5.setEnabled(False) self.mix4adat6.setEnabled(False) self.mix4adat7.setEnabled(False) self.mix4adat8.setEnabled(False) # Ensure the correct input controls are active for a given interface. # Only the Ultralite has phase inversion switches. if (not(self.model == MOTU_MODEL_ULTRALITE)): self.disable_hide(self.ana1_invert) self.disable_hide(self.ana2_invert) self.disable_hide(self.ana3_invert) self.disable_hide(self.ana4_invert) self.disable_hide(self.ana5_invert) self.disable_hide(self.ana6_invert) self.disable_hide(self.ana7_invert) self.disable_hide(self.ana8_invert) self.disable_hide(self.spdif1_invert) self.disable_hide(self.spdif2_invert) # The Traveler has pad switches for analog 1-4 only; other interfaces # don't have pad switches at all. if (not(self.model == MOTU_MODEL_TRAVELER)): self.disable_hide(self.ana1_pad) self.disable_hide(self.ana2_pad) self.disable_hide(self.ana3_pad) self.disable_hide(self.ana4_pad) self.disable_hide(self.ana5_pad) self.disable_hide(self.ana6_pad) self.disable_hide(self.ana7_pad) self.disable_hide(self.ana8_pad) # The Traveler has level and boost switches for analog 5-8. The # 8pre, Ultralite and the 896HD don't implement them. All other # interfaces have them over analog 1-8. if (self.model==MOTU_MODEL_TRAVELER or self.model==MOTU_MODEL_ULTRALITE or self.model==MOTU_MODEL_896HD or self.model==MOTU_MODEL_8PRE): self.disable_hide(self.ana1_level) self.disable_hide(self.ana2_level) self.disable_hide(self.ana3_level) self.disable_hide(self.ana4_level) self.disable_hide(self.ana1_boost) self.disable_hide(self.ana2_boost) self.disable_hide(self.ana3_boost) self.disable_hide(self.ana4_boost) if (self.model==MOTU_MODEL_ULTRALITE or self.model==MOTU_MODEL_896HD or self.model==MOTU_MODEL_8PRE): self.disable_hide(self.ana5_level) self.disable_hide(self.ana6_level) self.disable_hide(self.ana7_level) self.disable_hide(self.ana8_level) self.disable_hide(self.ana5_boost) self.disable_hide(self.ana6_boost) self.disable_hide(self.ana7_boost) self.disable_hide(self.ana8_boost) # The Traveler has trimgain for analog 1-4. The Ultralite has trimgain for # analog 1-8 and SPDIF 1-2. All other interfaces don't have trimgain. if (not(self.model==MOTU_MODEL_TRAVELER or self.model==MOTU_MODEL_ULTRALITE)): self.disable_hide(self.ana1_trimgain) self.disable_hide(self.ana1_trimgain_label) self.disable_hide(self.ana2_trimgain) self.disable_hide(self.ana2_trimgain_label) self.disable_hide(self.ana3_trimgain) self.disable_hide(self.ana3_trimgain_label) self.disable_hide(self.ana4_trimgain) self.disable_hide(self.ana4_trimgain_label) if (not(self.model == MOTU_MODEL_ULTRALITE)): self.disable_hide(self.ana5_trimgain) self.disable_hide(self.ana5_trimgain_label) self.disable_hide(self.ana6_trimgain) self.disable_hide(self.ana6_trimgain_label) self.disable_hide(self.ana7_trimgain) self.disable_hide(self.ana7_trimgain_label) self.disable_hide(self.ana8_trimgain) self.disable_hide(self.ana8_trimgain_label) self.disable_hide(self.spdif1_trimgain) self.disable_hide(self.spdif1_trimgain_label) self.disable_hide(self.spdif1ctrl) self.disable_hide(self.spdif2_trimgain) self.disable_hide(self.spdif2_trimgain_label) self.disable_hide(self.spdif2ctrl) # The 8pre has no digital controls for the analog inputs, so there's # no point in showing the frame which would normally contain them. if (self.model == MOTU_MODEL_8PRE): self.disable_hide(self.analog_settings_box) def initValues(self): # Is the device streaming? self.is_streaming = self.hw.getDiscrete('/Mixer/Info/IsStreaming') log.debug("device streaming flag: %d" % (self.is_streaming)) # Retrieve other device settings as needed and customise the UI # based on these options. self.model = self.hw.getDiscrete('/Mixer/Info/Model') log.debug("device model identifier: %d" % (self.model)) self.sample_rate = self.hw.getDiscrete('/Mixer/Info/SampleRate') log.debug("device sample rate: %d" % (self.sample_rate)) # MOTU Mark 3 devices should be configured to use the "Motu_Mark3" # mixer. if (self.model==MOTU_MODEL_828mk3 or self.model==MOTU_MODEL_ULTRALITEmk3 or self.model==MOTU_MODEL_ULTRALITEmk3_HYB or self.model==MOTU_MODEL_TRAVELERmk3 or self.model==MOTU_MODEL_896HDmk3): log.debug("Generation-3 MOTU devices should be configured to use the Motu_Mark3 mixer") return elif (self.model==MOTU_MODEL_828MkI): self.initValues_g1() else: self.initValues_g2() # Now fetch the current values into the respective controls. Don't # bother fetching controls which are disabled. for ctrl, info in self.ChannelFaders.items(): if (not(ctrl.isEnabled())): continue vol = self.hw.getMatrixMixerValue(info[0], info[1], info[2]) log.debug("%s for mix %d channel %d is %d" % (info[0], info[1], info[2], vol)) ctrl.setValue(vol) ctrl.valueChanged.connect(self.updateChannelFader) for ctrl, info in self.Faders.items(): if (not(ctrl.isEnabled())): continue vol = self.hw.getDiscrete(info[0]) log.debug("%s mix fader is %d" % (info[0] , vol)) ctrl.setValue(vol) ctrl.valueChanged.connect(self.updateFader) for ctrl, info in self.ChannelControls.items(): if (not(ctrl.isEnabled())): continue pan = self.hw.getMatrixMixerValue(info[0], info[1], info[2]) log.debug("%s for mix %d channel %d is %d" % (info[0], info[1], info[2], pan)) ctrl.setValue(pan) ctrl.valueChanged.connect(self.updateChannelControl) for ctrl, info in self.Controls.items(): if (not(ctrl.isEnabled())): continue pan = self.hw.getDiscrete(info[0]) log.debug("%s control is %d" % (info[0] , pan)) ctrl.setValue(pan) ctrl.valueChanged.connect(self.updateControl) for ctrl, info in self.ChannelBinarySwitches.items(): if (not(ctrl.isEnabled())): continue val = self.hw.getMatrixMixerValue(info[0], info[1], info[2]) log.debug("%s for mix %d channel %d is %d" % (info[0] , info[1], info[2], val)) if val: ctrl.setChecked(True) else: ctrl.setChecked(False) ctrl.toggled.connect(self.updateChannelBinarySwitch) for ctrl, info in self.BinarySwitches.items(): if (not(ctrl.isEnabled())): continue val = self.hw.getDiscrete(info[0]) log.debug("%s switch is %d" % (info[0] , val)) if val: ctrl.setChecked(True) else: ctrl.setChecked(False) ctrl.toggled.connect(self.updateBinarySwitch) for ctrl, info in self.Selectors.items(): if (not(ctrl.isEnabled())): continue dest = self.hw.getDiscrete(info[0]) log.debug("%s selector is %d" % (info[0] , dest)) ctrl.setCurrentIndex(dest) ctrl.activated.connect(self.updateSelector) # We could enable/disable ADAT controls here depending on whether # the optical port is set to ADAT or something else. A disable # can't be done earlier since we have to read the ADAT mixer # settings (which won't happen if they're disabled). However, on # the other hand it may be more convenient to leave all controls # active at all times. We'll see. # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/motu.ui0000644000175000001440000235262514206145246021777 0ustar jwoitheusers Copyright (C) 2008 by Jonathan Woithe Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. MotuMixerUI 0 0 1156 803 0 0 Form1 Device settings Optical input mode 9 Disabled ADAT Toslink QFrame::HLine QFrame::Sunken Optical output mode 9 Disabled ADAT Toslink Phones assign 9 Disabled Phones Analog 1-2 Analog 3-4 Analog 5-6 Analog 7-8 AES/EBU SPDIF ADAT 1-2 ADAT 3-4 ADAT 5-6 ADAT 7-8 QFrame::HLine QFrame::Sunken Meter source 9 Analog out ADAT in ADAT out AES/EBU Meter 9 AES/EBU out AES/EBU in Peakhold time 9 Off 2 sec 4 sec 10 sec 1 min 5 min 8 min infinite Cliphold time 9 Off 2 sec 4 sec 10 sec 1 min 5 min 8 min infinite Qt::Vertical QSizePolicy::Expanding 20 220 QFrame::NoFrame QFrame::Plain Analog input settings QFrame::NoFrame QFrame::Plain 9 53 9 Gain Qt::AlignCenter false 9 Pad true Invert true true 9 Boost true 9 +4dBu true 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 53 9 Gain Qt::AlignCenter false 9 Pad true Invert true true 9 Boost true 9 +4dBu true 75 true 2 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 53 9 Gain Qt::AlignCenter false 9 Pad true Invert true true 9 Boost true 9 +4dBu true 75 true 3 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 53 9 Gain Qt::AlignCenter false 9 Pad true Invert true true 9 Boost true 9 +4dBu true 75 true 4 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 53 9 Gain Qt::AlignCenter false 9 Pad true Invert true true 9 Boost true 9 +4dBu true 75 true 5 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 53 9 Gain Qt::AlignCenter false 9 Pad true Invert true true 9 Boost true 9 +4dBu true 75 true 6 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 53 9 Gain Qt::AlignCenter false 9 Pad true Invert true false true 9 Boost true 9 +4dBu true 75 true 7 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 53 9 Gain Qt::AlignCenter false 9 Pad true Invert true true 9 Boost true 9 +4dBu true 75 true 8 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 53 9 Gain Qt::AlignCenter false Invert true Qt::Vertical QSizePolicy::Minimum 20 40 75 true SPDIF 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 53 9 Gain Qt::AlignCenter false Invert true Qt::Vertical QSizePolicy::Minimum 20 40 75 true SPDIF 2 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 101 20 QFrame::NoFrame QFrame::Plain 0 0 0 Mix 1 0 Analog 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 3 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 4 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 5 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 6 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 7 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 8 Qt::AlignCenter false AES/EBU 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 450 20 SPDIF 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 470 20 ADAT 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 3 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 4 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 5 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 6 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 7 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 8 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 31 20 Mix 1 output 9 Destination Qt::AlignCenter false 9 Disabled Phones Analog 1-2 Analog 3-4 Analog 5-6 Analog 7-8 AES/EBU SPDIF ADAT 1-2 ADAT 3-4 ADAT 5-6 ADAT 7-8 9 Mute true QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 Mix 2 Analog 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 3 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 4 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 5 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 6 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 7 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 8 Qt::AlignCenter false AES/EBU 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 450 20 SPDIF 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 470 20 ADAT 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 3 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 4 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 5 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 6 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 7 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 8 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 31 20 Mix 2 output 9 Destination Qt::AlignCenter false 9 Disabled Phones Analog 1-2 Analog 3-4 Analog 5-6 Analog 7-8 AES/EBU SPDIF ADAT 1-2 ADAT 3-4 ADAT 5-6 ADAT 7-8 9 Mute true QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 Mix 3 Analog 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 3 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 4 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 5 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 6 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 7 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 8 Qt::AlignCenter false AES/EBU 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 450 20 SPDIF 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 470 20 ADAT 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 3 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 4 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 5 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 6 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 7 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 8 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 31 20 Mix 3 output 9 Destination Qt::AlignCenter false 9 Disabled Phones Analog 1-2 Analog 3-4 Analog 5-6 Analog 7-8 AES/EBU SPDIF ADAT 1-2 ADAT 3-4 ADAT 5-6 ADAT 7-8 9 Mute true QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 Mix 4 Analog 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 3 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 4 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 5 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 6 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 7 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 8 Qt::AlignCenter false AES/EBU 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 450 20 SPDIF 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 470 20 ADAT 0 QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 1 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 2 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 3 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 4 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 5 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 6 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 7 Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 9 Solo true 9 Mute true 9 -64 64 9 Pan Qt::AlignCenter false QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true 8 Qt::AlignCenter false Qt::Horizontal QSizePolicy::Expanding 31 20 Mix 4 output 9 Destination Qt::AlignCenter false 9 Disabled Phones Analog 1-2 Analog 3-4 Analog 5-6 Analog 7-8 AES/EBU SPDIF ADAT 1-2 ADAT 3-4 ADAT 5-6 ADAT 7-8 9 Mute true QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 Masters Qt::AlignCenter 0 9 0 QFrame::NoFrame QFrame::Plain QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true Phones Qt::AlignCenter false 0 QFrame::NoFrame QFrame::Plain QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal QSizePolicy::Expanding 1 20 12 0 128 128 Qt::Vertical 5 Qt::Horizontal QSizePolicy::Expanding 1 20 75 true MainOut Qt::AlignCenter false qPixmapFromMimeSource libffado-2.4.5/support/mixer-qt4/ffado/mixer/motu_mark3.py0000644000175000001440000000527414206145246023100 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # Copyright (C) 2008, 2013 by Jonathan Woithe # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QWidget, QApplication from ffado.import_pyqt import * from ffado.config import * import logging log = logging.getLogger('motu_mark3') # Model defines. These must agree with what is used in motu_avdevice.h. MOTU_MODEL_NONE = 0x0000 MOTU_MODEL_828mkII = 0x0001 MOTU_MODEL_TRAVELER = 0x0002 MOTU_MODEL_ULTRALITE = 0x0003 MOTU_MODEL_8PRE = 0x0004 MOTU_MODEL_828MkI = 0x0005 MOTU_MODEL_896HD = 0x0006 MOTU_MODEL_828mk3 = 0x0007 MOTU_MODEL_ULTRALITEmk3 = 0x0008 MOTU_MODEL_ULTRALITEmk3_HYB = 0x0009 MOTU_MODEL_TRAVELERmk3 = 0x000a MOTU_MODEL_896HDmk3 = 0x000b class Motu_Mark3(QWidget): def __init__(self,parent = None): QWidget.__init__(self,parent) uicLoad("ffado/mixer/motu_mark3", self) self.init() def init(self): # Initialise any object members as needed # Other mixer variables self.is_streaming = 0 self.sample_rate = 0 self.model = MOTU_MODEL_NONE # Hide and disable a control def disable_hide(self,widget): widget.hide() widget.setEnabled(False) def initValues_g3(self): # Set up widgets for generation-3 (aka Mark-3) devices return def initValues(self): # Is the device streaming? self.is_streaming = self.hw.getDiscrete('/Mixer/Info/IsStreaming') log.debug("device streaming flag: %d" % (self.is_streaming)) # Retrieve other device settings as needed and customise the UI # based on these options. self.model = self.hw.getDiscrete('/Mixer/Info/Model') log.debug("device model identifier: %d" % (self.model)) self.sample_rate = self.hw.getDiscrete('/Mixer/Info/SampleRate') log.debug("device sample rate: %d" % (self.sample_rate)) self.initValues_g3() libffado-2.4.5/support/mixer-qt4/ffado/mixer/nodevice.py0000644000175000001440000000214114206145246022601 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # 2007-2008 by Arnold Krille # # 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 . # # from PyQt4.QtGui import QWidget from ffado.import_pyqt import * from mixer_nodeviceui import Ui_NoDeviceMixerUI class NoDevice(QWidget, Ui_NoDeviceMixerUI): def __init__(self,parent = None): QWidget.__init__(self,parent) self.setupUi(self) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/phase24.ui0000644000175000001440000005014614206145246022250 0ustar jwoitheusers Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Phase24ControlUI 0 0 331 390 Terratec Phase 24 Control 3/4 Out Level false Analog In Level false Sync Source false Line Headphone 0 1 2 3 4 Internal External Hardware Mixer In Qt::AlignCenter false Analog false 0 25600 10000 1000 Qt::Vertical 10000 SPDIF false 0 25600 10000 1000 Qt::Vertical 10000 SPDIF false 0 25600 10000 1000 Qt::Vertical 10000 1/2 false 0 25600 10000 1000 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 20 20 3/4 false 0 25600 10000 1000 Qt::Vertical 10000 Out false Master false 0 80 0 25600 10000 1000 Qt::Vertical 10000 Qt::Horizontal QSizePolicy::Expanding 40 20 Routing Wave Out 1/2 Wave Out 3/4 Line in SPDIF in Mixer Wave Out SPDIF Wave Out 1/2 Wave Out 3/4 Line in SPDIF in Mixer Wave Out SPDIF SPDIF false 3/4 false Wave Out 1/2 Wave Out 3/4 Line in SPDIF in Mixer Wave Out SPDIF 1/2 false qPixmapFromMimeSource sldInput12 sldInput34 sldLineIn sldSPDIFOut sldSPDIFIn sldMaster cmbFrontLevel cmbOutSource12 cmbOutSource34 cmbOutSourceSPDIF cmbSetSyncSource sldInput12 valueChanged(int) Phase24ControlUI setVolume12(int) 20 20 20 20 sldInput34 valueChanged(int) Phase24ControlUI setVolume34(int) 20 20 20 20 sldLineIn valueChanged(int) Phase24ControlUI setVolumeLineIn(int) 20 20 20 20 sldSPDIFOut valueChanged(int) Phase24ControlUI setVolumeSPDIFOut(int) 20 20 20 20 sldSPDIFIn valueChanged(int) Phase24ControlUI setVolumeSPDIFIn(int) 20 20 20 20 sldMaster valueChanged(int) Phase24ControlUI setVolumeMaster(int) 20 20 20 20 cmbFrontLevel activated(int) Phase24ControlUI setFrontLevel(int) 20 20 20 20 cmbSetSyncSource activated(int) Phase24ControlUI setSyncSource(int) 20 20 20 20 cmbOutSourceSPDIF activated(int) Phase24ControlUI setOutSourceSPDIF(int) 20 20 20 20 cmbOutSource12 activated(int) Phase24ControlUI setOutSource12(int) 20 20 20 20 cmbOutSource34 activated(int) Phase24ControlUI setOutSource34(int) 20 20 20 20 cmbLineLevel activated(int) Phase24ControlUI setLineLevel(int) 20 20 20 20 libffado-2.4.5/support/mixer-qt4/ffado/mixer/phase24control.py0000644000175000001440000001250414206145246023660 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QWidget from ffado.import_pyqt import * from ffado.config import * import logging log = logging.getLogger('phase24') class Phase24Control(QWidget): def __init__(self,parent = None): QWidget.__init__(self,parent) uicLoad("ffado/mixer/phase24", self) self.VolumeControls={ 'analogin' : ['/Mixer/Feature_Volume_6', self.sldLineIn], 'spdifin' : ['/Mixer/Feature_Volume_7', self.sldSPDIFIn], 'out12' : ['/Mixer/Feature_Volume_3', self.sldInput12], 'out34' : ['/Mixer/Feature_Volume_4', self.sldInput34], 'outspdif' : ['/Mixer/Feature_Volume_5', self.sldSPDIFOut], } self.SelectorControls={ 'outsource12': ['/Mixer/Selector_1', self.cmbOutSource12], 'outsource34': ['/Mixer/Selector_2', self.cmbOutSource34], 'outsourcespdif': ['/Mixer/Selector_3', self.cmbOutSourceSPDIF], 'syncsource': ['/Mixer/Selector_4', self.cmbSetSyncSource], } # public slot def setVolume12(self,a0): self.setVolume('out12', a0) # public slot def setVolume34(self,a0): self.setVolume('out34', a0) # public slot def setVolumeLineIn(self,a0): self.setVolume('analogin', a0) # public slot def setVolumeSPDIFOut(self,a0): self.setVolume('outspdif', a0) # public slot def setVolumeSPDIFIn(self,a0): self.setVolume('spdifin', a0) # public slot def setVolumeMaster(self,a0): if self.isPhaseX24: return self.setVolume('master', a0) # public slot def setLineLevel(self,a0): log.debug("setting line level to %d" % (a0 * -768)) self.hw.setContignuous('/Mixer/Feature_Volume_2', a0 * -768) # public slot def setFrontLevel(self,a0): if self.isPhaseX24: return if(a0 == 0): log.debug("setting front level to %d" % (0)) self.hw.setContignuous('/Mixer/Feature_Volume_8', 0) else: log.debug("setting front level to %d" % (1536)) self.hw.setContignuous('/Mixer/Feature_Volume_8', 1536) # public slot def setOutSource12(self,a0): self.setSelector('outsource12', a0) # public slot def setOutSource34(self,a0): self.setSelector('outsource34', a0) # public slot def setOutSourceSPDIF(self,a0): self.setSelector('outsourcespdif', a0) # public slot def setSyncSource(self,a0): self.setSelector('syncsource', a0) def setVolume(self,a0,a1): name=a0 vol = -a1 log.debug("setting %s volume to %d" % (name, vol)) self.hw.setContignuous(self.VolumeControls[name][0], vol) def setSelector(self,a0,a1): name=a0 state = a1 log.debug("setting %s state to %d" % (name, state)) self.hw.setDiscrete(self.SelectorControls[name][0], state) def initValues(self): self.modelId = self.configrom.getModelId() if self.modelId == 0x00000007: self.isPhaseX24 = True else: self.isPhaseX24 = False if self.isPhaseX24: self.setWindowTitle("Terratec Phase X24 Control") self.cmbFrontLevel.setEnabled(False) self.sldMaster.setEnabled(False) else: self.setWindowTitle("Terratec Phase 24 Control") self.VolumeControls['master'] = ['/Mixer/Feature_Volume_1', self.sldMaster] self.sldMaster.setEnabled(True) self.cmbFrontLevel.setEnabled(True) val = self.hw.getContignuous('/Mixer/Feature_Volume_8') if val > 0: self.cmbFrontLevel.setCurrentIndex(1) else: self.cmbFrontLevel.setCurrentIndex(0) for name, ctrl in self.VolumeControls.items(): vol = self.hw.getContignuous(ctrl[0]) log.debug("%s volume is %d" % (name , vol)) ctrl[1].setValue(-vol) for name, ctrl in self.SelectorControls.items(): state = self.hw.getDiscrete(ctrl[0]) log.debug("%s state is %d" % (name , state)) ctrl[1].setCurrentIndex(state) val = self.hw.getContignuous('/Mixer/Feature_Volume_2')/-768 if val > 4: self.cmbLineLevel.setCurrentIndex(4) else: self.cmbLineLevel.setCurrentIndex(val) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/phase88.ui0000644000175000001440000027635114206145246022272 0ustar jwoitheusers Copyright (C) 2014 by András Murányi Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Phase88ControlUI 0 0 977 758 Terratec Phase 88 Control QToolButton:checked { background-color: red; color: white; border-radius: 4px; } Hardware Mixer 2 2 3 0 0 QFrame::StyledPanel QFrame::Plain 4 0 0 S/PDIF in Qt::AlignCenter false 0 0 Line in 3/4 Qt::AlignCenter false 0 0 Line in 1/2 Qt::AlignCenter false 0 0 WavePlay Qt::AlignCenter false 0 0 Line/Back Mic/Front 0 0 Line in 5/6 Qt::AlignCenter false 0 0 Line in 7/8 Qt::AlignCenter false 0 0 S/PDIF CH 1/2 CH 3/4 CH5/6 CH7/8 0 0 QFrame::StyledPanel QFrame::Plain 1 1 3 Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksBelow 3276 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksAbove 3276 Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 0 0 Stereo Link true Qt::Horizontal 40 20 0 0 QFrame::StyledPanel QFrame::Plain 1 1 3 Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksBelow 3276 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksAbove 3276 Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Stereo Link true Qt::Horizontal 40 20 0 0 QFrame::StyledPanel QFrame::Plain 1 1 3 Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksBelow 3276 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksAbove 3276 Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Stereo Link true Qt::Horizontal 40 20 0 0 QFrame::StyledPanel QFrame::Plain 1 1 3 Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksBelow 3276 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksAbove 3276 Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Stereo Link true Qt::Horizontal 40 20 0 0 QFrame::StyledPanel QFrame::Plain 1 1 3 Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksBelow 3276 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksAbove 3276 Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Stereo Link true Qt::Horizontal 40 20 0 0 QFrame::StyledPanel QFrame::Plain 1 1 3 Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksBelow 3276 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksAbove 3276 Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Stereo Link true Qt::Horizontal 40 20 0 0 QFrame::StyledPanel QFrame::Plain 11 1 3 Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 55 0 -0dB Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksBelow 3276 Qt::Horizontal 40 20 0 0 -25600 0 1000 10000 Qt::Vertical QSlider::TicksAbove 3276 Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 Mute true Qt::Horizontal 40 20 0 Qt::Horizontal 40 20 Stereo Link true Qt::Horizontal 40 20 0 0 S/PDIF Out Line Out 1/2 Line Out 3/4 Line Out 5/6 Line Out 7/8 None 0 0 Master Qt::AlignCenter false Sync Source false Internal External External Sync Source false S/PDIF WordClock qPixmapFromMimeSource muteButton1 toggled(bool) sldInput1 setDisabled(bool) 46 622 61 581 muteButton2 toggled(bool) sldInput2 setDisabled(bool) 95 619 104 559 muteButton3 toggled(bool) sldInput3 setDisabled(bool) 170 621 178 534 muteButton4 toggled(bool) sldInput4 setDisabled(bool) 242 613 235 549 muteButton5 toggled(bool) sldInput5 setDisabled(bool) 285 617 297 540 muteButton6 toggled(bool) sldInput6 setDisabled(bool) 361 619 356 545 muteButton7 toggled(bool) sldInput7 setDisabled(bool) 408 614 415 550 muteButton8 toggled(bool) sldInput8 setDisabled(bool) 480 618 477 553 muteButtonSPDIFL toggled(bool) sldInputSPDIFL setDisabled(bool) 522 617 532 555 muteButtonSPDIFR toggled(bool) sldInputSPDIFR setDisabled(bool) 600 616 596 532 muteButtonWavePlayL toggled(bool) sldInputWavePlayL setDisabled(bool) 674 619 667 554 muteButtonWavePlayR toggled(bool) sldInputWavePlayR setDisabled(bool) 717 621 715 552 muteButtonMasterL toggled(bool) sldInputMasterL setDisabled(bool) 826 621 814 532 muteButtonMasterR toggled(bool) sldInputMasterR setDisabled(bool) 882 621 890 545 libffado-2.4.5/support/mixer-qt4/ffado/mixer/phase88control.py0000644000175000001440000002050614206145246023673 0ustar jwoitheusers# # Copyright (C) 2014-2015 by Andras Muranyi # Copyright (c) 2013 by Takashi Sakamoto (Yamaha GO Control) # Copyright (C) 2005-2008 by Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QWidget from ffado.import_pyqt import * from math import log10 from ffado.config import * import logging log = logging.getLogger('phase88') class Phase88Control(QWidget): def __init__(self,parent = None): QWidget.__init__(self,parent) def initValues(self): uicLoad("ffado/mixer/phase88", self) self.VolumeControls={ self.sldInputMasterL: ['/Mixer/Feature_Volume_1', 1, self.sldInputMasterR, 2, self.linkButtonMaster, self.decibelMasterL, 0], self.sldInputMasterR: ['/Mixer/Feature_Volume_1', 2, self.sldInputMasterL, 1, self.linkButtonMaster, self.decibelMasterR, 0], self.sldInput1: ['/Mixer/Feature_Volume_2', 1, self.sldInput2, 2, self.linkButton12, self.decibel1, 0], self.sldInput2: ['/Mixer/Feature_Volume_2', 2, self.sldInput1, 1, self.linkButton12, self.decibel2, 0], self.sldInput3: ['/Mixer/Feature_Volume_3', 1, self.sldInput4, 2, self.linkButton34, self.decibel3, 0], self.sldInput4: ['/Mixer/Feature_Volume_3', 2, self.sldInput3, 1, self.linkButton34, self.decibel4, 0], self.sldInput5: ['/Mixer/Feature_Volume_4', 1, self.sldInput6, 2, self.linkButton56, self.decibel5, 0], self.sldInput6: ['/Mixer/Feature_Volume_4', 2, self.sldInput5, 1, self.linkButton56, self.decibel6, 0], self.sldInput7: ['/Mixer/Feature_Volume_5', 1, self.sldInput8, 2, self.linkButton78, self.decibel7, 0], self.sldInput8: ['/Mixer/Feature_Volume_5', 2, self.sldInput7, 1, self.linkButton78, self.decibel8, 0], self.sldInputSPDIFL: ['/Mixer/Feature_Volume_6', 1, self.sldInputSPDIFR, 2, self.linkButtonSPDIF, self.decibelSPDIFL, 0], self.sldInputSPDIFR: ['/Mixer/Feature_Volume_6', 2, self.sldInputSPDIFL, 1, self.linkButtonSPDIF, self.decibelSPDIFR, 0], self.sldInputWavePlayL: ['/Mixer/Feature_Volume_7', 1, self.sldInputWavePlayR, 2, self.linkButtonWavePlay, self.decibelWavePlayL, 0], self.sldInputWavePlayR: ['/Mixer/Feature_Volume_7', 2, self.sldInputWavePlayL, 1, self.linkButtonWavePlay, self.decibelWavePlayR, 0] } self.SelectorControls={ self.comboOutAssign: '/Mixer/Selector_6', self.comboInAssign: '/Mixer/Selector_7', self.comboExtSync: '/Mixer/Selector_8', self.comboSyncSource: '/Mixer/Selector_9', self.comboFrontBack: '/Mixer/Selector_10' } self.MuteControls={ self.muteButtonMasterL: [self.sldInputMasterL, self.muteButtonMasterR], self.muteButtonMasterR: [self.sldInputMasterR, self.muteButtonMasterL], self.muteButton1: [self.sldInput1, self.muteButton2], self.muteButton2: [self.sldInput2, self.muteButton1], self.muteButton3: [self.sldInput3, self.muteButton4], self.muteButton4: [self.sldInput4, self.muteButton3], self.muteButton5: [self.sldInput5, self.muteButton6], self.muteButton6: [self.sldInput6, self.muteButton5], self.muteButton7: [self.sldInput7, self.muteButton8], self.muteButton8: [self.sldInput8, self.muteButton7], self.muteButtonSPDIFL: [self.sldInputSPDIFL, self.muteButtonSPDIFR], self.muteButtonSPDIFR: [self.sldInputSPDIFR, self.muteButtonSPDIFL], self.muteButtonWavePlayL: [self.sldInputWavePlayL, self.muteButtonWavePlayR], self.muteButtonWavePlayR: [self.sldInputWavePlayR, self.muteButtonWavePlayL] } # gain control for ctl, params in self.VolumeControls.items(): path = params[0] idx = params[1] dbmeter = params[5] #db = self.hw.getContignuous(path, idx) #vol = self.db2vol(db) vol = self.hw.getContignuous(path, idx) print("%s ch %d volume is %d" % (path, idx, vol)) ctl.setValue(vol) dbmeter.setText(self.vol2dbstr(vol)) self.VolumeControls[ctl][6] = vol pair = params[2] pidx = params[3] link = params[4] # pdb = self.hw.getContignuous(path, pidx) # pvol = self.db2vol(db) pvol = self.hw.getContignuous(path, pidx) if pvol == vol: link.setChecked(True) ctl.valueChanged.connect(self.updateVolume) # selector controls for ctl, param in self.SelectorControls.items(): state = self.hw.getDiscrete(param) ctl.setCurrentIndex(state) ctl.activated.connect(self.updateSelector) # mute controls for ctl, param in self.MuteControls.items(): ctl.toggled.connect(self.muteVolume) # helper functions def muteVolume(self, state): sender = self.sender() volctl = self.MuteControls[sender][0] path = self.VolumeControls[volctl][0] idx = self.VolumeControls[volctl][1] pair = self.VolumeControls[volctl][2] pidx = self.VolumeControls[volctl][3] link = self.VolumeControls[volctl][4] savedvol = self.VolumeControls[volctl][6] psavedvol = self.VolumeControls[pair][6] if state == True : self.hw.setContignuous(path, -25600, idx) # The PHASE88 supports volume between 0 and -25600 if link.isChecked(): pair.setDisabled(state) self.MuteControls[sender][1].setChecked(state) # self.hw.setContignuous(path, 0, pidx) else: self.hw.setContignuous(path, savedvol, idx) if link.isChecked(): pair.setDisabled(state) self.MuteControls[sender][1].setChecked(state) # self.hw.setContignuous(path, psavedvol, pidx) def updateVolume(self, vol): sender = self.sender() path = self.VolumeControls[sender][0] idx = self.VolumeControls[sender][1] pair = self.VolumeControls[sender][2] pidx = self.VolumeControls[sender][3] link = self.VolumeControls[sender][4] dbmeter = self.VolumeControls[sender][5] #db = self.vol2dbstr(vol) #self.hw.setContignuous(path, db, idx) self.hw.setContignuous(path, vol, idx) dbmeter.setText(self.vol2dbstr(vol)) self.VolumeControls[sender][6] = vol if link.isChecked(): pair.setValue(vol) def updateSelector(self, state): sender = self.sender() path = self.SelectorControls[sender] self.hw.setDiscrete(path, state) # if path == '/Mixer/Selector_7' # ctrl = self.VolumeControls['line78'] # vol = self.hw.getContignuous(ctrl[0]) # Recall volume for selected channel ******************************************************* # name = 'line78' # log.debug("%s volume is %d" % (name , vol)) # ctrl[1].setValue(-vol) def vol2dbstr(self, vol): vol = vol + 25600 if vol == 0 : return "- "+u"\u221E"+" dB" return str("{0:.2f}".format(log10( float(abs(vol) + 0.001) / 25600 ) * 20))+"dB" # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/presonus_firebox.py0000644000175000001440000002763314206145246024416 0ustar jwoitheusers# # presonus_firebox.py - Qt4/FFADO application for Presonus FIREBOX # Copyright (C) 2013 Takashi Sakamoto # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4 import QtGui, QtCore # from PyQt4.QtCore import Qt # from PyQt4.QtGui import QHBoxLayout, QVBoxLayout, QGridLayout # from PyQt4.QtGui import QWidget, QTabWidget, QGroupBox, QLabel, QSizePolicy, QToolButton, QSlider, QComboBox, QSpacerItem, QDial from ffado.import_pyqt import * from math import log10 from ffado.config import * class Presonus_Firebox(QWidget): #name/feature_id/+12dB_id inputs = [["Analog in 1/2", 5, 10], ["Analog in 3/4", 6, 11], ["Digital in 1/2", 7, 0], ["Stream in", 8, 0]] # name/id for mixer sorce selector streams = [["Stream in 1/2", 0], ["Stream in 3/4", 1], ["Stream in 5/6", 2], ["Stream in 7/8", 3]] # name/feature id outputs = [["Analog out 1/2", 1], ["Analog out 3/4", 2], ["Analog out 5/6", 3], ["Digital out 1/2", 0], ["Headphone out 1/2", 4]] # selector_id/sources out_src = [[1, ["Stream in 1/2", "Mixer out 1/2"]], [2, ["Stream in 3/4", "Mixer out 1/2"]], [3, ["Stream in 5/6", "Mixer out 1/2"]], [5, ["Stream in 7/8", "Mixer out 1/2"]], [4, ["Stream in 1/2", "Stream in 3/4", "Stream in 5/6", "Stream in 7/8", "Mixer out 1/2"]]] def __init__(self, parent=None): QWidget.__init__(self, parent) def getDisplayTitle(self): return 'Firebox' def buildMixer(self): self.Selectors = {} self.MicBoosts = {} self.Volumes = {} self.Mutes = {} self.Balances = {} tabs = QTabWidget(self) tabs_layout = QHBoxLayout(self) tabs_layout.addWidget(tabs) self.addMixer(tabs) self.addOutput(tabs) def addMixer(self, tabs): tab_mixer = QWidget(tabs) tabs.addTab(tab_mixer, "Mixer") tab_mixer_layout = QGridLayout() tab_mixer.setLayout(tab_mixer_layout) for i in range(len(self.inputs)): col = 2 * i; label = QLabel(tab_mixer) tab_mixer_layout.addWidget(label, 0, col, 1, 2, Qt.AlignHCenter) label.setText(self.inputs[i][0]) label.setAlignment(Qt.AlignCenter) label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) link = self.getLink(tab_mixer) tab_mixer_layout.addWidget(link, 3, col, 1, 2, Qt.AlignHCenter) if self.inputs[i][2] > 0: for j in range(1, 3): button = QToolButton(tab_mixer) tab_mixer_layout.addWidget(button, 1, col + j - 1, Qt.AlignHCenter) button.setText('+12dB') button.setCheckable(True) button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) path = "/Mixer/Feature_Volume_%d" % self.inputs[i][2] self.MicBoosts[button] = [path, j] elif self.inputs[i][0].find('Stream') >= 0: cmb = QComboBox(tab_mixer) tab_mixer_layout.addWidget(cmb, 1, col, 1, 2, Qt.AlignHCenter) for i in range(len(self.streams)): cmb.addItem(self.streams[i][0], self.streams[i][1]) self.Selectors[cmb] = ["/Mixer/Selector_6"] l_sld = self.getSlider(tab_mixer) r_sld = self.getSlider(tab_mixer) tab_mixer_layout.addWidget(l_sld, 2, col, Qt.AlignHCenter) tab_mixer_layout.addWidget(r_sld, 2, col + 1, Qt.AlignHCenter) mute = self.getMute(tab_mixer) tab_mixer_layout.addWidget(mute, 4, col, 1, 2, Qt.AlignHCenter) path = "/Mixer/Feature_Volume_%d" % self.inputs[i][1] self.Volumes[l_sld] = [path, 1, r_sld, link, mute] self.Volumes[r_sld] = [path, 2, l_sld, link, mute] self.Mutes[mute] = [path, l_sld, r_sld] for j in range(0, 2): dial = self.getDial(tab_mixer) tab_mixer_layout.addWidget(dial, 5, col + j, Qt.AlignHCenter) if self.inputs[i][2] > 0: path = "/Mixer/Feature_LRBalance_%d" % self.inputs[i][1] self.Balances[dial] = [path, j + 1] # to keep width else: dial.setDisabled(True) def addOutput(self, tabs): tab_out = QWidget(self) tabs.addTab(tab_out, "Output") tab_out_layout = QGridLayout() tab_out.setLayout(tab_out_layout) for i in range(len(self.outputs)): col = 2 * i label = QLabel(tab_out) tab_out_layout.addWidget(label, 0, col, 1, 2, Qt.AlignCenter) label.setText(self.outputs[i][0]) cmb = QComboBox(tab_out) tab_out_layout.addWidget(cmb, 1, col, 1, 2, Qt.AlignHCenter) for j in range(len(self.out_src[i][1])): cmb.addItem(self.out_src[i][1][j]) self.Selectors[cmb] = ["/Mixer/Selector_%d" % self.out_src[i][0]] if self.outputs[i][1] == 0: continue l_sld = self.getSlider(tab_out) r_sld = self.getSlider(tab_out) tab_out_layout.addWidget(l_sld, 2, col, Qt.AlignHCenter) tab_out_layout.addWidget(r_sld, 2, col + 1, Qt.AlignHCenter) link = self.getLink(tab_out) tab_out_layout.addWidget(link, 3, col, 1, 2, Qt.AlignHCenter) mute = self.getMute(tab_out) tab_out_layout.addWidget(mute, 4, col, 1, 2, Qt.AlignHCenter) path = "/Mixer/Feature_Volume_%d" % self.outputs[i][1] self.Volumes[l_sld] = [path, 1, r_sld, link, mute] self.Volumes[r_sld] = [path, 2, l_sld, link, mute] self.Mutes[mute] = [path, l_sld, r_sld] def getSlider(self, parent): sld = QSlider(parent) sld.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) sld.setMinimum(0) sld.setMaximum(99) sld.setPageStep(10) sld.setPageStep(10) sld.setMinimumHeight(50) sld.setTickInterval(25) sld.setTickPosition(QSlider.TicksBothSides) return sld def getDial(self, parent): dial = QDial(parent) dial.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) dial.setMinimumSize(50, 50) dial.setMaximumHeight(50) dial.setNotchTarget(25) dial.setNotchesVisible(True) dial.setMinimum(0) dial.setMaximum(99) dial.setPageStep(10) dial.setPageStep(10) return dial def getLink(self, parent): link = QToolButton(parent) link.setText("link") link.setCheckable(True) link.setMinimumWidth(100) link.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) return link def getMute(self, parent): mute = QToolButton(parent) mute.setText("mute") mute.setCheckable(True) mute.setMinimumWidth(100) mute.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) return mute def initValues(self): for elm, params in self.Selectors.items(): path = params[0] state = self.hw.getDiscrete(path) elm.setCurrentIndex(state) elm.activated.connect(self.updateSelector) for elm, params in self.MicBoosts.items(): path = params[0] idx = params[1] value = self.hw.getContignuous(path, idx) elm.setChecked(not value == 0) elm.clicked.connect(self.updateMicBoost) for elm, params in self.Volumes.items(): path = params[0] idx = params[1] db = self.hw.getContignuous(path, idx) vol = self.db2vol(db) elm.setValue(vol) elm.valueChanged.connect(self.updateVolume) if idx == 0: continue p_idx = 0 pair = params[2] link = params[3] mute = params[4] p_db = self.hw.getContignuous(path, p_idx) if db == p_db: link.setChecked(True) for elm, params in self.Mutes.items(): path = params[0] l_elm = params[1] r_elm = params[2] l_db = self.hw.getContignuous(path, 1) r_db = self.hw.getContignuous(path, 2) l_vol = self.db2vol(l_db) r_vol = self.db2vol(r_db) if l_vol == r_vol and l_vol == 0: elm.setChecked(True) l_elm.setDisabled(True) r_elm.setDisabled(True) elm.clicked.connect(self.updateMute) for elm, params in self.Balances.items(): path = params[0] idx = params[1] pan = self.hw.getContignuous(path, idx) val = self.pan2val(pan) elm.setValue(val) elm.valueChanged.connect(self.updateBalance) # helper functions def vol2db(self, vol): return (log10(vol + 1) - 2) * 16384 def db2vol(self, db): return pow(10, db / 16384 + 2) - 1 # Right - Center - Left # 0x8000 - 0x0000 - 0x0001 - 0x7FFE # ..., -1, 0, +1, ... def val2pan(self, val): return (val - 50) * 0x7ffe / -50 def pan2val(self, pan): return -(pan/ 0x7ffe) * 50 + 50 def updateSelector(self, state): sender = self.sender() path = self.Selectors[sender][0] self.hw.setDiscrete(path, state) def updateVolume(self, vol): sender = self.sender() path = self.Volumes[sender][0] idx = self.Volumes[sender][1] pair = self.Volumes[sender][2] link = self.Volumes[sender][3] mute = self.Volumes[sender][4] db = self.vol2db(vol) self.hw.setContignuous(path, db, idx) if idx == 1: p_idx = 2 else: p_idx = 1 if link.isChecked(): self.hw.setContignuous(path, db, p_idx) pair.setValue(vol) def updateMicBoost(self, state): sender = self.sender() path = self.MicBoosts[sender][0] idx = self.MicBoosts[sender][1] if state: value = 0x7ffe else: value = 0x0000 self.hw.setContignuous(path, value, idx) def updateBalance(self, val): sender = self.sender() path = self.Balances[sender][0] idx = self.Balances[sender][1] pan = self.val2pan(val) self.hw.setContignuous(path, pan, idx) def updateMute(self, state): sender = self.sender() path = self.Mutes[sender][0] l_elm = self.Mutes[sender][1] r_elm = self.Mutes[sender][2] if state: # mute emulation db = self.vol2db(10) l_elm.setValue(0) l_elm.setDisabled(True) r_elm.setValue(0) r_elm.setDisabled(True) else: db = self.vol2db(99) l_elm.setDisabled(False) l_elm.setValue(99) r_elm.setDisabled(False) r_elm.setValue(99) self.hw.setContignuous(path, db, 1) self.hw.setContignuous(path, db, 2) libffado-2.4.5/support/mixer-qt4/ffado/mixer/presonus_fp10.py0000644000175000001440000001553414206145246023523 0ustar jwoitheusers# # presonus_firebox.py - Qt4/FFADO application for Presonus FIREBOX # Copyright (C) 2013 Takashi Sakamoto # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4 import QtGui, QtCore # from PyQt4.QtCore import Qt # from PyQt4.QtGui import QGridLayout # from PyQt4.QtGui import QWidget, QLabel, QSizePolicy, QToolButton, QSlider, QDial from ffado.import_pyqt import * from math import log10 from ffado.config import * class Presonus_FP10(QWidget): outputs = [["Analog Out 1/2", 1], ["Analog Out 3/4", 2], ["Analog Out 5/6", 3], ["Analog Out 7/8", 4]] def __init__(self, parent=None): QWidget.__init__(self, parent) def getDisplayTitle(self): return 'FP10' def buildMixer(self): self.Volumes = {} self.Mutes = {} self.Pannings = {} layout = QGridLayout(self) for i in range(len(self.outputs)): self.addVolume(self.outputs[i][0], self.outputs[i][1], i, layout) def addVolume(self, name, bid, col, layout): label = QLabel(self) label.setText(name) l_dial = self.getDial() r_dial = self.getDial() l_sld = self.getSlider() r_sld = self.getSlider() l_mute = self.getMute() r_mute = self.getMute() link = self.getLink() layout.addWidget(label, 1, col * 2, 1, 2, Qt.AlignHCenter) layout.addWidget(l_dial, 2, col * 2, Qt.AlignHCenter) layout.addWidget(r_dial, 2, col * 2 + 1, Qt.AlignHCenter) layout.addWidget(l_sld, 3, col * 2, Qt.AlignHCenter) layout.addWidget(r_sld, 3, col * 2 + 1, Qt.AlignHCenter) layout.addWidget(l_mute, 4, col * 2, Qt.AlignHCenter) layout.addWidget(r_mute, 4, col * 2 + 1, Qt.AlignHCenter) layout.addWidget(link, 5, col * 2, 1, 2, Qt.AlignHCenter) path = "/Mixer/Feature_Volume_%d" % bid self.Volumes[l_sld] = [path, 1, r_sld, l_mute, link] self.Volumes[r_sld] = [path, 2, l_sld, r_mute, link] self.Mutes[l_mute] = [r_mute, l_sld] self.Mutes[r_mute] = [l_mute, r_sld] path = "/Mixer/Feature_LRBalance_%d" % bid self.Pannings[l_dial] = [path, 1] self.Pannings[r_dial] = [path, 2] # widget helper functions def getSlider(self): sld = QSlider(self) sld.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) sld.setMinimum(0) sld.setMaximum(99) sld.setPageStep(10) sld.setPageStep(10) sld.setMinimumHeight(50) sld.setTickInterval(25) sld.setTickPosition(QSlider.TicksBothSides) return sld def getDial(self): dial = QDial(self) dial.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) dial.setMinimumSize(50, 50) dial.setMaximumHeight(50) dial.setNotchTarget(25) dial.setNotchesVisible(True) dial.setMinimum(0) dial.setMaximum(99) dial.setPageStep(10) dial.setPageStep(10) return dial def getLink(self): link = QToolButton(self) link.setText("link") link.setCheckable(True) link.setMinimumWidth(100) link.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) return link def getMute(self): mute = QToolButton(self) mute.setText("mute") mute.setCheckable(True) mute.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) return mute def initValues(self): for ctl, params in self.Volumes.items(): path = params[0] idx = params[1] pair = params[2] mute = params[3] link = params[4] db = self.hw.getContignuous(path, idx) vol = self.db2vol(db) ctl.setValue(vol) ctl.valueChanged.connect(self.updateVolume) if vol == 0: mute.setChecked(True) if idx == 2: pair_db = self.hw.getContignuous(path, 1) if pair_db == db: link.setChecked(True) # Right - Center - Left # 0x8000 - 0x0000 - 0x0001 - 0x7FFE # ..., -1, 0, +1, ... for ctl, params in self.Pannings.items(): path = params[0] idx = params[1] val = self.hw.getContignuous(path, idx) state = -(val / 0x7FFE) * 50 + 50 ctl.setValue(state) ctl.valueChanged.connect(self.updatePanning) for ctl, params in self.Mutes.items(): ctl.clicked.connect(self.updateMute) return # helper functions def vol2db(self, vol): return (log10(vol + 1) - 2) * 16384 def db2vol(self, db): return pow(10, db / 16384 + 2) - 1 def updateVolume(self, vol): sender = self.sender() path = self.Volumes[sender][0] idx = self.Volumes[sender][1] pair = self.Volumes[sender][2] mute = self.Volumes[sender][3] link = self.Volumes[sender][4] if mute.isChecked(): return db = self.vol2db(vol) self.hw.setContignuous(path, db, idx) if link.isChecked(): pair.setValue(vol) def updatePanning(self, state): sender = self.sender() path = self.Pannings[sender][0] idx = self.Pannings[sender][1] val = (state - 50) * 0x7FFE / -50 self.hw.setContignuous(path, idx, val) def updateMute(self, state): sender = self.sender() pair = self.Mutes[sender][0] sld = self.Mutes[sender][1] path = self.Volumes[sld][0] idx = self.Volumes[sld][1] pair_sld = self.Volumes[sld][2] link = self.Volumes[sld][4] if state: db = 0x8000 vol = 0 else: db = 0x0000 vol = 99 self.hw.setContignuous(path, db, idx) sld.setValue(vol) sld.setDisabled(state) if link.isChecked(): if idx == 1: idx = 2 else: idx = 1 self.hw.setContignuous(path, db, idx) pair.setChecked(state) pair_sld.setValue(vol) pair_sld.setDisabled(state) libffado-2.4.5/support/mixer-qt4/ffado/mixer/presonus_inspire1394.py0000644000175000001440000002516114206145246024744 0ustar jwoitheusers# # presonus_inspire1394.py - Qt4/FFADO application for Presonus Inspire1394 # Copyright (C) 2014 Takashi Sakamoto # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4 import QtGui, QtCore # from PyQt4.QtCore import QObject, Qt # from PyQt4.QtGui import QWidget, QHBoxLayout, QVBoxLayout, QGridLayout # from PyQt4.QtGui import QGroupBox, QLabel, QSizePolicy, QSlider, QDial, QComboBox, QToolButton from ffado.import_pyqt import * from math import log10 from ffado.config import * class Presonus_Inspire1394(QWidget): # feature_id/name inputs = [[1, "Analog in 1/2"], [2, "Analog in 3/4"]] # feature_id/name mixer_src = [[3, "Analog in 1/2"], [4, "Analog in 3/4"], [5, "Stream in 1/2"]] # feature id/name outputs = [[6, "Analog out 1/2"], [7, "HP out 1/2"]] # selector_id/sources out_src = [1, ["Mixer out 1/2", "Stream in 1/2"]] def __init__(self, parent=None): QWidget.__init__(self, parent) def getDisplayTitle(self): return 'Inspire1394' def buildMixer(self): self.Selectors = {} self.Volumes = {} self.Preamps = {} self.Pannings = {} self.Mutes = {} plain_layout = QHBoxLayout(self) left = QGroupBox(self) plain_layout.addWidget(left) self.addAnalogInputs(left) center = QGroupBox(self) plain_layout.addWidget(center) self.addInternalMixer(center) right = QGroupBox(self) plain_layout.addWidget(right) self.addAnalogOutputs(right) def addAnalogInputs(self, box): box_layout = QVBoxLayout() box.setLayout(box_layout) box.setTitle("Analog Inputs") grid_layout = QGridLayout() box_layout.addLayout(grid_layout) self.addVolumes(self.inputs, 0, box, grid_layout) def addAnalogOutputs(self, box): box_layout = QVBoxLayout() box.setLayout(box_layout) box.setTitle("Analog Outputs") grid_layout = QGridLayout() box_layout.addLayout(grid_layout) self.addVolumes(self.outputs, 2, box, grid_layout) def addInternalMixer(self, box): box_layout = QGridLayout() box.setLayout(box_layout) box.setTitle("Hardware Mixer") self.addVolumes(self.mixer_src, 1, box, box_layout) def addVolumes(self, elms, kind, parent, layout): def addPreampParam(label, ch, path, layout): button = self.getThinButton(parent, label) layout.addWidget(button) self.Preamps[button] = ["/Preamp/%s" % path, ch] return for col in range(len(elms)): label = QLabel(parent) label.setText(elms[col][1]) layout.addWidget(label, 0, col * 2, 1, 2, Qt.AlignHCenter | Qt.AlignTop) if kind == 0: if col == 0: for ch in range(2): box_layout = QVBoxLayout() layout.addLayout(box_layout, 1, col * 2 + ch, Qt.AlignHCenter | Qt.AlignBottom) if col == 0: addPreampParam("+48V", ch + 1, "PhantomPower", box_layout) addPreampParam("Boost", ch + 1, "MicBoost", box_layout) addPreampParam("Limit", ch + 1, "MicLimit", box_layout) else: box_layout = QVBoxLayout() addPreampParam("Phono", 0, "PhonoSwitch", box_layout) layout.addLayout(box_layout, 1, col * 2, 1, 2, Qt.AlignHCenter | Qt.AlignBottom) elif kind == 1: l_dial = self.getDial(parent) r_dial = self.getDial(parent) layout.addWidget(l_dial, 1, col * 2, Qt.AlignHCenter | Qt.AlignBottom) layout.addWidget(r_dial, 1, col * 2 + 1, Qt.AlignHCenter | Qt.AlignBottom) path = "/Mixer/Feature_LRBalance_%d" % elms[col][0] self.Pannings[l_dial] = [path, 1] self.Pannings[r_dial] = [path, 2] if col == 2: l_dial.setDisabled(True) r_dial.setDisabled(True) elif col == 0: cmb = QComboBox(parent) layout.addWidget(cmb, 1, col * 2, 1, 4, Qt.AlignHCenter | Qt.AlignBottom) for i in range(len(self.out_src[1])): cmb.addItem(self.out_src[1][i], i) self.Selectors[cmb] = ["/Mixer/Selector_%d" % self.out_src[0]] l_sld = self.getSlider(parent) r_sld = self.getSlider(parent) layout.addWidget(l_sld, 2, col * 2, Qt.AlignHCenter) layout.addWidget(r_sld, 2, col * 2 + 1, Qt.AlignHCenter) l_mute = self.getThinButton(parent, "Mute") r_mute = self.getThinButton(parent, "Mute") layout.addWidget(l_mute, 3, col * 2, Qt.AlignHCenter) layout.addWidget(r_mute, 3, col * 2 + 1, Qt.AlignHCenter) link = self.getWideButton(parent, "Link") layout.addWidget(link, 4, col * 2, 1, 2, Qt.AlignHCenter) path = "/Mixer/Feature_Volume_%d" % elms[col][0] self.Volumes[l_sld] = [path, 1, r_sld, l_mute, link] self.Volumes[r_sld] = [path, 2, l_sld, r_mute, link] self.Mutes[l_mute] = [r_mute, l_sld] self.Mutes[r_mute] = [l_mute, r_sld] # widget helper functions def getSlider(self, parent): sld = QSlider(parent) sld.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) sld.setMinimum(0) sld.setMaximum(99) sld.setPageStep(10) sld.setPageStep(10) sld.setMinimumHeight(50) sld.setTickInterval(25) sld.setTickPosition(QSlider.TicksBothSides) return sld def getDial(self, parent): dial = QDial(parent) dial.setNotchesVisible(True) dial.setNotchTarget(25.0) dial.setMaximumHeight(40) return dial; def getThinButton(self, parent, text): button = QToolButton(parent) button.setText(text) button.setCheckable(True) button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) return button def getWideButton(self, parent, label): button = QToolButton(parent) button.setText(label) button.setCheckable(True) button.setMinimumWidth(100) button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) return button def initValues(self): for ctl, params in self.Selectors.items(): path = params[0] state = self.hw.getDiscrete(path) ctl.setCurrentIndex(state) ctl.activated.connect(self.updateSelector) for ctl, params in self.Volumes.items(): path = params[0] idx = params[1] pair = params[2] mute = params[3] link = params[4] db = self.hw.getContignuous(path, idx) vol = self.db2vol(db) ctl.setValue(vol) ctl.valueChanged.connect(self.updateVolume) if vol == 0: mute.setChecked(True) if idx == 2: pair_db = self.hw.getContignuous(path, 1) if pair_db == db: link.setChecked(True) for ctl, params in self.Preamps.items(): path = params[0] idx = params[1] vol = self.hw.getDiscrete(path, idx) if vol > 0: ctl.setChecked(True) ctl.clicked.connect(self.updatePreamps) # Right - Center - Left # 0x8000 - 0x0000 - 0x0001 - 0x7FFE # ..., -1, 0, +1, ... for ctl, params in self.Pannings.items(): path = params[0] idx = params[1] val = self.hw.getContignuous(path, idx) state = -(val / 0x7FFE) * 50 + 50 ctl.setValue(state) ctl.valueChanged.connect(self.updatePanning) for ctl, params in self.Mutes.items(): ctl.clicked.connect(self.updateMute) # helper functions def vol2db(self, vol): return (log10(vol + 1) - 2) * 16384 def db2vol(self, db): return pow(10, db / 16384 + 2) - 1 def updateSelector(self, state): sender = self.sender() path = self.Selectors[sender][0] self.hw.setDiscrete(path, state) def updatePreamps(self, state): sender = self.sender() path = self.Preamps[sender][0] idx = self.Preamps[sender][1] self.hw.setDiscrete(path, idx, state) def updateVolume(self, vol): sender = self.sender() path = self.Volumes[sender][0] idx = self.Volumes[sender][1] pair = self.Volumes[sender][2] mute = self.Volumes[sender][3] link = self.Volumes[sender][4] if mute.isChecked(): return db = self.vol2db(vol) self.hw.setContignuous(path, db, idx) if link.isChecked(): pair.setValue(vol) def updatePanning(self, state): sender = self.sender() path = self.Pannings[sender][0] idx = self.Pannings[sender][1] val = (state - 50) * 0x7FFE / -50 self.hw.setContignuous(path, idx, val) def updateMute(self, state): sender = self.sender() pair = self.Mutes[sender][0] sld = self.Mutes[sender][1] path = self.Volumes[sld][0] idx = self.Volumes[sld][1] pair_sld = self.Volumes[sld][2] link = self.Volumes[sld][4] if state: db = 0x8000 vol = 0 else: db = 0x0000 vol = 99 self.hw.setContignuous(path, db, idx) sld.setValue(vol) sld.setDisabled(state) if link.isChecked(): if idx == 1: idx = 2 else: idx = 1 self.hw.setContignuous(path, db, idx) pair.setChecked(state) pair_sld.setValue(vol) pair_sld.setDisabled(state) libffado-2.4.5/support/mixer-qt4/ffado/mixer/profire2626.py0000644000175000001440000000735714206145246023011 0ustar jwoitheusers# # Copyright (C) 2009-2010 by Arnold Krille # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4 import QtGui, QtCore, Qt # from PyQt4.QtGui import QWidget, QScrollArea from ffado.import_pyqt import * import dbus import ffado.config from ffado.widgets.matrixmixer import MatrixMixer from ffado.widgets.crossbarrouter import * from ffado.mixer.generic_dice_eap import * from ffado.config import * class BooleanControl: def __init__(self, hw, path): if ffado.config.bypassdbus: self.value = False return self.iface = dbus.Interface( hw.bus.get_object(hw.servername, path), dbus_interface="org.ffado.Control.Element.Boolean") self.value = self.iface.selected() def selected(self): return self.value def select(self, n): if self.iface.select(n): self.value = n return True return False class DiscreteControl: def __init__(self, hw, path): if ffado.config.bypassdbus: self.value = 0 return self.iface = dbus.Interface( hw.bus.get_object(hw.servername, path), dbus_interface="org.ffado.Control.Element.Discrete") self.value = self.iface.getValue() def getvalue(self): return self.value def setvalue(self, v): if v != self.value: self.iface.setValue(v) self.value = v class Profire2626(Generic_Dice_EAP): def __init__(self, parent=None): Generic_Dice_EAP.__init__(self, parent) def buildMixer(self): #print( self.hw ) #print( self.hw.getText("/Generic/Nickname") ) Generic_Dice_EAP.buildMixer(self) widget = QWidget() uicLoad("ffado/mixer/profire2626_settings.ui", widget) # Add Settings to ffado-mixer panels scrollarea = QScrollArea(self.tabs) scrollarea.setWidgetResizable(False) scrollarea.setWidget(widget) self.tabs.addTab(scrollarea, "Settings") # Master volume knob from collections import namedtuple LineInfo = namedtuple('LineInfo', ['widget','Interface']) # Volume Unactivating self.LineUnActivates = [] p = LineInfo(widget.line1line2, BooleanControl(self.hw, self.hw.basepath+"/EAP/Settings/VolumeKnob/Line1Line2")) self.LineUnActivates.append(p) p = LineInfo(widget.line3line4, BooleanControl(self.hw, self.hw.basepath+"/EAP/Settings/VolumeKnob/Line3Line4")) self.LineUnActivates.append(p) p = LineInfo(widget.line5line6, BooleanControl(self.hw, self.hw.basepath+"/EAP/Settings/VolumeKnob/Line5Line6")) self.LineUnActivates.append(p) p = LineInfo(widget.line7line8, BooleanControl(self.hw, self.hw.basepath+"/EAP/Settings/VolumeKnob/Line7Line8")) self.LineUnActivates.append(p) for l in self.LineUnActivates: l.widget.setChecked(l.Interface.selected()) l.widget.toggled.connect(l.Interface.select) def getDisplayTitle(self): return "M-Audio Profire 2626 and 610 Mixer" # # vim: et ts=4 sw=4 libffado-2.4.5/support/mixer-qt4/ffado/mixer/quatafire.py0000644000175000001440000000777614206145246023011 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QWidget from ffado.import_pyqt import * from ffado.config import * import logging log = logging.getLogger('quatafire') class QuataFire(QWidget): def __init__(self,parent = None): QWidget.__init__(self,parent) uicLoad("ffado/mixer/quatafire", self) self.VolumeControls={ self.sldCh1: ['/Mixer/Feature_Volume_1', 1], self.sldCh2: ['/Mixer/Feature_Volume_1', 2], self.sldCh34: ['/Mixer/Feature_Volume_2', 0], self.sldCh56: ['/Mixer/Feature_Volume_3', 0], self.sldDawAll: ['/Mixer/Feature_Volume_4', 0], self.sldDawCH1: ['/Mixer/Feature_Volume_4', 1], self.sldDawCH2: ['/Mixer/Feature_Volume_4', 2], self.sldDawCH3: ['/Mixer/Feature_Volume_4', 3], self.sldDawCH4: ['/Mixer/Feature_Volume_4', 4], self.sldDawCH5: ['/Mixer/Feature_Volume_4', 5], self.sldDawCH6: ['/Mixer/Feature_Volume_4', 6], self.sldDawCH7: ['/Mixer/Feature_Volume_4', 7], self.sldDawCH8: ['/Mixer/Feature_Volume_4', 8], } self.PanControls={ #self.dialCh1: ['/Mixer/Feature_Volume_1'], #self.dialCh2: ['/Mixer/Feature_Volume_1'], self.dialCh34: ['/Mixer/Feature_Volume_2'], self.dialCh56: ['/Mixer/Feature_Volume_3'], } def updateVolume(self,a0): sender = self.sender() vol = -a0 log.debug("setting %s volume to %d" % (self.VolumeControls[sender][0], vol)) self.hw.setContignuous(self.VolumeControls[sender][0], vol, self.VolumeControls[sender][1]) def updatePan(self,a0): sender = self.sender() pan_left = a0 if pan_left < 0: pan_left = 0 pan_right = -a0 if pan_right < 0: pan_right = 0 log.debug("setting %s pan left to %d" % (self.PanControls[sender][0], -pan_left)) self.hw.setContignuous(self.PanControls[sender][0], -pan_left, 1) log.debug("setting %s pan right to %d" % (self.PanControls[sender][0], -pan_right)) self.hw.setContignuous(self.PanControls[sender][0], -pan_right, 2) def initValues(self): for ctrl, info in self.VolumeControls.items(): vol = self.hw.getContignuous(self.VolumeControls[ctrl][0], self.VolumeControls[ctrl][1]) val = -vol log.debug("%s volume is %d, set to %d" % (ctrl.objectName(), vol, val)) ctrl.setValue(val) # connect the UI element ctrl.valueChanged.connect(self.updateVolume) for ctrl, info in self.PanControls.items(): pan_left = self.hw.getContignuous(self.PanControls[ctrl][0], 1) pan_right = self.hw.getContignuous(self.PanControls[ctrl][0], 2) log.debug("%s pan left is %d" % (ctrl.objectName() , pan_left)) log.debug("%s pan right is %d" % (ctrl.objectName() , pan_right)) if pan_left == 0: val = pan_right else: val = -pan_left ctrl.setValue(val) # connect the UI element ctrl.valueChanged.connect(self.updatePan) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/quatafire.ui0000644000175000001440000005107414206145246022764 0ustar jwoitheusers Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. QuataFireMixerUI 0 0 702 360 0 0 ESI QuataFire610 Mixer Input Mix Qt::Vertical 20 40 0 0 0 32767 10000 1000 Qt::Vertical 10000 MIC1 Qt::AlignCenter false Qt::Vertical 20 40 0 0 0 32767 10000 1000 Qt::Vertical 10000 MIC2 Qt::AlignCenter false -32768 32768 1000 10000 0 0 0 32767 10000 1000 Qt::Vertical 10000 IN3/4 Qt::AlignCenter false -32768 32768 1000 10000 0 0 0 32767 10000 1000 Qt::Vertical 10000 IN5/6 Qt::AlignCenter false Channel Volume Control 0 0 0 32767 10000 1000 Qt::Vertical 10000 0 0 0 32767 10000 1000 Qt::Vertical 10000 0 0 0 32767 10000 1000 Qt::Vertical 10000 0 0 0 32767 10000 1000 Qt::Vertical 10000 0 0 0 32767 10000 1000 Qt::Vertical 10000 0 0 0 32767 10000 1000 Qt::Vertical 10000 0 0 0 32767 10000 1000 Qt::Vertical 10000 0 0 0 32767 10000 1000 Qt::Vertical 10000 0 0 0 32767 10000 1000 Qt::Vertical 10000 ALL Qt::AlignCenter false CH1 Qt::AlignCenter false CH2 Qt::AlignCenter false CH3 Qt::AlignCenter false CH4 Qt::AlignCenter false CH5 Qt::AlignCenter false CH6 Qt::AlignCenter false CH7 Qt::AlignCenter false CH8 Qt::AlignCenter false qPixmapFromMimeSource libffado-2.4.5/support/mixer-qt4/ffado/mixer/rme.py0000644000175000001440000007222414206145246021601 0ustar jwoitheusers# # Copyright (C) 2009, 2011 by Jonathan Woithe # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4 import QtGui # from PyQt4.QtCore import QObject, Qt, QTimer # from PyQt4.QtGui import QWidget, QApplication, QVBoxLayout from ffado.import_pyqt import * from ffado.config import * from ffado.widgets.matrixmixer import MatrixMixer import logging log = logging.getLogger('rme') # Model defines. These must agree with what is used in rme_avdevice.h. RME_MODEL_NONE = 0x0000 RME_MODEL_FF800 = 0x0001 RME_MODEL_FF400 = 0x0002 class Rme(QWidget): def __init__(self,parent = None): QWidget.__init__(self,parent) uicLoad("ffado/mixer/rme", self) self.init() def init(self): self.PhantomSwitches={ self.phantom_0: ['/Control/Phantom', 0], self.phantom_1: ['/Control/Phantom', 1], self.phantom_2: ['/Control/Phantom', 2], self.phantom_3: ['/Control/Phantom', 3], } self.Switches={ self.ff400_chan3_opt_instr: ['/Control/Chan3_opt_instr'], self.ff400_chan3_opt_pad: ['/Control/Chan3_opt_pad'], self.ff400_chan4_opt_instr: ['/Control/Chan4_opt_instr'], self.ff400_chan4_opt_pad: ['/Control/Chan4_opt_pad'], self.spdif_output_optical: ['/Control/SPDIF_output_optical', 0], self.spdif_output_emphasis: ['/Control/SPDIF_output_emphasis', 0], self.spdif_output_pro: ['/Control/SPDIF_output_pro', 0], self.spdif_output_nonaudio: ['/Control/SPDIF_output_nonaudio', 0], } self.Radiobuttons={ self.level_in_lo_gain: ['/Control/Input_level', 0], self.level_in_p4dBu: ['/Control/Input_level', 2], self.level_in_m10dBV: ['/Control/Input_level', 1], self.level_out_hi_gain: ['/Control/Output_level', 2], self.level_out_p4dBu: ['/Control/Output_level', 1], self.level_out_m10dBV: ['/Control/Output_level', 0], self.spdif_input_coax: ['/Control/SPDIF_input_mode', 0], self.spdif_input_optical: ['/Control/SPDIF_input_mode', 1], self.phones_hi_gain: ['/Control/Phones_level', 0], self.phones_p4dBu: ['/Control/Phones_level', 1], self.phones_m10dBV: ['/Control/Phones_level', 2], self.clock_mode_autosync: ['/Control/Clock_mode', 1], self.clock_mode_master: ['/Control/Clock_mode', 0], self.sync_ref_wordclk: ['/Control/Sync_ref', 0], self.sync_ref_adat1: ['/Control/Sync_ref', 1], self.sync_ref_adat2: ['/Control/Sync_ref', 2], self.sync_ref_spdif: ['/Control/Sync_ref', 3], self.sync_ref_tco: ['/Control/Sync_ref', 4], # TCO controls self.sync_source_ltc: ['/Control/Tco_sync_src', 0], self.sync_source_video: ['/Control/Tco_sync_src', 1], self.sync_source_wordclk: ['/Control/Tco_sync_src', 2], self.frame_rate_24fps: ['/Control/Tco_frame_rate', 0], self.frame_rate_25fps: ['/Control/Tco_frame_rate', 1], self.frame_rate_29_97fps: ['/Control/Tco_frame_rate', 2], self.frame_rate_29_97dfps: ['/Control/Tco_frame_rate', 3], self.frame_rate_30fps: ['/Control/Tco_frame_rate', 4], self.frame_rate_30dfps: ['/Control/Tco_frame_rate', 5], self.sample_rate_44k1: ['/Control/Tco_sample_rate', 0], self.sample_rate_48k: ['/Control/Tco_sample_rate', 1], self.sample_rate_0pc: ['/Control/Tco_sample_rate_ofs', 0], self.sample_rate_p01pc: ['/Control/Tco_sample_rate_ofs', 1], self.sample_rate_n01pc: ['/Control/Tco_sample_rate_ofs', 2], self.sample_rate_p40pc: ['/Control/Tco_sample_rate_ofs', 3], self.sample_rate_n40pc: ['/Control/Tco_sample_rate_ofs', 4], self.wordclk_conv_1_1: ['/Control/Tco_word_clk_conv', 0], self.wordclk_conv_44k1_48k: ['/Control/Tco_word_clk_conv', 1], self.wordclk_conv_48k_44k1: ['/Control/Tco_word_clk_conv', 2], } self.Checkboxes={ self.ch1_instr_fuzz: ['/Control/Chan1_instr_opts', 0x04], self.ch1_instr_limiter: ['/Control/Chan1_instr_opts', 0x08], self.ch1_instr_filter: ['/Control/Chan1_instr_opts', 0x02], # TCO controls self.video_input_termination_on: ['/Control/Tco_video_in_term', 0x01], } self.Gains={ self.gain_mic1: ['/Control/Gains', 0], self.gain_mic2: ['/Control/Gains', 1], self.gain_input3: ['/Control/Gains', 2], self.gain_input4: ['/Control/Gains', 3], } self.Combos={ self.ff800_ch1_src: ['/Control/Chan1_source'], self.ff800_ch7_src: ['/Control/Chan7_source'], self.ff800_ch8_src: ['/Control/Chan8_source'], } self.CommandButtons={ self.control_load: ['/Control/Flash_control', 0], self.control_save: ['/Control/Flash_control', 1], self.mixer_load: ['/Control/Flash_control', 2], self.mixer_save: ['/Control/Flash_control', 3], self.mixer_preset_ffado_default: ['/Control/Mixer_preset', 0], } # Other mixer variables self.is_streaming = 0 self.sample_rate = 0 self.model = 0 self.tco_present = 0 # Public slot: update phantom power hardware switches def updatePhantomSwitch(self, a0): sender = self.sender() # Value is the phantom switch value, with a corresponding enable # bit in the high 16 bit word val = (a0 << self.PhantomSwitches[sender][1]) | (0x00010000 << self.PhantomSwitches[sender][1]) log.debug("phantom switch %d set to %d" % (self.PhantomSwitches[sender][1], a0)) self.hw.setDiscrete(self.PhantomSwitches[sender][0], val) # Public slot: update generic switches def updateSwitch(self, a0): sender = self.sender() log.debug("switch %s set to %d" % (self.Switches[sender][0], a0)) self.hw.setDiscrete(self.Switches[sender][0], a0) # Public slot: update generic radiobuttons def updateRadiobutton(self, a0): sender = self.sender() if (a0 != 0): # Only change the control state on a button being "checked" log.debug("radiobutton group %s set to %d" % (self.Radiobuttons[sender][0], self.Radiobuttons[sender][1])) self.hw.setDiscrete(self.Radiobuttons[sender][0], self.Radiobuttons[sender][1]) def updateCheckboxes(self, a0): sender = self.sender() val = self.hw.getDiscrete(self.Checkboxes[sender][0]); if (a0 != 0): val = val | self.Checkboxes[sender][1] else: val = val & ~self.Checkboxes[sender][1] log.debug("checkbox group %s set to %d" % (self.Checkboxes[sender][0], val)); self.hw.setDiscrete(self.Checkboxes[sender][0], val) # Public slot: update gains def updateGain(self, a0): sender = self.sender() log.debug("gain %s[%d] set to %d" % (self.Gains[sender][0], self.Gains[sender][1], a0)) self.hw.setMatrixMixerValue(self.Gains[sender][0], 0, self.Gains[sender][1], a0) def updateBandwidthLimit(self, a0): # Account for the "No ADAT-2" item which will not be present on # a FF400. if (self.model==RME_MODEL_FF400 and a0>0): a0 = a0 + 1 # log.debug("limit update: %d" % (a0)); self.hw.setDiscrete('/Control/Bandwidth_limit', a0); # Public slot: send command def sendCommand(self, a0): sender = self.sender() log.debug("command %d sent to %s" % (self.CommandButtons[sender][1], self.CommandButtons[sender][0])) self.hw.setDiscrete(self.CommandButtons[sender][0], self.CommandButtons[sender][1]) # If mixer values have been reloaded, refresh the mixer GUI. This # will also commit the new values to the hardware via the "changed" # signal handlers of the mixer elements. if (self.CommandButtons[sender][1] == 2): self.inputmatrix.refreshValues() self.outputmatrix.refreshValues() self.playbackmatrix.refreshValues() # If settings have been reloaded from flash, refresh the GUI. The # settings will be made active in the hardware via the "changed" # signal handlers of the respective GUI control widgets. if (self.CommandButtons[sender][1] == 0): self.getValuesFromFF() def updateCombo(self, a0): sender = self.sender() log.debug("combo %s set to %d" % (self.Combos[sender][0], a0)) self.hw.setDiscrete(self.Combos[sender][0], a0) # Enable the limiter control only when the front source is active if (sender == self.ff800_ch1_src): self.ch1_instr_limiter.setEnabled(a0==0) def updateStreamingState(self): ss = self.streamingstatus.selected() ss_txt = self.streamingstatus.getEnumLabel(ss) if ss_txt != 'Idle': self.is_streaming = True else: self.is_streaming = False if (self.last_streaming_state != self.is_streaming): self.bandwidth_limit.setEnabled(not(self.is_streaming)); self.control_load.setEnabled(not(self.is_streaming)); self.last_streaming_state = self.is_streaming def status_update(self): # log.debug("timer event") self.updateStreamingState() clk_mode = ['Master', 'Slave'] src_str = ['None', 'ADAT 1', 'ADAT 2', 'SPDIF', 'Wordclock', 'TCO'] sync_stat = ['No lock', 'Locked', 'Synced'] sysclock_mode = self.hw.getDiscrete('/Control/sysclock_mode') sysclock_freq = self.hw.getDiscrete('/Control/sysclock_freq') autosync_freq = self.hw.getDiscrete('/Control/autosync_freq') autosync_src = self.hw.getDiscrete('/Control/autosync_src') sync_status = self.hw.getDiscrete('/Control/sync_status') spdif_freq = self.hw.getDiscrete('/Control/spdif_freq') self.sysclock_freq.setText("%d Hz" % (sysclock_freq)) self.sysclock_mode.setText(clk_mode[sysclock_mode]) self.autosync_freq.setText("%d Hz" % (autosync_freq)) self.autosync_src.setText(src_str[autosync_src]) self.sync_check_adat1_status.setText(sync_stat[sync_status & 0x03]) self.sync_check_adat2_status.setText(sync_stat[(sync_status >> 2) & 0x03]) self.sync_check_spdif_status.setText(sync_stat[(sync_status >> 4) & 0x03]) self.sync_check_wclk_status.setText(sync_stat[(sync_status >> 6) & 0x03]) self.sync_check_tco_status.setText(sync_stat[(sync_status >> 8) & 0x03]) self.spdif_freq.setText("%d Hz" % (spdif_freq)) if (self.tco_present): ltc_valid_str = ['Not detected', 'Valid'] ltc_framerate_str = ['24 fps', '25 fps', '29.97 fps', '30 fps'] ltc_frametype_str = ['Normal (full frame)', 'Dropframe'] video_type_str = ['No video', 'PAL', 'NTSC'] word_clock_str = ['None', 'Single Speed', 'Double Speed', 'Quad Speed'] status_str = ['Not locked', "Locked"] ltc = self.hw.getDiscrete('/Control/Tco_ltc_in') ltc_valid = self.hw.getDiscrete('/Control/Tco_input_ltc_valid') ltc_fps = self.hw.getDiscrete('/Control/Tco_input_ltc_fps') ltc_dropframe = self.hw.getDiscrete('/Control/Tco_input_ltc_dropframe') videotype = self.hw.getDiscrete('/Control/Tco_input_video_type') wordclk = self.hw.getDiscrete('/Control/Tco_input_word_clk') input_lock = self.hw.getDiscrete('/Control/Tco_input_lock') tco_freq = self.hw.getDiscrete('/Control/Tco_freq') self.state_ltc_valid_label.setText(ltc_valid_str[ltc_valid]) if (ltc_valid): self.ltc_in_hours.setText("%02d" % (ltc >> 24)) self.ltc_in_minutes.setText("%02d" % ((ltc >> 16) & 0xff)) self.ltc_in_seconds.setText("%02d" % ((ltc >> 8) & 0xff)) self.ltc_in_frames.setText("%02d" % (ltc & 0xff)) self.state_ltc_framerate.setText(ltc_framerate_str[ltc_fps]) self.state_ltc_frame_type.setText(ltc_frametype_str[ltc_dropframe]) else: self.ltc_in_hours.setText("--") self.ltc_in_minutes.setText("--") self.ltc_in_seconds.setText("--") self.ltc_in_frames.setText("--") self.state_ltc_framerate.setText("-") self.state_ltc_frame_type.setText("-") self.state_video_type.setText(video_type_str[videotype]) self.state_word_clk.setText(word_clock_str[wordclk]) self.sync_source_status.setText(status_str[input_lock]) self.tco_frequency_label.setText("%d Hz" % (tco_freq)) # Hide and disable a control def disable_hide(self,widget): for w in widget.children(): if isinstance(w, QWidget): w.hide() w.setEnabled(False) widget.hide() widget.setEnabled(False) def setupSignals(self): # Connect signal handlers for all command buttons for ctrl, info in self.CommandButtons.items(): if (not(ctrl.isEnabled())): continue ctrl.clicked.connect(self.sendCommand) for ctrl, info in self.Combos.items(): if (not(ctrl.isEnabled())): continue; ctrl.currentIndexChanged.connect(self.updateCombo) self.bandwidth_limit.currentIndexChanged.connect(self.updateBandwidthLimit) # Get current hardware values and connect GUI element signals to # their respective slots for ctrl, info in self.PhantomSwitches.items(): if (not(ctrl.isEnabled())): continue ctrl.toggled.connect(self.updatePhantomSwitch) for ctrl, info in self.Switches.items(): if (not(ctrl.isEnabled())): continue ctrl.toggled.connect(self.updateSwitch) for ctrl, info in self.Radiobuttons.items(): if (not(ctrl.isEnabled())): continue; ctrl.toggled.connect(self.updateRadiobutton) for ctrl, info in self.Checkboxes.items(): if (not(ctrl.isEnabled())): continue; ctrl.toggled.connect(self.updateCheckboxes) for ctrl, info in self.Gains.items(): if (not(ctrl.isEnabled())): continue ctrl.valueChanged.connect(self.updateGain) # Obtain control values from the Fireface and make the GUI reflect these def getValuesFromFF(self): for ctrl, info in self.Combos.items(): if (not(ctrl.isEnabled())): continue; val = self.hw.getDiscrete(info[0]) log.debug("combo %s is %d" % (info[0], val)); ctrl.setCurrentIndex(val); # Set the bandwidth limit control to reflect the current device # setting, allowing for the additional "No ADAT-2" item which is # present on the FF800. val = self.hw.getDiscrete('/Control/Bandwidth_limit') if (self.model==RME_MODEL_FF400 and val>1): val = val - 1 self.bandwidth_limit.setCurrentIndex(val); # Get current hardware values for ctrl, info in self.PhantomSwitches.items(): if (not(ctrl.isEnabled())): continue val = (self.hw.getDiscrete(info[0]) >> info[1]) & 0x01 log.debug("phantom switch %d is %d" % (info[1], val)) if val: ctrl.setChecked(True) else: ctrl.setChecked(False) for ctrl, info in self.Switches.items(): if (not(ctrl.isEnabled())): continue val = self.hw.getDiscrete(info[0]) log.debug("switch %s is %d" % (info[0], val)) if val: ctrl.setChecked(True) else: ctrl.setChecked(False) for ctrl, info in self.Radiobuttons.items(): if (not(ctrl.isEnabled())): continue; # This is a touch wasteful since it means we retrieve the control # value once per radio button rather than once per radio button # group. In time we might introduce radiobutton groupings in the # self.* datastructures to avoid this, but for the moment this is # easy and it works. val = self.hw.getDiscrete(info[0]) if (val == info[1]): val = 1 else: val = 0 ctrl.setChecked(val) log.debug("Radiobutton %s[%d] is %d" % (info[0], info[1], val)) # Make sure the Limiter control can receive a value if (self.ff800_ch1_src.isEnabled()): self.ch1_instr_limiter.setEnabled(1) for ctrl, info in self.Checkboxes.items(): if (not(ctrl.isEnabled())): continue; # This is a touch wasteful since it means we retrieve the control # value once per checkbox button rather than once per checkbox # group. In time we might introduce checkbox groupings in the # self.* datastructures to avoid this, but for the moment this is # easy and it works. val = self.hw.getDiscrete(info[0]) if (val & info[1]): val = 1 else: val = 0 ctrl.setChecked(val) log.debug("Checkbox %s[%d] is %d" % (info[0], info[1], val)) # The limiter (a checkbox) can only be controlled if the front # source is selected for channel 1. ch1_src = self.ff800_ch1_src.currentIndex() if (self.ff800_ch1_src.isEnabled()): self.ch1_instr_limiter.setEnabled(ch1_src==0) for ctrl, info in self.Gains.items(): if (not(ctrl.isEnabled())): continue val = self.hw.getMatrixMixerValue(info[0], 0, info[1]) log.debug("gain %s[%d] is %d" % (info[0], info[1], val)) ctrl.setValue(val); def initValues(self): # print( self.hw.servername ) # print( self.hw.basepath ) self.inputmatrix = MatrixMixer(self.hw.servername, self.hw.basepath+"/Mixer/InputFaders", self, "Columns_are_inputs", 0x8000, self.hw.basepath+"/Mixer/InputMutes", self.hw.basepath+"/Mixer/InputInverts", True) layout = QVBoxLayout() layout.addWidget(self.inputmatrix) self.mixer.setLayout(layout) self.playbackmatrix = MatrixMixer(self.hw.servername, self.hw.basepath+"/Mixer/PlaybackFaders", self, "Columns_are_inputs", 0x8000, self.hw.basepath+"/Mixer/PlaybackMutes", self.hw.basepath+"/Mixer/PlaybackInverts", True) layout = QVBoxLayout() layout.addWidget(self.playbackmatrix) self.playbackmixer.setLayout(layout) self.outputmatrix = MatrixMixer(self.hw.servername, self.hw.basepath+"/Mixer/OutputFaders", self, "Columns_are_inputs", 0x8000, self.hw.basepath+"/Mixer/OutputMutes", None, True) layout = QVBoxLayout() # This is a bit of a hack, but it works to ensure this single-row # matrix mixer doesn't fill the entire screen but also doesn't end # up with a pointless scrollbar. The matrix mixer's minimum height # is 0 according to minimumHeight(), which is probably the # fundamental issue here; however, I've already wasted too much time # trying to get this to work so if the hack is effective we'll run # with it. self.outputmatrix.setMinimumHeight(150) layout.addWidget(self.outputmatrix, 0, Qt.AlignTop) self.outputmixer.setLayout(layout) self.is_streaming = False self.last_streaming_state = False # Disable the "load settings" button if streaming is active. Its # enable state will be kept up to date by updateStreamingState(). self.control_load.setEnabled(not(self.is_streaming)) # Also disable other controls which are not yet implemented. self.mixer_preset_ffado_default.setEnabled(False) # Retrieve other device settings as needed and customise the UI # based on these options. self.model = self.hw.getDiscrete('/Control/Model') log.debug("device model identifier: %d" % (self.model)) self.tco_present = self.hw.getDiscrete('/Control/TCO_present') log.debug("device has TCO: %d" % (self.tco_present)) #self.sample_rate = self.hw.getDiscrete('/Mixer/Info/SampleRate') #log.debug("device sample rate: %d" % (self.sample_rate)) # Assume the TCO options tab is the second from the left (index 1) if (not(self.tco_present)): self.disable_hide(self.tco_options); self.tabWidget.setTabEnabled(1, False) self.tabWidget.removeTab(1) # The Fireface-400 only has 2 phantom-capable channels if (self.model == RME_MODEL_FF400): self.disable_hide(self.phantom_2) self.disable_hide(self.phantom_3) else: self.phantom_0.setText("Mic 7") self.phantom_1.setText("Mic 8") self.phantom_2.setText("Mic 9") self.phantom_3.setText("Mic 10") # Instrument options, input jack selection controls and an ADAT2 # input are applicable only to the FF800 if (self.model != RME_MODEL_FF800): self.instrument_options_group.setEnabled(False) self.input_plug_select_group.setEnabled(False) self.sync_ref_adat2.setEnabled(False) self.sync_check_adat2_label.setEnabled(False) self.sync_check_adat2_status.setEnabled(False) self.spdif_output_optical.setText("ADAT optical") self.spdif_input_optical.setText("ADAT optical") if (not(self.tco_present)): self.sync_check_tco_label.setEnabled(False) self.sync_check_tco_status.setEnabled(False) self.sync_ref_tco.setEnabled(False) # Only the FF400 has specific channel 3/4 options, input gain # controls and switchable phones level if (self.model != RME_MODEL_FF400): # Hide the upper-level frame (and everything in it) to ensure it # requests no vertical space when its contents aren't needed. self.disable_hide(self.igains_chan34_opts_frame) self.phones_level_group.setEnabled(False) # Add the "No ADAT-2" item to the bandwidth limit control if the # device is not a FF400. if (self.model != RME_MODEL_FF400): self.bandwidth_limit.insertItem(1, "No ADAT-2") self.getValuesFromFF() self.setupSignals() # Ensure the limiter checkbox has a signal handler associated with # it. If getValuesFromFF() disabled it because the front input was # not selected, setupSignals() would not have configured a handler. if (not(self.ch1_instr_limiter.isEnabled())): self.ch1_instr_limiter.toggled.connect(self.updateCheckboxes) self.updateStreamingState() #log.debug("device streaming flag: %d" % (self.is_streaming)) self.update_timer = QTimer(self) self.update_timer.timeout.connect(self.status_update) self.update_timer.start(1000) def saveSettings(self, indent): saveString = [] idt = indent + " " saveString.append('%s\n' % indent) saveString.extend(self.inputmatrix.saveSettings(idt)) # Do not forget to mention the adopted rule for matrix columns mixer # This might be useful for future import function saveString.append("%s \n" % indent) saveString.append("%s Columns_are_inputs\n" % indent) saveString.append("%s \n" % indent) saveString.append('%s\n' % indent) saveString.append('%s\n' % indent) saveString.extend(self.playbackmatrix.saveSettings(idt)) # Do not forget to mention the adopted rule for matrix columns mixer # This might be useful for future import function saveString.append("%s \n" % indent) saveString.append("%s Columns_are_inputs\n" % indent) saveString.append("%s \n" % indent) saveString.append('%s\n' % indent) saveString.append('%s\n' % indent) saveString.extend(self.outputmatrix.saveSettings(idt)) # Do not forget to mention the adopted rule for matrix columns mixer # This might be useful for future import function saveString.append("%s \n" % indent) saveString.append("%s Columns_are_inputs\n" % indent) saveString.append("%s \n" % indent) saveString.append('%s\n' % indent) return saveString def readSettings(self, readString): try: idxb = readString.index('') idxe = readString.index('') except Exception: log.debug("No Input matrix settings found") idxb = -1 idxe = -1 if idxb >= 0: if idxe > idxb + 1: stringMixer = [] for s in readString[idxb+1:idxe]: stringMixer.append(s) # When trying to import from a different device, the rule for column interpretation is # not necessarily the same try: idx = stringMixer.index('') except Exception: log.debug('Do not know how to handle column versus input/output') idx = -1 transpose_coeff = False if idx >=0: if stringMixer[idx+1].find("Columns_are_inputs") == -1: log.debug('Transposing the matrix coefficient; you are importing, are not you ?') transpose_coeff = True if self.inputmatrix.readSettings(stringMixer, transpose_coeff): log.debug("Input matrix settings modified") del stringMixer try: idxb = readString.index('') idxe = readString.index('') except Exception: log.debug("No Plaback matrix settings found") idxb = -1 idxe = -1 if idxb >= 0: if idxe > idxb + 1: stringMixer = [] for s in readString[idxb+1:idxe]: stringMixer.append(s) # When trying to import from a different device, the rule for column interpretation is # not necessarily the same try: idx = stringMixer.index('') except Exception: log.debug('Do not know how to handle column versus input/output') idx = -1 transpose_coeff = False if idx >=0: if stringMixer[idx+1].find("Columns_are_inputs") == -1: log.debug('Transposing the matrix coefficient; you are importing, are not you ?') transpose_coeff = True if self.playbackmatrix.readSettings(stringMixer, transpose_coeff): log.debug("Plaback matrix settings modified") del stringMixer try: idxb = readString.index('') idxe = readString.index('') except Exception: log.debug("No Output matrix settings found") idxb = -1 idxe = -1 if idxb >= 0: if idxe > idxb + 1: stringMixer = [] for s in readString[idxb+1:idxe]: stringMixer.append(s) # When trying to import from a different device, the rule for column interpretation is # not necessarily the same try: idx = stringMixer.index('') except Exception: log.debug('Do not know how to handle column versus input/output') idx = -1 transpose_coeff = False if idx >=0: if stringMixer[idx+1].find("Columns_are_inputs") == -1: log.debug('Transposing the matrix coefficient; you are importing, are not you ?') transpose_coeff = True if self.outputmatrix.readSettings(stringMixer, transpose_coeff): log.debug("Output matrix settings modified") del stringMixer # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/saffire.py0000644000175000001440000006602514206145246022437 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QWidget, QHBoxLayout from ffado.import_pyqt import * from ffado.config import * from ffado.mixer.saffire_base import SaffireMixerBase import logging log = logging.getLogger('saffire') #MIXER LAYOUT: # # |-- Out9/10--| |-- Out1/2 --| |-- Out3/4 --| |-- Out5/6 --| |-- Out7/8 --| #P5 0: 0/ 0 1: 110/ 110 2: 0/ 0 3: 0/ 0 4: 0/ 0 #P1 5: 0/ 0 6:32767/32767 7: 0/ 0 8: 0/ 0 9: 0/ 0 #P2 10: 0/ 0 11: 0/ 0 12:32767/32767 13: 0/ 0 14: 0/ 0 #P3 15: 0/ 0 16: 0/ 0 17: 0/ 0 18:32767/32767 19: 0/ 0 #P4 20: 0/ 0 21: 0/ 0 22: 0/ 0 23: 0/ 0 24:32767/32767 #R1 25: 0/ 0 26: 0/ 0 27: 0/ 0 28: 0/ 0 29: 0/ 0 #R2 30: 0/ 0 31: 0/ 0 32: 0/ 0 33: 0/ 0 34: 0/ 0 #Fx 35: 0/ 0 36: 0/ 0 37: 0/ 0 38: 0/ 0 39: 0/ 0 # #P5: DAW ch 9/10 #P1: DAW ch 1/2 #P2: DAW ch 3/4 #P3: DAW ch 5/6 #P4: DAW ch 7/8 #R1: HW INPUT ch 1/2 #R2: HW INPUT ch 3/4 #Fx: reverb/fx return class Saffire(QWidget): def __init__(self,parent = None): QWidget.__init__(self, parent) self.mono_mode = False self.is_saffire_le = False # make a layout self.layout = QHBoxLayout() self.setLayout(self.layout) def show(self): self.selectCorrectMode() QWidget.show(self) def getMonoMode(self): return self.hw.getDiscrete('/Mixer/MonoMode') def setMonoMode(self, mode): if mode: self.hw.setDiscrete('/Mixer/MonoMode', 1) else: self.hw.setDiscrete('/Mixer/MonoMode', 0) self.mono_mode = self.getMonoMode() def getDisplayTitle(self): if self.is_saffire_le: return "SaffireLE" else: return "Saffire" def selectCorrectMode(self): if self.is_saffire_le: if self.samplerate <= 48000: log.debug("large") self.small.hide() self.large.initValues() self.large.show() else: log.debug("small") self.large.hide() self.small.initValues() self.small.show() else: if self.mono_mode: self.stereo.hide() self.mono.initValues() self.mono.show() else: self.mono.hide() self.stereo.initValues() self.stereo.show() def initValues(self): selected = self.samplerateselect.selected() label = self.samplerateselect.getEnumLabel( selected ) try: self.samplerate = int(label) except: # FIXME: this should be handled properly self.samplerate = 44100 # Saffire: 0x130e010001???? # SaffireLE: 0x130e010004???? if int(self.configrom.getGUID(), 16) >= 0x130e0100040000: self.is_saffire_le = True log.debug("Found SaffireLE GUID") else: self.is_saffire_le = False log.debug("Found Saffire GUID") # init depending on what device we have # and what mode it is in if self.is_saffire_le: # create the child widgets self.small = SaffireLEMixerSmall(self) self.layout.addWidget(self.small) self.large = SaffireLEMixerLarge(self) self.layout.addWidget(self.large) self.small.hw = self.hw self.small.configrom = self.configrom self.large.hw = self.hw self.large.configrom = self.configrom else: # create the child widgets self.mono = SaffireMixerMono(self) self.layout.addWidget(self.mono) self.stereo = SaffireMixerStereo(self) self.layout.addWidget(self.stereo) self.mono_mode = self.getMonoMode() self.mono.hw = self.hw self.mono.configrom = self.configrom self.stereo.hw = self.hw self.stereo.configrom = self.configrom self.selectCorrectMode() def polledUpdate(self): if self.is_saffire_le: if self.samplerate <= 48000: self.large.polledUpdate() else: self.small.polledUpdate() else: if self.mono_mode: self.mono.polledUpdate() else: self.stereo.polledUpdate() class SaffireMixerStereo(QWidget, SaffireMixerBase): def __init__(self,parent = None): self.my_parent = parent QWidget.__init__(self,parent) uicLoad("ffado/mixer/saffire_stereo", self) SaffireMixerBase.__init__(self) self.btnRefresh.clicked.connect(self.updateValues) self.btnSwitchStereoMode.clicked.connect(self.switchStereoMode) self.VolumeControls={ self.sldPC910Out910: ['/Mixer/MatrixMixerStereo', 0, 0], self.sldPC910Out12: ['/Mixer/MatrixMixerStereo', 0, 1], self.sldPC910Out34: ['/Mixer/MatrixMixerStereo', 0, 2], self.sldPC910Out56: ['/Mixer/MatrixMixerStereo', 0, 3], self.sldPC910Out78: ['/Mixer/MatrixMixerStereo', 0, 4], self.sldPC12Out910: ['/Mixer/MatrixMixerStereo', 1, 0], self.sldPC12Out12: ['/Mixer/MatrixMixerStereo', 1, 1], self.sldPC12Out34: ['/Mixer/MatrixMixerStereo', 1, 2], self.sldPC12Out56: ['/Mixer/MatrixMixerStereo', 1, 3], self.sldPC12Out78: ['/Mixer/MatrixMixerStereo', 1, 4], self.sldPC34Out910: ['/Mixer/MatrixMixerStereo', 2, 0], self.sldPC34Out12: ['/Mixer/MatrixMixerStereo', 2, 1], self.sldPC34Out34: ['/Mixer/MatrixMixerStereo', 2, 2], self.sldPC34Out56: ['/Mixer/MatrixMixerStereo', 2, 3], self.sldPC34Out78: ['/Mixer/MatrixMixerStereo', 2, 4], self.sldPC56Out910: ['/Mixer/MatrixMixerStereo', 3, 0], self.sldPC56Out12: ['/Mixer/MatrixMixerStereo', 3, 1], self.sldPC56Out34: ['/Mixer/MatrixMixerStereo', 3, 2], self.sldPC56Out56: ['/Mixer/MatrixMixerStereo', 3, 3], self.sldPC56Out78: ['/Mixer/MatrixMixerStereo', 3, 4], self.sldPC78Out910: ['/Mixer/MatrixMixerStereo', 4, 0], self.sldPC78Out12: ['/Mixer/MatrixMixerStereo', 4, 1], self.sldPC78Out34: ['/Mixer/MatrixMixerStereo', 4, 2], self.sldPC78Out56: ['/Mixer/MatrixMixerStereo', 4, 3], self.sldPC78Out78: ['/Mixer/MatrixMixerStereo', 4, 4], self.sldIN12Out910: ['/Mixer/MatrixMixerStereo', 5, 0], self.sldIN12Out12: ['/Mixer/MatrixMixerStereo', 5, 1], self.sldIN12Out34: ['/Mixer/MatrixMixerStereo', 5, 2], self.sldIN12Out56: ['/Mixer/MatrixMixerStereo', 5, 3], self.sldIN12Out78: ['/Mixer/MatrixMixerStereo', 5, 4], self.sldIN34Out910: ['/Mixer/MatrixMixerStereo', 6, 0], self.sldIN34Out12: ['/Mixer/MatrixMixerStereo', 6, 1], self.sldIN34Out34: ['/Mixer/MatrixMixerStereo', 6, 2], self.sldIN34Out56: ['/Mixer/MatrixMixerStereo', 6, 3], self.sldIN34Out78: ['/Mixer/MatrixMixerStereo', 6, 4], self.sldFXOut910: ['/Mixer/MatrixMixerStereo', 7, 0], self.sldFXOut12: ['/Mixer/MatrixMixerStereo', 7, 1], self.sldFXOut34: ['/Mixer/MatrixMixerStereo', 7, 2], self.sldFXOut56: ['/Mixer/MatrixMixerStereo', 7, 3], self.sldFXOut78: ['/Mixer/MatrixMixerStereo', 7, 4], } # First column is the DBUS subpath of the control. # Second column is a list of linked controls that should # be rewritten whenever this control is updated self.SelectorControls={ self.chkSpdifSwitch: ['/Mixer/SpdifSwitch'], self.chkOut12Mute: ['/Mixer/Out12Mute', [self.chkOut12HwCtrl]], self.chkOut12HwCtrl: ['/Mixer/Out12HwCtrl'], self.chkOut12Dim: ['/Mixer/Out12Dim'], self.chkOut34Mute: ['/Mixer/Out34Mute', [self.chkOut34HwCtrl]], self.chkOut34HwCtrl: ['/Mixer/Out34HwCtrl'], self.chkOut56Mute: ['/Mixer/Out56Mute', [self.chkOut56HwCtrl]], self.chkOut56HwCtrl: ['/Mixer/Out56HwCtrl'], self.chkOut78Mute: ['/Mixer/Out78Mute', [self.chkOut78HwCtrl]], self.chkOut78HwCtrl: ['/Mixer/Out78HwCtrl'], self.chkOut910Mute: ['/Mixer/Out910Mute'], } self.VolumeControlsLowRes={ self.sldOut12Level: ['/Mixer/Out12Level'], self.sldOut34Level: ['/Mixer/Out34Level'], self.sldOut56Level: ['/Mixer/Out56Level'], self.sldOut78Level: ['/Mixer/Out78Level'], } self.TriggerButtonControls={ self.btnSaveSettings: ['/Mixer/SaveSettings'], } self.TextControls={ } self.saveTextControls={ } self.ComboControls={ } def polledUpdate(self): self.polledUpdateHwCtrl(self.chkOut12HwCtrl, self.sldOut12Level) self.polledUpdateHwCtrl(self.chkOut34HwCtrl, self.sldOut34Level) self.polledUpdateHwCtrl(self.chkOut56HwCtrl, self.sldOut56Level) self.polledUpdateHwCtrl(self.chkOut78HwCtrl, self.sldOut78Level) def polledUpdateHwCtrl(self, selector, volctrl): state = selector.isChecked() if state: self.polledUpdateVolumeLowRes('/Mixer/MonitorDial', volctrl, 64) volctrl.setEnabled(False) else: volctrl.setEnabled(True) def updateMatrixVolume(self,a0): SaffireMixerBase.updateMatrixVolume(self,a0) def updateLowResVolume(self,a0): SaffireMixerBase.updateLowResVolume(self,a0) def updateSelector(self,a0): SaffireMixerBase.updateSelector(self,a0) def triggerButton(self): SaffireMixerBase.triggerButton(self) def saveText(self): SaffireMixerBase.saveText(self) def initCombo(self, combo): SaffireMixerBase.initCombo(self,combo) def selectCombo(self, mode): SaffireMixerBase.selectCombo(self,mode) def updateValues(self): SaffireMixerBase.updateValues(self) def switchStereoMode(self): log.debug("should switch to mono mode") self.my_parent.setMonoMode(1) self.my_parent.selectCorrectMode() class SaffireMixerMono(QWidget, SaffireMixerBase): def __init__(self,parent = None): self.my_parent = parent QWidget.__init__(self,parent) uicLoad("ffado/mixer/saffire_mono", self) SaffireMixerBase.__init__(self) self.btnRefresh.clicked.connect(self.updateValues) self.btnSwitchStereoMode.clicked.connect(self.switchStereoMode) self.VolumeControls={ self.sldIN1Out910: ['/Mixer/MatrixMixerMono', 0, 0], self.sldIN1Out12: ['/Mixer/MatrixMixerMono', 0, 1], self.sldIN1Out34: ['/Mixer/MatrixMixerMono', 0, 2], self.sldIN1Out56: ['/Mixer/MatrixMixerMono', 0, 3], self.sldIN1Out78: ['/Mixer/MatrixMixerMono', 0, 4], self.sldIN3Out910: ['/Mixer/MatrixMixerMono', 1, 0], self.sldIN3Out12: ['/Mixer/MatrixMixerMono', 1, 1], self.sldIN3Out34: ['/Mixer/MatrixMixerMono', 1, 2], self.sldIN3Out56: ['/Mixer/MatrixMixerMono', 1, 3], self.sldIN3Out78: ['/Mixer/MatrixMixerMono', 1, 4], self.sldFX1Out910: ['/Mixer/MatrixMixerMono', 2, 0], self.sldFX1Out12: ['/Mixer/MatrixMixerMono', 2, 1], self.sldFX1Out34: ['/Mixer/MatrixMixerMono', 2, 2], self.sldFX1Out56: ['/Mixer/MatrixMixerMono', 2, 3], self.sldFX1Out78: ['/Mixer/MatrixMixerMono', 2, 4], self.sldIN2Out910: ['/Mixer/MatrixMixerMono', 3, 0], self.sldIN2Out12: ['/Mixer/MatrixMixerMono', 3, 1], self.sldIN2Out34: ['/Mixer/MatrixMixerMono', 3, 2], self.sldIN2Out56: ['/Mixer/MatrixMixerMono', 3, 3], self.sldIN2Out78: ['/Mixer/MatrixMixerMono', 3, 4], self.sldIN4Out910: ['/Mixer/MatrixMixerMono', 4, 0], self.sldIN4Out12: ['/Mixer/MatrixMixerMono', 4, 1], self.sldIN4Out34: ['/Mixer/MatrixMixerMono', 4, 2], self.sldIN4Out56: ['/Mixer/MatrixMixerMono', 4, 3], self.sldIN4Out78: ['/Mixer/MatrixMixerMono', 4, 4], self.sldFX2Out910: ['/Mixer/MatrixMixerMono', 5, 0], self.sldFX2Out12: ['/Mixer/MatrixMixerMono', 5, 1], self.sldFX2Out34: ['/Mixer/MatrixMixerMono', 5, 2], self.sldFX2Out56: ['/Mixer/MatrixMixerMono', 5, 3], self.sldFX2Out78: ['/Mixer/MatrixMixerMono', 5, 4], self.sldPC910Out910: ['/Mixer/MatrixMixerMono', 6, 0], self.sldPC910Out12: ['/Mixer/MatrixMixerMono', 6, 1], self.sldPC910Out34: ['/Mixer/MatrixMixerMono', 6, 2], self.sldPC910Out56: ['/Mixer/MatrixMixerMono', 6, 3], self.sldPC910Out78: ['/Mixer/MatrixMixerMono', 6, 4], self.sldPC12Out910: ['/Mixer/MatrixMixerMono', 7, 0], self.sldPC12Out12: ['/Mixer/MatrixMixerMono', 7, 1], self.sldPC12Out34: ['/Mixer/MatrixMixerMono', 7, 2], self.sldPC12Out56: ['/Mixer/MatrixMixerMono', 7, 3], self.sldPC12Out78: ['/Mixer/MatrixMixerMono', 7, 4], self.sldPC34Out910: ['/Mixer/MatrixMixerMono', 8, 0], self.sldPC34Out12: ['/Mixer/MatrixMixerMono', 8, 1], self.sldPC34Out34: ['/Mixer/MatrixMixerMono', 8, 2], self.sldPC34Out56: ['/Mixer/MatrixMixerMono', 8, 3], self.sldPC34Out78: ['/Mixer/MatrixMixerMono', 8, 4], self.sldPC56Out910: ['/Mixer/MatrixMixerMono', 9, 0], self.sldPC56Out12: ['/Mixer/MatrixMixerMono', 9, 1], self.sldPC56Out34: ['/Mixer/MatrixMixerMono', 9, 2], self.sldPC56Out56: ['/Mixer/MatrixMixerMono', 9, 3], self.sldPC56Out78: ['/Mixer/MatrixMixerMono', 9, 4], self.sldPC78Out910: ['/Mixer/MatrixMixerMono', 10, 0], self.sldPC78Out12: ['/Mixer/MatrixMixerMono', 10, 1], self.sldPC78Out34: ['/Mixer/MatrixMixerMono', 10, 2], self.sldPC78Out56: ['/Mixer/MatrixMixerMono', 10, 3], self.sldPC78Out78: ['/Mixer/MatrixMixerMono', 10, 4], } # First column is the DBUS subpath of the control. # Second column is a list of linked controls that should # be rewritten whenever this control is updated self.SelectorControls={ self.chkSpdifSwitch: ['/Mixer/SpdifSwitch'], self.chkOut12Mute: ['/Mixer/Out12Mute', [self.chkOut12HwCtrl]], self.chkOut12HwCtrl: ['/Mixer/Out12HwCtrl'], self.chkOut12Dim: ['/Mixer/Out12Dim'], self.chkOut34Mute: ['/Mixer/Out34Mute', [self.chkOut34HwCtrl]], self.chkOut34HwCtrl: ['/Mixer/Out34HwCtrl'], self.chkOut56Mute: ['/Mixer/Out56Mute', [self.chkOut56HwCtrl]], self.chkOut56HwCtrl: ['/Mixer/Out56HwCtrl'], self.chkOut78Mute: ['/Mixer/Out78Mute', [self.chkOut78HwCtrl]], self.chkOut78HwCtrl: ['/Mixer/Out78HwCtrl'], self.chkOut910Mute: ['/Mixer/Out910Mute'], } self.VolumeControlsLowRes={ self.sldOut12Level: ['/Mixer/Out12Level'], self.sldOut34Level: ['/Mixer/Out34Level'], self.sldOut56Level: ['/Mixer/Out56Level'], self.sldOut78Level: ['/Mixer/Out78Level'], } self.TriggerButtonControls={ self.btnSaveSettings: ['/Mixer/SaveSettings'], } self.TextControls={ } self.saveTextControls={ } self.ComboControls={ } def polledUpdate(self): self.polledUpdateHwCtrl(self.chkOut12HwCtrl, self.sldOut12Level) self.polledUpdateHwCtrl(self.chkOut34HwCtrl, self.sldOut34Level) self.polledUpdateHwCtrl(self.chkOut56HwCtrl, self.sldOut56Level) self.polledUpdateHwCtrl(self.chkOut78HwCtrl, self.sldOut78Level) def polledUpdateHwCtrl(self, selector, volctrl): state = selector.isChecked() if state: self.polledUpdateVolumeLowRes('/Mixer/MonitorDial', volctrl, 4) volctrl.setEnabled(False) else: volctrl.setEnabled(True) def updateMatrixVolume(self,a0): SaffireMixerBase.updateMatrixVolume(self,a0) def updateLowResVolume(self,a0): SaffireMixerBase.updateLowResVolume(self,a0) def updateSelector(self,a0): SaffireMixerBase.updateSelector(self,a0) def triggerButton(self): SaffireMixerBase.triggerButton(self) def saveText(self): SaffireMixerBase.saveText(self) def initCombo(self, combo): SaffireMixerBase.initCombo(self,combo) def selectCombo(self, mode): SaffireMixerBase.selectCombo(self,mode) def updateValues(self): SaffireMixerBase.updateValues(self) def switchStereoMode(self): log.debug("should switch to stereo mode") self.my_parent.setMonoMode(0) self.my_parent.selectCorrectMode() class SaffireLEMixerLarge(QWidget, SaffireMixerBase): def __init__(self,parent = None): self.my_parent = parent QWidget.__init__(self,parent) uicLoad("ffado/mixer/saffirele_large", self) SaffireMixerBase.__init__(self) log.debug("Init large Saffire LE mixer window") self.VolumeControls={ self.sldIN1Out1: ['/Mixer/LEMix48', 0, 0], self.sldIN1Out2: ['/Mixer/LEMix48', 0, 1], self.sldIN1Out3: ['/Mixer/LEMix48', 0, 2], self.sldIN1Out4: ['/Mixer/LEMix48', 0, 3], self.sldIN2Out1: ['/Mixer/LEMix48', 1, 0], self.sldIN2Out2: ['/Mixer/LEMix48', 1, 1], self.sldIN2Out3: ['/Mixer/LEMix48', 1, 2], self.sldIN2Out4: ['/Mixer/LEMix48', 1, 3], self.sldIN3Out1: ['/Mixer/LEMix48', 2, 0], self.sldIN3Out2: ['/Mixer/LEMix48', 2, 1], self.sldIN3Out3: ['/Mixer/LEMix48', 2, 2], self.sldIN3Out4: ['/Mixer/LEMix48', 2, 3], self.sldIN4Out1: ['/Mixer/LEMix48', 3, 0], self.sldIN4Out2: ['/Mixer/LEMix48', 3, 1], self.sldIN4Out3: ['/Mixer/LEMix48', 3, 2], self.sldIN4Out4: ['/Mixer/LEMix48', 3, 3], self.sldSPDIF1Out1: ['/Mixer/LEMix48', 4, 0], self.sldSPDIF1Out2: ['/Mixer/LEMix48', 4, 1], self.sldSPDIF1Out3: ['/Mixer/LEMix48', 4, 2], self.sldSPDIF1Out4: ['/Mixer/LEMix48', 4, 3], self.sldSPDIF2Out1: ['/Mixer/LEMix48', 5, 0], self.sldSPDIF2Out2: ['/Mixer/LEMix48', 5, 1], self.sldSPDIF2Out3: ['/Mixer/LEMix48', 5, 2], self.sldSPDIF2Out4: ['/Mixer/LEMix48', 5, 3], self.sldPC1Out1: ['/Mixer/LEMix48', 6, 0], self.sldPC1Out2: ['/Mixer/LEMix48', 6, 1], self.sldPC1Out3: ['/Mixer/LEMix48', 6, 2], self.sldPC1Out4: ['/Mixer/LEMix48', 6, 3], self.sldPC2Out1: ['/Mixer/LEMix48', 7, 0], self.sldPC2Out2: ['/Mixer/LEMix48', 7, 1], self.sldPC2Out3: ['/Mixer/LEMix48', 7, 2], self.sldPC2Out4: ['/Mixer/LEMix48', 7, 3], self.sldPC3Out1: ['/Mixer/LEMix48', 8, 0], self.sldPC3Out2: ['/Mixer/LEMix48', 8, 1], self.sldPC3Out3: ['/Mixer/LEMix48', 8, 2], self.sldPC3Out4: ['/Mixer/LEMix48', 8, 3], self.sldPC4Out1: ['/Mixer/LEMix48', 9, 0], self.sldPC4Out2: ['/Mixer/LEMix48', 9, 1], self.sldPC4Out3: ['/Mixer/LEMix48', 9, 2], self.sldPC4Out4: ['/Mixer/LEMix48', 9, 3], self.sldPC5Out1: ['/Mixer/LEMix48', 10, 0], self.sldPC5Out2: ['/Mixer/LEMix48', 10, 1], self.sldPC5Out3: ['/Mixer/LEMix48', 10, 2], self.sldPC5Out4: ['/Mixer/LEMix48', 10, 3], self.sldPC6Out1: ['/Mixer/LEMix48', 11, 0], self.sldPC6Out2: ['/Mixer/LEMix48', 11, 1], self.sldPC6Out3: ['/Mixer/LEMix48', 11, 2], self.sldPC6Out4: ['/Mixer/LEMix48', 11, 3], self.sldPC7Out1: ['/Mixer/LEMix48', 12, 0], self.sldPC7Out2: ['/Mixer/LEMix48', 12, 1], self.sldPC7Out3: ['/Mixer/LEMix48', 12, 2], self.sldPC7Out4: ['/Mixer/LEMix48', 12, 3], self.sldPC8Out1: ['/Mixer/LEMix48', 13, 0], self.sldPC8Out2: ['/Mixer/LEMix48', 13, 1], self.sldPC8Out3: ['/Mixer/LEMix48', 13, 2], self.sldPC8Out4: ['/Mixer/LEMix48', 13, 3], } self.SelectorControls={ self.chkOut12Mute: ['/Mixer/Out12Mute'], self.chkOut12HwCtrl: ['/Mixer/Out12HwCtrl'], self.chkOut34Mute: ['/Mixer/Out34Mute'], self.chkOut34HwCtrl: ['/Mixer/Out34HwCtrl'], self.chkOut56Mute: ['/Mixer/Out56Mute'], self.chkOut56HwCtrl: ['/Mixer/Out56HwCtrl'], self.chkSPDIFTransparent: ['/Mixer/SpdifTransparent'], self.chkMIDITru: ['/Mixer/MidiThru'], self.chkHighGain3: ['/Mixer/HighGainLine3'], self.chkHighGain4: ['/Mixer/HighGainLine4'], } self.VolumeControlsLowRes={ self.sldOut12Level: ['/Mixer/Out12Level'], self.sldOut34Level: ['/Mixer/Out34Level'], self.sldOut56Level: ['/Mixer/Out56Level'], } self.TriggerButtonControls={ self.btnSaveSettings: ['/Mixer/SaveSettings'], } self.TextControls={ } self.saveTextControls={ } self.ComboControls={ } def polledUpdate(self): #fixme do what it takes to make the gui follow the front panel dial pass def updateMatrixVolume(self,a0): SaffireMixerBase.updateMatrixVolume(self,a0) def updateLowResVolume(self,a0): SaffireMixerBase.updateLowResVolume(self,a0) def updateSelector(self,a0): SaffireMixerBase.updateSelector(self,a0) def triggerButton(self): SaffireMixerBase.triggerButton(self) def saveText(self): SaffireMixerBase.saveText(self) def initCombo(self, combo): SaffireMixerBase.initCombo(self,combo) def selectCombo(self, mode): SaffireMixerBase.selectCombo(self,mode) def updateValues(self): SaffireMixerBase.updateValues(self) class SaffireLEMixerSmall(QWidget, SaffireMixerBase): def __init__(self,parent = None): self.my_parent = parent QWidget.__init__(self,parent) uicLoad("ffado/mixer/saffirele_small", self) SaffireMixerBase.__init__(self) log.debug("Init small Saffire LE mixer window") self.VolumeControls={ self.sldIN1RecMix: ['/Mixer/LEMix96', 0, 4], self.sldIN2RecMix: ['/Mixer/LEMix96', 1, 4], self.sldIN3RecMix: ['/Mixer/LEMix96', 2, 4], self.sldIN4RecMix: ['/Mixer/LEMix96', 3, 4], self.sldSPDIF1RecMix: ['/Mixer/LEMix96', 4, 4], self.sldSPDIF2RecMix: ['/Mixer/LEMix96', 5, 4], self.sldPC1Out1: ['/Mixer/LEMix96', 6, 0], self.sldPC1Out2: ['/Mixer/LEMix96', 6, 1], self.sldPC1Out3: ['/Mixer/LEMix96', 6, 2], self.sldPC1Out4: ['/Mixer/LEMix96', 6, 3], self.sldPC2Out1: ['/Mixer/LEMix96', 7, 0], self.sldPC2Out2: ['/Mixer/LEMix96', 7, 1], self.sldPC2Out3: ['/Mixer/LEMix96', 7, 2], self.sldPC2Out4: ['/Mixer/LEMix96', 7, 3], } self.SelectorControls={ self.chkOut12Mute: ['/Mixer/Out12Mute'], self.chkOut12HwCtrl: ['/Mixer/Out12HwCtrl'], self.chkOut34Mute: ['/Mixer/Out34Mute'], self.chkOut34HwCtrl: ['/Mixer/Out34HwCtrl'], self.chkOut56Mute: ['/Mixer/Out56Mute'], self.chkOut56HwCtrl: ['/Mixer/Out56HwCtrl'], self.chkSPDIFTransparent: ['/Mixer/SpdifTransparent'], self.chkMIDITru: ['/Mixer/MidiThru'], self.chkHighGain3: ['/Mixer/HighGainLine3'], self.chkHighGain4: ['/Mixer/HighGainLine4'], } self.VolumeControlsLowRes={ self.sldOut12Level: ['/Mixer/Out12Level'], self.sldOut34Level: ['/Mixer/Out34Level'], self.sldOut56Level: ['/Mixer/Out56Level'], } self.TriggerButtonControls={ self.btnSaveSettings: ['/Mixer/SaveSettings'], } self.TextControls={ } self.saveTextControls={ } self.ComboControls={ } def polledUpdate(self): #fixme do what it takes to make the gui follow the front panel dial pass def updateMatrixVolume(self,a0): SaffireMixerBase.updateMatrixVolume(self,a0) def updateLowResVolume(self,a0): SaffireMixerBase.updateLowResVolume(self,a0) def updateSelector(self,a0): SaffireMixerBase.updateSelector(self,a0) def triggerButton(self): SaffireMixerBase.triggerButton(self) def saveText(self): SaffireMixerBase.saveText(self) def initCombo(self, combo): SaffireMixerBase.initCombo(self,combo) def selectCombo(self, mode): SaffireMixerBase.selectCombo(self,mode) def updateValues(self): SaffireMixerBase.updateValues(self) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/saffire_base.py0000644000175000001440000001335014206145246023422 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import logging log = logging.getLogger('saffirebase') # the class that holds all actual control code class SaffireMixerBase: def __init__(self): pass def updateMatrixVolume(self,a0): sender = self.sender() vol = a0 log.debug("set %s %d %d to %d" % ( self.VolumeControls[sender][0], self.VolumeControls[sender][1], self.VolumeControls[sender][2], vol)) self.hw.setMatrixMixerValue(self.VolumeControls[sender][0], self.VolumeControls[sender][1], self.VolumeControls[sender][2], vol) def updateLowResVolume(self,a0): sender = self.sender() vol = 127 - a0 log.debug("set %s to %d" % ( self.VolumeControlsLowRes[sender][0], vol)) self.hw.setDiscrete(self.VolumeControlsLowRes[sender][0], vol) def updateSelector(self,a0): sender = self.sender() if a0: state = 1 else: state = 0 log.debug("set %s to %d" % ( self.SelectorControls[sender][0], state)) self.hw.setDiscrete(self.SelectorControls[sender][0], state) # if there are linked selector controls, update them if len(self.SelectorControls[sender]) >= 2: linked = self.SelectorControls[sender][1] for ctl in linked: if ctl.isChecked(): state = 1 else: state = 0 self.hw.setDiscrete(self.SelectorControls[ctl][0], state) def triggerButton(self): sender = self.sender() log.debug("trigger %s" % ( self.TriggerButtonControls[sender][0])) self.hw.setDiscrete(self.TriggerButtonControls[sender][0], 1) def saveText(self): sender = self.sender() textbox = self.saveTextControls[sender][0] log.debug("save %s" % ( textbox.text().ascii())) self.hw.setText(self.TextControls[textbox][0], textbox.text().ascii()) def initCombo(self, combo): path = self.ComboControls[combo][0] combo.clear() nb_items = self.hw.enumCount(path) for i in range( nb_items ): combo.insertItem( nb_items, self.hw.enumGetLabel(path, i) ) combo.setCurrentIndex( self.hw.enumSelected(path) ) def selectCombo(self, mode): sender = self.sender() path = self.ComboControls[sender][0] self.hw.enumSelect(path, mode) sender.setCurrentIndex( self.hw.enumSelected(path) ) def updateValues(self): for ctrl, info in self.VolumeControls.items(): vol = self.hw.getMatrixMixerValue(self.VolumeControls[ctrl][0], self.VolumeControls[ctrl][1], self.VolumeControls[ctrl][2]) log.debug("%s volume is %d" % (ctrl.objectName() , vol)) ctrl.setValue(vol) for ctrl, info in self.VolumeControlsLowRes.items(): vol = self.hw.getDiscrete(self.VolumeControlsLowRes[ctrl][0]) log.debug("%s volume is %d" % (ctrl.objectName() , 127-vol)) ctrl.setValue(127 - vol) for ctrl, info in self.SelectorControls.items(): state = self.hw.getDiscrete(self.SelectorControls[ctrl][0]) log.debug("%s state is %d" % (ctrl.objectName() , state)) if state: ctrl.setChecked(True) else: ctrl.setChecked(False) for ctrl, info in self.TriggerButtonControls.items(): pass for ctrl, info in self.TextControls.items(): text = self.hw.getText(self.TextControls[ctrl][0]) log.debug("%s text is %s" % (ctrl.objectName() , text)) ctrl.setText(text) for ctrl, info in self.ComboControls.items(): self.initCombo(ctrl) def polledUpdateVolumeLowRes(self, srcpath, ctrl, divider=1): vol = self.hw.getDiscrete(srcpath) / divider #log.debug("polledUpdateVolumeLowRes: vol = %s" % vol) ctrl.setValue(vol) def initValues(self): self.updateValues() for ctrl, info in self.VolumeControls.items(): ctrl.valueChanged.connect(self.updateMatrixVolume) for ctrl, info in self.VolumeControlsLowRes.items(): ctrl.valueChanged.connect(self.updateLowResVolume) for ctrl, info in self.SelectorControls.items(): ctrl.stateChanged.connect(self.updateSelector) for ctrl, info in self.TriggerButtonControls.items(): ctrl.clicked.connect(self.triggerButton) for ctrl, info in self.saveTextControls.items(): ctrl.clicked.connect(self.saveText) for ctrl, info in self.ComboControls.items(): ctrl.activated.connect(self.selectCombo) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/saffire_dice.py0000644000175000001440000004635214206145246023424 0ustar jwoitheusers# # Copyright (C) 2009-2010 by Arnold Krille # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4 import QtGui, QtCore, Qt # from PyQt4.QtGui import QWidget, QScrollArea from ffado.import_pyqt import * import dbus from ffado.widgets.matrixmixer import MatrixMixer from ffado.widgets.crossbarrouter import * from ffado.mixer.generic_dice_eap import * from ffado.config import * class BooleanControl: def __init__(self, hw, path): if bypassdbus: self.value = False return self.iface = dbus.Interface( hw.bus.get_object(hw.servername, path), dbus_interface="org.ffado.Control.Element.Boolean") self.value = self.iface.selected() def selected(self): return self.value def select(self, n): if bypassdbus: return False if self.iface.select(n): self.value = n return True return False class DiscreteControl: def __init__(self, hw, path): if bypassdbus: self.value = 0 return self.iface = dbus.Interface( hw.bus.get_object(hw.servername, path), dbus_interface="org.ffado.Control.Element.Discrete") self.value = self.iface.getValue() def getvalue(self): return self.value def setvalue(self, v): if v != self.value: self.iface.setValue(v) self.value = v class Saffire_Dice(Generic_Dice_EAP): def __init__(self, parent=None): Generic_Dice_EAP.__init__(self, parent) def buildMixer(self): #print( self.hw ) #print( self.hw.getText("/Generic/Nickname") ) Generic_Dice_EAP.buildMixer(self) widget = QWidget() ModelName = self.configrom.getModelName().rstrip() if ffado.config.bypassdbus: # A hack for the bypassdbus case, where ModelName has a slightly # different format. Rather than using the name as returned by # the device (eg: "SAFFIRE_PRO_40"), the name is taken from the # configuration file desciption (eg: "Saffire PRO 40"). if ModelName[-2:] == "-1": ModelName = ModelName[:-2] if ModelName[-4:] == " DSP": ModelName = ModelName[:-4] ModelNum = ModelName[-2:] ModelName = "SAFFIRE_PRO" if ModelNum != "26": ModelName += "_" ModelName += ModelNum print( ModelName ) if ModelName == "SAFFIRE_PRO_14": uicLoad("ffado/mixer/Saffire_Pro14_monitoring.ui", widget) elif ModelName == "SAFFIRE_PRO_24" or self.configrom.getModelName() == "SAFFIRE_PRO_24DSP": uicLoad("ffado/mixer/Saffire_Pro24_monitoring.ui", widget) elif ModelName == "SAFFIRE_PRO26": uicLoad("ffado/mixer/Saffire_Pro26_monitoring.ui", widget) elif ModelName == "SAFFIRE_PRO_40": uicLoad("ffado/mixer/Saffire_Pro40_monitoring.ui", widget) # Add Monitoring to ffado-mixer panels scrollarea = QScrollArea(self.tabs) scrollarea.setWidgetResizable(False) scrollarea.setWidget(widget) self.tabs.addTab(scrollarea, "Monitoring") # Global settings self.muteInterface = BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/GlobalMute/State") widget.GlobalMute.setChecked(self.muteInterface.selected()) widget.GlobalMute.toggled.connect(self.muteToggle) self.dimInterface = BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/GlobalDim/State") widget.GlobalDim.setChecked(self.dimInterface.selected()) widget.GlobalDim.toggled.connect(self.dimToggle) self.dimLevelInterface = DiscreteControl(self.hw, self.hw.basepath+"/EAP/Monitoring/GlobalDim/Level") widget.DimLevel.setValue(self.dimLevelInterface.getvalue()) widget.DimLevel.valueChanged.connect(self.dimLevelChanged) self.DimLevel = widget.DimLevel widget.DimLevel.setEnabled(self.dimInterface.selected()) # Per line monitoring from collections import namedtuple LineInfo = namedtuple('LineInfo', ['widget','Interface']) self.nbLines = 4 self.LineMonos = [] if ModelName != "SAFFIRE_PRO26": # TODO: is this redundant on any other interface? # Mono/Stereo Switch p = LineInfo(widget.Mono_12, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/Mono/Line1Line2")) self.LineMonos.append(p) p = LineInfo(widget.Mono_34, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/Mono/Line3Line4")) self.LineMonos.append(p) # Volume Unactivating self.LineUnActivates = [] p = LineInfo(widget.LineUnActivate_1, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/UnActivate1")) self.LineUnActivates.append(p) p = LineInfo(widget.LineUnActivate_2, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/UnActivate2")) self.LineUnActivates.append(p) p = LineInfo(widget.LineUnActivate_3, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/UnActivate3")) self.LineUnActivates.append(p) p = LineInfo(widget.LineUnActivate_4, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/UnActivate4")) self.LineUnActivates.append(p) # Mute self.LineMutes = [] p = LineInfo(widget.LineMute_1, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Mute1")) self.LineMutes.append(p) p = LineInfo(widget.LineMute_2, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Mute2")) self.LineMutes.append(p) p = LineInfo(widget.LineMute_3, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Mute3")) self.LineMutes.append(p) p = LineInfo(widget.LineMute_4, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Mute4")) self.LineMutes.append(p) # per line Global Mute switch self.LineGMutes = [] p = LineInfo(widget.LineGMute_1, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GMute1")) self.LineGMutes.append(p) p = LineInfo(widget.LineGMute_2, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GMute2")) self.LineGMutes.append(p) p = LineInfo(widget.LineGMute_3, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GMute3")) self.LineGMutes.append(p) p = LineInfo(widget.LineGMute_4, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GMute4")) self.LineGMutes.append(p) # per line Global Dim switch self.LineGDims = [] p = LineInfo(widget.LineGDim_1, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GDim1")) self.LineGDims.append(p) p = LineInfo(widget.LineGDim_2, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GDim2")) self.LineGDims.append(p) p = LineInfo(widget.LineGDim_3, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GDim3")) self.LineGDims.append(p) p = LineInfo(widget.LineGDim_4, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GDim4")) self.LineGDims.append(p) # Volume self.LineVolumes = [] p = LineInfo(widget.LineVolume_1, DiscreteControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Volume1")) self.LineVolumes.append(p) p = LineInfo(widget.LineVolume_2, DiscreteControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Volume2")) self.LineVolumes.append(p) p = LineInfo(widget.LineVolume_3, DiscreteControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Volume3")) self.LineVolumes.append(p) p = LineInfo(widget.LineVolume_4, DiscreteControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Volume4")) self.LineVolumes.append(p) if ModelName != "SAFFIRE_PRO_14": self.nbLines = 6 if ModelName != "SAFFIRE_PRO26": # Mono/Stereo Switch p = LineInfo(widget.Mono_56, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/Mono/Line5Line6")) self.LineMonos.append(p) # Volume Unactivating p = LineInfo(widget.LineUnActivate_5, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/UnActivate5")) self.LineUnActivates.append(p) p = LineInfo(widget.LineUnActivate_6, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/UnActivate6")) self.LineUnActivates.append(p) # Mute p = LineInfo(widget.LineMute_5, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Mute5")) self.LineMutes.append(p) p = LineInfo(widget.LineMute_6, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Mute6")) self.LineMutes.append(p) # per line Global Mute switch p = LineInfo(widget.LineGMute_5, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GMute5")) self.LineGMutes.append(p) p = LineInfo(widget.LineGMute_6, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GMute6")) self.LineGMutes.append(p) # per line Global Dim switch p = LineInfo(widget.LineGDim_5, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GDim5")) self.LineGDims.append(p) p = LineInfo(widget.LineGDim_6, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GDim6")) self.LineGDims.append(p) # Volume p = LineInfo(widget.LineVolume_5, DiscreteControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Volume5")) self.LineVolumes.append(p) p = LineInfo(widget.LineVolume_6, DiscreteControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Volume6")) self.LineVolumes.append(p) if ModelName == "SAFFIRE_PRO_40": self.nbLines = 10 # Mono/Stereo Switch p = LineInfo(widget.Mono_78, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/Mono/Line7Line8")) self.LineMonos.append(p) p = LineInfo(widget.Mono_910, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/Mono/Line9Line10")) self.LineMonos.append(p) # Volume Unactivating p = LineInfo(widget.LineUnActivate_7, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/UnActivate7")) self.LineUnActivates.append(p) p = LineInfo(widget.LineUnActivate_8, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/UnActivate8")) self.LineUnActivates.append(p) p = LineInfo(widget.LineUnActivate_9, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/UnActivate9")) self.LineUnActivates.append(p) p = LineInfo(widget.LineUnActivate_10, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/UnActivate10")) self.LineUnActivates.append(p) # Mute p = LineInfo(widget.LineMute_7, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Mute7")) self.LineMutes.append(p) p = LineInfo(widget.LineMute_8, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Mute8")) self.LineMutes.append(p) p = LineInfo(widget.LineMute_9, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Mute9")) self.LineMutes.append(p) p = LineInfo(widget.LineMute_10, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Mute10")) self.LineMutes.append(p) # per line Global Mute switch p = LineInfo(widget.LineGMute_7, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GMute7")) self.LineGMutes.append(p) p = LineInfo(widget.LineGMute_8, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GMute8")) self.LineGMutes.append(p) p = LineInfo(widget.LineGMute_9, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GMute9")) self.LineGMutes.append(p) p = LineInfo(widget.LineGMute_10, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GMute10")) self.LineGMutes.append(p) # per line Global Dim switch p = LineInfo(widget.LineGDim_7, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GDim7")) self.LineGDims.append(p) p = LineInfo(widget.LineGDim_8, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GDim8")) self.LineGDims.append(p) p = LineInfo(widget.LineGDim_9, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GDim9")) self.LineGDims.append(p) p = LineInfo(widget.LineGDim_10, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/GDim10")) self.LineGDims.append(p) # Volume p = LineInfo(widget.LineVolume_7, DiscreteControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Volume7")) self.LineVolumes.append(p) p = LineInfo(widget.LineVolume_8, DiscreteControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Volume8")) self.LineVolumes.append(p) p = LineInfo(widget.LineVolume_9, DiscreteControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Volume9")) self.LineVolumes.append(p) p = LineInfo(widget.LineVolume_10, DiscreteControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineOut/Volume10")) self.LineVolumes.append(p) # Adat/Spdif switch control from interface for Pro40 self.adatSpdifInterface = BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/AdatSpdif/State") widget.AdatSpdif.setChecked(self.adatSpdifInterface.selected()) widget.AdatSpdif.toggled.connect(self.adatSpdifToggle) if ModelName in ("SAFFIRE_PRO_14", "SAFFIRE_PRO_24"): # Line/Inst and Hi/Lo switches for Pro14 and 24 widget.LineInSwitches.setVisible(True) self.LineInSwitches = [] p = LineInfo(widget.LineInSwitchInst_1, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineInstGain/LineInst1")) self.LineInSwitches.append(p) p = LineInfo(widget.LineInSwitchInst_2, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineInstGain/LineInst2")) self.LineInSwitches.append(p) p = LineInfo(widget.LineInSwitchHi_3, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineInstGain/LineGain3")) self.LineInSwitches.append(p) p = LineInfo(widget.LineInSwitchHi_4, BooleanControl(self.hw, self.hw.basepath+"/EAP/Monitoring/LineInstGain/LineGain4")) self.LineInSwitches.append(p) for i in range(4): self.LineInSwitches[i].widget.setChecked(self.LineInSwitches[i].Interface.selected()) self.LineInSwitches[i].widget.toggled.connect(self.LineInSwitches[i].Interface.select) widget.LineInSwitchLine_1.setChecked(not self.LineInSwitches[0].Interface.selected()) widget.LineInSwitchLine_2.setChecked(not self.LineInSwitches[1].Interface.selected()) widget.LineInSwitchLo_3.setChecked(not self.LineInSwitches[2].Interface.selected()) widget.LineInSwitchLo_4.setChecked(not self.LineInSwitches[3].Interface.selected()) # Mono/Stereo Switch if ModelName != "SAFFIRE_PRO26": for i in range(int(self.nbLines/2)): self.LineMonos[i].widget.setChecked(self.LineMonos[i].Interface.selected()) self.LineMonos[i].widget.toggled.connect(self.LineMonos[i].Interface.select) for i in range(self.nbLines): self.LineUnActivates[i].widget.setChecked(self.LineUnActivates[i].Interface.selected()) self.LineUnActivates[i].widget.toggled.connect(self.LineUnActivates[i].Interface.select) self.LineMutes[i].widget.setChecked(self.LineMutes[i].Interface.selected()) self.LineMutes[i].widget.toggled.connect(self.LineMutes[i].Interface.select) self.LineGMutes[i].widget.setChecked(self.LineGMutes[i].Interface.selected()) self.LineGMutes[i].widget.toggled.connect(self.LineGMutes[i].Interface.select) self.LineGDims[i].widget.setChecked(self.LineGDims[i].Interface.selected()) self.LineGDims[i].widget.toggled.connect(self.LineGDims[i].Interface.select) self.LineVolumes[i].widget.setValue(self.LineVolumes[i].Interface.getvalue()) self.LineVolumes[i].widget.valueChanged.connect(self.LineVolumes[i].Interface.setvalue) # HW switch controls the possibility of monitoring each output separatly widget.HWSwitch.setChecked(self.HWselected()) widget.HWSwitch.toggled.connect(self.HWToggle) # Line Out monitoring enabling depends on H/W switch self.LineOut = widget.LineOut self.LineOut.setEnabled(self.HWselected()) def muteToggle(self, mute): self.muteInterface.select(mute) def dimToggle(self, dim): self.dimInterface.select(dim) self.DimLevel.setEnabled(dim) def adatSpdifToggle(self, spdif): self.adatSpdifInterface.select(spdif) # FIXME # Here we should consider that router configuration has changed inside the device # so that the device has to be rediscovered by the dbus (as it is the case when self.onSamplerateChange() def HWToggle(self, HW): for i in range(self.nbLines): self.LineUnActivates[i].widget.setChecked(not HW) self.LineMutes[i].widget.setChecked(False) self.LineGMutes[i].widget.setChecked(True) self.LineGDims[i].widget.setChecked(True) self.LineVolumes[i].widget.setValue(0) for i in self.LineMonos: i.widget.setChecked(False) self.LineOut.setEnabled(HW) def HWselected(self): selected = False for i in range(self.nbLines): if (not self.LineUnActivates[i].Interface.selected()): selected = True if (self.LineMutes[i].Interface.selected()): selected = True return selected def dimLevelChanged(self, value): self.dimLevelInterface.setvalue(value) def volumeChanged(self, value): self.volumeInterface.setvalue(value) def getDisplayTitle(self): return "Saffire PRO40/26/24/14 Mixer" # # vim: et ts=4 sw=4 libffado-2.4.5/support/mixer-qt4/ffado/mixer/saffire_mono.ui0000644000175000001440000031661514206145246023457 0ustar jwoitheusers Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. SaffireMixerMonoUI 0 0 973 397 0 0 Focusrite Saffire Mixer 0 Out1/2 0 0 250 0 Channel Control 0 127 1 16 Qt::Vertical 32 Level Qt::AlignCenter false Mute Hardware Level Control Level Dim Qt::Vertical QSizePolicy::Expanding 20 31 Input Mix 0 32767 10000 1000 Qt::Vertical 10000 IN1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN3 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN4 Qt::AlignCenter false DAW Return Channel Mix 0 32767 10000 1000 Qt::Vertical 10000 1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 5/6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 7/8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 9/10 Qt::AlignCenter false Return 0 32767 10000 1000 Qt::Vertical 10000 FX1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 FX2 Qt::AlignCenter false Out3/4 0 0 250 0 Channel Control 0 127 1 16 Qt::Vertical 32 Level Qt::AlignCenter false Qt::Vertical QSizePolicy::Expanding 31 91 Mute Hardware Level Control Input Mix 0 32767 10000 1000 Qt::Vertical 10000 IN1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN3 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN4 Qt::AlignCenter false DAW Return Channel Mix 0 32767 10000 1000 Qt::Vertical 10000 1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 5/6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 7/8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 9/10 Qt::AlignCenter false Return 0 32767 10000 1000 Qt::Vertical 10000 FX1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 FX2 Qt::AlignCenter false Out5/6 (Headphones 1) 0 0 250 0 Channel Control Mute Hardware Level Control 0 127 1 16 Qt::Vertical 32 Level Qt::AlignCenter false Qt::Vertical QSizePolicy::Expanding 20 61 Input Mix 0 32767 10000 1000 Qt::Vertical 10000 IN1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN3 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN4 Qt::AlignCenter false DAW Return Channel Mix 0 32767 10000 1000 Qt::Vertical 10000 1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 5/6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 7/8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 9/10 Qt::AlignCenter false Return 0 32767 10000 1000 Qt::Vertical 10000 FX1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 FX2 Qt::AlignCenter false Out7/8 (Headphones 2) 250 0 Channel Control Mute Hardware Level Control 0 127 1 16 Qt::Vertical 32 Level Qt::AlignCenter false Qt::Vertical QSizePolicy::Expanding 21 91 Input Mix 0 32767 10000 1000 Qt::Vertical 10000 IN1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN3 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN4 Qt::AlignCenter false DAW Return Channel Mix 0 32767 10000 1000 Qt::Vertical 10000 1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 5/6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 7/8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 9/10 Qt::AlignCenter false Return 0 32767 10000 1000 Qt::Vertical 10000 FX1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 FX2 Qt::AlignCenter false Out9/10 250 0 Channel Control Mute Qt::Vertical QSizePolicy::Expanding 41 81 Input Mix 0 32767 10000 1000 Qt::Vertical 10000 IN1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN3 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN4 Qt::AlignCenter false DAW Return Channel Mix 0 32767 10000 1000 Qt::Vertical 10000 1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 5/6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 7/8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 9/10 Qt::AlignCenter false Reverb Mix 0 32767 10000 1000 Qt::Vertical 10000 FX1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 FX2 Qt::AlignCenter false Device Control S/PDIF input enable Qt::Horizontal QSizePolicy::Expanding 70 21 true 0 0 Save settings to device Stereo Mode true 0 0 Save settings to device Refresh true 0 0 Save settings to device Save Settings qPixmapFromMimeSource libffado-2.4.5/support/mixer-qt4/ffado/mixer/saffire_stereo.ui0000644000175000001440000024465514206145246024014 0ustar jwoitheusers Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. SaffireMixerStereoUI 0 0 747 397 0 0 Focusrite Saffire Mixer 0 Out1/2 0 0 250 0 Channel Control 0 127 1 16 Qt::Vertical 32 Level Qt::AlignCenter false Mute Hardware Level Control Level Dim Qt::Vertical QSizePolicy::Expanding 20 31 Input Mix 0 32767 10000 1000 Qt::Vertical 10000 IN1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN3/4 Qt::AlignCenter false DAW Return Channel Mix 0 32767 10000 1000 Qt::Vertical 10000 1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 5/6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 7/8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 9/10 Qt::AlignCenter false Return 0 32767 10000 1000 Qt::Vertical 10000 FX Qt::AlignCenter false Out3/4 0 0 250 0 Channel Control 0 127 1 16 Qt::Vertical 32 Level Qt::AlignCenter false Qt::Vertical QSizePolicy::Expanding 31 91 Mute Hardware Level Control Input Mix 0 32767 10000 1000 Qt::Vertical 10000 IN1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN3/4 Qt::AlignCenter false DAW Return Channel Mix 0 32767 10000 1000 Qt::Vertical 10000 1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 5/6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 7/8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 9/10 Qt::AlignCenter false Return 0 32767 10000 1000 Qt::Vertical 10000 FX Qt::AlignCenter false Out5/6 (Headphones 1) 0 0 250 0 Channel Control Mute Hardware Level Control 0 127 1 16 Qt::Vertical 32 Level Qt::AlignCenter false Qt::Vertical QSizePolicy::Expanding 20 61 Input Mix 0 32767 10000 1000 Qt::Vertical 10000 IN1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN3/4 Qt::AlignCenter false DAW Return Channel Mix 0 32767 10000 1000 Qt::Vertical 10000 1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 5/6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 7/8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 9/10 Qt::AlignCenter false Return 0 32767 10000 1000 Qt::Vertical 10000 FX Qt::AlignCenter false Out7/8 (Headphones 2) 250 0 Channel Control Mute Hardware Level Control 0 127 1 16 Qt::Vertical 32 Level Qt::AlignCenter false Qt::Vertical QSizePolicy::Expanding 21 91 Input Mix 0 32767 10000 1000 Qt::Vertical 10000 IN1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN3/4 Qt::AlignCenter false DAW Return Channel Mix 0 32767 10000 1000 Qt::Vertical 10000 1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 5/6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 7/8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 9/10 Qt::AlignCenter false Return 0 32767 10000 1000 Qt::Vertical 10000 FX Qt::AlignCenter false Out9/10 250 0 Channel Control Mute Qt::Vertical QSizePolicy::Expanding 41 81 Input Mix 0 32767 10000 1000 Qt::Vertical 10000 IN1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IN3/4 Qt::AlignCenter false DAW Return Channel Mix 0 32767 10000 1000 Qt::Vertical 10000 1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 5/6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 7/8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 9/10 Qt::AlignCenter false Reverb Mix 0 32767 10000 1000 Qt::Vertical 10000 IN1/2 Qt::AlignCenter false Device Control S/PDIF input enable Qt::Horizontal QSizePolicy::Expanding 240 21 true 0 0 Save settings to device Mono Mode true 0 0 Save settings to device Refresh true 0 0 Save settings to device Save Settings qPixmapFromMimeSource libffado-2.4.5/support/mixer-qt4/ffado/mixer/saffirele_large.ui0000644000175000001440000022312314206145246024111 0ustar jwoitheusers Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. SaffireLEMixerLargeUI 0 0 988 465 Focusrite Saffire LE Mixer 0 Out&1 Input Mix 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 IN1/2 Qt::AlignCenter false IN3/4 Qt::AlignCenter false SPDIF Qt::AlignCenter false DAW Return Channel Mix 4 Qt::AlignCenter false 3 Qt::AlignCenter false 2 Qt::AlignCenter false 1 Qt::AlignCenter false 5 Qt::AlignCenter false 6 Qt::AlignCenter false 7 Qt::AlignCenter false 8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 Out&2 Input Mix IN1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 SPDIF Qt::AlignCenter false IN3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 DAW Return Channel Mix 4 Qt::AlignCenter false 3 Qt::AlignCenter false 2 Qt::AlignCenter false 1 Qt::AlignCenter false 5 Qt::AlignCenter false 6 Qt::AlignCenter false 7 Qt::AlignCenter false 8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 Out&3 Input Mix IN1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 SPDIF Qt::AlignCenter false IN3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 DAW Return Channel Mix 4 Qt::AlignCenter false 3 Qt::AlignCenter false 2 Qt::AlignCenter false 1 Qt::AlignCenter false 5 Qt::AlignCenter false 6 Qt::AlignCenter false 7 Qt::AlignCenter false 8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 Out&4 Input Mix IN1/2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 SPDIF Qt::AlignCenter false IN3/4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 DAW Return Channel Mix 4 Qt::AlignCenter false 3 Qt::AlignCenter false 2 Qt::AlignCenter false 1 Qt::AlignCenter false 5 Qt::AlignCenter false 6 Qt::AlignCenter false 7 Qt::AlignCenter false 8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 Output Level 0 127 1 16 Qt::Vertical 32 0 127 1 16 Qt::Vertical 32 0 127 1 16 Qt::Vertical 32 1/2 Qt::AlignCenter false 3/4 Qt::AlignCenter false 5/6 Qt::AlignCenter false Ignore slider Qt::AlignCenter false Mute CH5/&6 CH3/4 CH1/2 High Gain Input 3 Input 4 S/PDIF Transp &MIDI Tru Alt+M Save settings to device Save qPixmapFromMimeSource libffado-2.4.5/support/mixer-qt4/ffado/mixer/saffirele_small.ui0000644000175000001440000006752014206145246024136 0ustar jwoitheusers Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. SaffireLEMixerSmallUI 0 0 620 425 Focusrite Saffire LE Mixer Record Mix 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 IN1/2 Qt::AlignCenter false IN3/4 Qt::AlignCenter false SPDIF Qt::AlignCenter false 0 Out&1 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 1 Qt::AlignCenter false 2 Qt::AlignCenter false REC Qt::AlignCenter false Out&2 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 1 Qt::AlignCenter false 2 Qt::AlignCenter false REC Qt::AlignCenter false Out&3 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 1 Qt::AlignCenter false 2 Qt::AlignCenter false REC Qt::AlignCenter false Out&4 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 1 Qt::AlignCenter false 2 Qt::AlignCenter false REC Qt::AlignCenter false Output Level 0 127 1 16 Qt::Vertical 32 0 127 1 16 Qt::Vertical 32 0 127 1 16 Qt::Vertical 32 1/2 Qt::AlignCenter false 3/4 Qt::AlignCenter false 5/6 Qt::AlignCenter false Ignore slider Qt::AlignCenter false Mute CH5/&6 CH3/4 CH1/2 High Gain Input 3 Input 4 S/PDIF Transp &MIDI Tru Alt+M Save settings to device Save qPixmapFromMimeSource libffado-2.4.5/support/mixer-qt4/ffado/mixer/saffirepro.py0000644000175000001440000005735214206145246023163 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QWidget, QHBoxLayout, QMessageBox from ffado.import_pyqt import * from ffado.config import * from ffado.mixer.saffire_base import SaffireMixerBase import logging log = logging.getLogger('saffirepro') class SaffirePro(QWidget): def __init__(self,parent = None): QWidget.__init__(self, parent) self.have_adat = False self.samplerate = None self.is_pro10 = None # make a layout self.layout = QHBoxLayout() self.setLayout(self.layout) def show(self): self.selectCorrectMode() QWidget.show(self) def getDisplayTitle(self): try: return self.nickname.text() except: if self.is_pro10: return "SaffirePRO10" else: return "SaffirePRO26" def selectCorrectMode(self): if self.samplerate <= 96000: log.debug("large") self.small.hide() self.large.initValues() self.large.show() else: log.debug("small") self.large.hide() self.small.initValues() self.small.show() def initValues(self): self.is_not_streaming = self.samplerateselect.canChangeValue() selected = self.samplerateselect.selected() self.samplerate = int(self.samplerateselect.getEnumLabel( selected )) log.debug("detected samplerate %d" % self.samplerate) # adat on PRO26 makes a difference modelId = self.configrom.getModelId() if modelId == 0x00000003: # PRO26 self.is_pro10 = False state = self.hw.getDiscrete('/Control/ADATDisable') if state: self.have_adat = False log.debug("detected PRO26, ADAT disabled") else: self.have_adat = True log.debug("detected PRO26, ADAT enabled") elif modelId == 0x00000006: # PRO10 self.is_pro10 = True self.have_adat = False # create the child widgets self.small = SaffireProMixerSmall(self) self.layout.addWidget(self.small) self.large = SaffireProMixerLarge(self) self.layout.addWidget(self.large) self.small.hw = self.hw self.small.configrom = self.configrom self.large.hw = self.hw self.large.configrom = self.configrom self.selectCorrectMode() def polledUpdate(self): self.is_not_streaming = self.samplerateselect.canChangeValue() if self.samplerate <= 96000: self.large.polledUpdate() else: self.small.polledUpdate() class SaffireProMixerLarge(QWidget, SaffireMixerBase): def __init__(self,parent = None): self.my_parent = parent QWidget.__init__(self,parent) SaffireMixerBase.__init__(self) uicLoad("ffado/mixer/saffirepro_large", self) self.have_adat = False log.debug("Init large Saffire Pro mixer window") self.VolumeControls={ self.sldIMixAnalog1L: ['/Mixer/InputMix', 0, 0], self.sldIMixAnalog1R: ['/Mixer/InputMix', 0, 1], self.sldIMixAnalog2L: ['/Mixer/InputMix', 1, 0], self.sldIMixAnalog2R: ['/Mixer/InputMix', 1, 1], self.sldIMixAnalog3L: ['/Mixer/InputMix', 2, 0], self.sldIMixAnalog3R: ['/Mixer/InputMix', 2, 1], self.sldIMixAnalog4L: ['/Mixer/InputMix', 3, 0], self.sldIMixAnalog4R: ['/Mixer/InputMix', 3, 1], self.sldIMixAnalog5L: ['/Mixer/InputMix', 4, 0], self.sldIMixAnalog5R: ['/Mixer/InputMix', 4, 1], self.sldIMixAnalog6L: ['/Mixer/InputMix', 5, 0], self.sldIMixAnalog6R: ['/Mixer/InputMix', 5, 1], self.sldIMixAnalog7L: ['/Mixer/InputMix', 6, 0], self.sldIMixAnalog7R: ['/Mixer/InputMix', 6, 1], self.sldIMixAnalog8L: ['/Mixer/InputMix', 7, 0], self.sldIMixAnalog8R: ['/Mixer/InputMix', 7, 1], self.sldIMixAnalog9L: ['/Mixer/InputMix', 8, 0], self.sldIMixAnalog9R: ['/Mixer/InputMix', 8, 1], self.sldIMixAnalog10L: ['/Mixer/InputMix', 9, 0], self.sldIMixAnalog10R: ['/Mixer/InputMix', 9, 1], self.sldIMixADAT11L: ['/Mixer/InputMix', 10, 0], self.sldIMixADAT11R: ['/Mixer/InputMix', 10, 1], self.sldIMixADAT12L: ['/Mixer/InputMix', 11, 0], self.sldIMixADAT12R: ['/Mixer/InputMix', 11, 1], self.sldIMixADAT13L: ['/Mixer/InputMix', 12, 0], self.sldIMixADAT13R: ['/Mixer/InputMix', 12, 1], self.sldIMixADAT14L: ['/Mixer/InputMix', 13, 0], self.sldIMixADAT14R: ['/Mixer/InputMix', 13, 1], self.sldIMixADAT15L: ['/Mixer/InputMix', 14, 0], self.sldIMixADAT15R: ['/Mixer/InputMix', 14, 1], self.sldIMixADAT16L: ['/Mixer/InputMix', 15, 0], self.sldIMixADAT16R: ['/Mixer/InputMix', 15, 1], self.sldIMixADAT17L: ['/Mixer/InputMix', 16, 0], self.sldIMixADAT17R: ['/Mixer/InputMix', 16, 1], self.sldIMixADAT18L: ['/Mixer/InputMix', 17, 0], self.sldIMixADAT18R: ['/Mixer/InputMix', 17, 1], self.sldIMixADAT21L: ['/Mixer/InputMix', 18, 0], self.sldIMixADAT21R: ['/Mixer/InputMix', 18, 1], self.sldIMixADAT22L: ['/Mixer/InputMix', 19, 0], self.sldIMixADAT22R: ['/Mixer/InputMix', 19, 1], self.sldIMixADAT23L: ['/Mixer/InputMix', 20, 0], self.sldIMixADAT23R: ['/Mixer/InputMix', 20, 1], self.sldIMixADAT24L: ['/Mixer/InputMix', 21, 0], self.sldIMixADAT24R: ['/Mixer/InputMix', 21, 1], self.sldIMixADAT25L: ['/Mixer/InputMix', 22, 0], self.sldIMixADAT25R: ['/Mixer/InputMix', 22, 1], self.sldIMixADAT26L: ['/Mixer/InputMix', 23, 0], self.sldIMixADAT26R: ['/Mixer/InputMix', 23, 1], self.sldIMixADAT27L: ['/Mixer/InputMix', 24, 0], self.sldIMixADAT27R: ['/Mixer/InputMix', 24, 1], self.sldIMixADAT28L: ['/Mixer/InputMix', 25, 0], self.sldIMixADAT28R: ['/Mixer/InputMix', 25, 1], self.sldOMixPC1O1: ['/Mixer/OutputMix', 0, 0], self.sldOMixPC2O2: ['/Mixer/OutputMix', 1, 1], self.sldOMixPC3O3: ['/Mixer/OutputMix', 2, 2], self.sldOMixPC4O4: ['/Mixer/OutputMix', 3, 3], self.sldOMixPC5O5: ['/Mixer/OutputMix', 4, 4], self.sldOMixPC6O6: ['/Mixer/OutputMix', 5, 5], self.sldOMixPC7O7: ['/Mixer/OutputMix', 6, 6], self.sldOMixPC8O8: ['/Mixer/OutputMix', 7, 7], self.sldOMixPC9O9: ['/Mixer/OutputMix', 8, 8], self.sldOMixPC10O10: ['/Mixer/OutputMix', 9, 9], self.sldOMixPC1O3: ['/Mixer/OutputMix', 0, 2], self.sldOMixPC2O4: ['/Mixer/OutputMix', 1, 3], self.sldOMixPC1O5: ['/Mixer/OutputMix', 0, 4], self.sldOMixPC2O6: ['/Mixer/OutputMix', 1, 5], self.sldOMixPC1O7: ['/Mixer/OutputMix', 0, 6], self.sldOMixPC2O8: ['/Mixer/OutputMix', 1, 7], self.sldOMixPC1O9: ['/Mixer/OutputMix', 0, 8], self.sldOMixPC2O10: ['/Mixer/OutputMix', 1, 9], self.sldOMixIMixO1: ['/Mixer/OutputMix', 10, 0], self.sldOMixIMixO2: ['/Mixer/OutputMix', 11, 1], self.sldOMixIMixO3: ['/Mixer/OutputMix', 10, 2], self.sldOMixIMixO4: ['/Mixer/OutputMix', 11, 3], self.sldOMixIMixO5: ['/Mixer/OutputMix', 10, 4], self.sldOMixIMixO6: ['/Mixer/OutputMix', 11, 5], self.sldOMixIMixO7: ['/Mixer/OutputMix', 10, 6], self.sldOMixIMixO8: ['/Mixer/OutputMix', 11, 7], self.sldOMixIMixO9: ['/Mixer/OutputMix', 10, 8], self.sldOMixIMixO10: ['/Mixer/OutputMix', 11, 9], } self.SelectorControls={ # control elements self.chkInsert1: ['/Control/Insert1'], self.chkInsert2: ['/Control/Insert2'], self.chkPhantom14: ['/Control/Phantom_1to4'], self.chkPhantom58: ['/Control/Phantom_5to8'], self.chkAC3: ['/Control/AC3pass'], self.chkMidiThru: ['/Control/MidiTru'], self.chkHighVoltage: ['/Control/UseHighVoltageRail'], #self.chkEnableADAT1: ['/Control/EnableAdat1'], #self.chkEnableADAT2: ['/Control/EnableAdat2'], #self.chkEnableSPDIF1: ['/Control/EnableSPDIF1'], self.chkMidiEnable: ['/Control/MIDIEnable'], self.chkAdatDisable: ['/Control/ADATDisable'], # Mixer switches self.chkMute12: ['/Mixer/Out12Mute', [self.chkHwCtrl12]], self.chkHwCtrl12: ['/Mixer/Out12HwCtrl'], self.chkPad12: ['/Mixer/Out12Pad'], self.chkDim12: ['/Mixer/Out12Dim'], self.chkMute34: ['/Mixer/Out34Mute', [self.chkHwCtrl34]], self.chkHwCtrl34: ['/Mixer/Out34HwCtrl'], self.chkPad34: ['/Mixer/Out34Pad'], self.chkDim34: ['/Mixer/Out34Dim'], self.chkMute56: ['/Mixer/Out56Mute', [self.chkHwCtrl56]], self.chkHwCtrl56: ['/Mixer/Out56HwCtrl'], self.chkPad56: ['/Mixer/Out56Pad'], self.chkDim56: ['/Mixer/Out56Dim'], self.chkMute78: ['/Mixer/Out78Mute', [self.chkHwCtrl78]], self.chkHwCtrl78: ['/Mixer/Out78HwCtrl'], self.chkPad78: ['/Mixer/Out78Pad'], self.chkDim78: ['/Mixer/Out78Dim'], # direct monitoring self.chkMonitor1: ['/Mixer/DirectMonitorCH1'], self.chkMonitor2: ['/Mixer/DirectMonitorCH2'], self.chkMonitor3: ['/Mixer/DirectMonitorCH3'], self.chkMonitor4: ['/Mixer/DirectMonitorCH4'], self.chkMonitor5: ['/Mixer/DirectMonitorCH5'], self.chkMonitor6: ['/Mixer/DirectMonitorCH6'], self.chkMonitor7: ['/Mixer/DirectMonitorCH7'], self.chkMonitor8: ['/Mixer/DirectMonitorCH8'], } self.VolumeControlsLowRes={ self.sldOut12Level: ['/Mixer/Out12Level'], self.sldOut34Level: ['/Mixer/Out34Level'], self.sldOut56Level: ['/Mixer/Out56Level'], self.sldOut78Level: ['/Mixer/Out78Level'], } self.TriggerButtonControls={ self.btnReboot: ['/Control/Reboot'], self.btnIdentify: ['/Control/FlashLed'], self.btnSaveSettings: ['/Control/SaveSettings'], } self.TextControls={ } self.saveTextControls={ } self.ComboControls={ self.comboStandalone: ['/Control/StandaloneConfig'], } def updateMatrixVolume(self,a0): SaffireMixerBase.updateMatrixVolume(self,a0) def updateLowResVolume(self,a0): SaffireMixerBase.updateLowResVolume(self,a0) def updateSelector(self,a0): sender = self.sender() #if sender == self.chkAC3 and not self.my_parent.is_not_streaming: #msg = QMessageBox() #msg.question( msg, "Error", \ #"Change not permitted. Is streaming active?", \ #QMessageBox.Ok ) #self.chkAC3.setEnabled(False) #if a0: #self.chkAC3.setChecked(False) #else: #self.chkAC3.setChecked(True) #return if sender == self.chkMidiEnable and not self.my_parent.is_not_streaming: msg = QMessageBox() msg.question( msg, "Error", \ "Change not permitted. Is streaming active?", \ QMessageBox.Ok ) self.chkMidiEnable.setEnabled(False) state = self.hw.getDiscrete(self.SelectorControls[self.chkMidiEnable][0]) if state: self.chkMidiEnable.setChecked(True) else: self.chkMidiEnable.setChecked(False) return if sender == self.chkAdatDisable and not self.my_parent.is_not_streaming: msg = QMessageBox() msg.question( msg, "Error", \ "Change not permitted. Is streaming active?", \ QMessageBox.Ok ) self.chkAdatDisable.setEnabled(False) state = self.hw.getDiscrete(self.SelectorControls[self.chkAdatDisable][0]) if state: self.chkAdatDisable.setChecked(True) else: self.chkAdatDisable.setChecked(False) return SaffireMixerBase.updateSelector(self,a0) def triggerButton(self): sender = self.sender() if sender == self.btnReboot and not self.my_parent.is_not_streaming: msg = QMessageBox() msg.question( msg, "Error", \ "Operation not permitted. Is streaming active?", \ QMessageBox.Ok ) self.btnReboot.setEnabled(False) return SaffireMixerBase.triggerButton(self) def saveText(self): SaffireMixerBase.saveText(self) def initCombo(self, combo): SaffireMixerBase.initCombo(self,combo) def selectCombo(self, mode): SaffireMixerBase.selectCombo(self,mode) def updateValues(self): for i in range(self.tabInputMix.count()): self.tabInputMix.setTabEnabled(i, True) if not self.my_parent.have_adat: for i in range(self.tabInputMix.count()): page = self.tabInputMix.widget(i) name = page.objectName() if name[0:4] == "adat": self.tabInputMix.setTabEnabled(i, False) else: self.tabInputMix.setTabEnabled(i, True) self.tabInputMix.setCurrentWidget(self.tabInputMix.widget(0)) SaffireMixerBase.updateValues(self) def polledUpdate(self): #log.debug("polled update (large)") self.polledUpdateHwCtrl(self.chkHwCtrl12, self.sldOut12Level) self.polledUpdateHwCtrl(self.chkHwCtrl34, self.sldOut34Level) self.polledUpdateHwCtrl(self.chkHwCtrl56, self.sldOut56Level) self.polledUpdateHwCtrl(self.chkHwCtrl78, self.sldOut78Level) #make these inaccessible whenever streaming is running #self.chkAC3.setEnabled(self.my_parent.is_not_streaming) self.chkMidiEnable.setEnabled(self.my_parent.is_not_streaming) self.chkAdatDisable.setEnabled(self.my_parent.is_not_streaming) self.btnReboot.setEnabled(self.my_parent.is_not_streaming) def polledUpdateHwCtrl(self, selector, volctrl): state = selector.isChecked() if state: self.polledUpdateVolumeLowRes('/Mixer/MonitorDial', volctrl, 2) volctrl.setEnabled(False) else: volctrl.setEnabled(True) class SaffireProMixerSmall(QWidget, SaffireMixerBase): def __init__(self,parent = None): self.my_parent = parent QWidget.__init__(self,parent) SaffireMixerBase.__init__(self) uicLoad("ffado/mixer/saffirepro_small", self) log.debug("Init small Saffire Pro mixer window") self.VolumeControls={ self.sldOMixPC1O1: ['/Mixer/OutputMix', 0, 0], self.sldOMixPC2O2: ['/Mixer/OutputMix', 1, 1], self.sldOMixPC3O3: ['/Mixer/OutputMix', 2, 2], self.sldOMixPC4O4: ['/Mixer/OutputMix', 3, 3], self.sldOMixPC5O5: ['/Mixer/OutputMix', 4, 4], self.sldOMixPC6O6: ['/Mixer/OutputMix', 5, 5], self.sldOMixPC7O7: ['/Mixer/OutputMix', 6, 6], self.sldOMixPC8O8: ['/Mixer/OutputMix', 7, 7], self.sldOMixPC9O9: ['/Mixer/OutputMix', 8, 8], self.sldOMixPC10O10: ['/Mixer/OutputMix', 9, 9], self.sldOMixPC1O3: ['/Mixer/OutputMix', 0, 2], self.sldOMixPC2O4: ['/Mixer/OutputMix', 1, 3], self.sldOMixPC1O5: ['/Mixer/OutputMix', 0, 4], self.sldOMixPC2O6: ['/Mixer/OutputMix', 1, 5], self.sldOMixPC1O7: ['/Mixer/OutputMix', 0, 6], self.sldOMixPC2O8: ['/Mixer/OutputMix', 1, 7], self.sldOMixPC1O9: ['/Mixer/OutputMix', 0, 8], self.sldOMixPC2O10: ['/Mixer/OutputMix', 1, 9], self.sldOMixIMixO1: ['/Mixer/OutputMix', 10, 0], self.sldOMixIMixO2: ['/Mixer/OutputMix', 11, 1], self.sldOMixIMixO3: ['/Mixer/OutputMix', 10, 2], self.sldOMixIMixO4: ['/Mixer/OutputMix', 11, 3], self.sldOMixIMixO5: ['/Mixer/OutputMix', 10, 4], self.sldOMixIMixO6: ['/Mixer/OutputMix', 11, 5], self.sldOMixIMixO7: ['/Mixer/OutputMix', 10, 6], self.sldOMixIMixO8: ['/Mixer/OutputMix', 11, 7], self.sldOMixIMixO9: ['/Mixer/OutputMix', 10, 8], self.sldOMixIMixO10: ['/Mixer/OutputMix', 11, 9], } self.SelectorControls={ # control elements self.chkInsert1: ['/Control/Insert1'], self.chkInsert2: ['/Control/Insert2'], self.chkPhantom14: ['/Control/Phantom_1to4'], self.chkPhantom58: ['/Control/Phantom_5to8'], self.chkAC3: ['/Control/AC3pass'], self.chkMidiThru: ['/Control/MidiTru'], self.chkHighVoltage: ['/Control/UseHighVoltageRail'], #self.chkEnableADAT1: ['/Control/EnableAdat1'], #self.chkEnableADAT2: ['/Control/EnableAdat2'], #self.chkEnableSPDIF1: ['/Control/EnableSPDIF1'], self.chkMidiEnable: ['/Control/MIDIEnable'], self.chkAdatDisable: ['/Control/ADATDisable'], # Mixer switches self.chkMute12: ['/Mixer/Out12Mute'], self.chkHwCtrl12: ['/Mixer/Out12HwCtrl'], self.chkPad12: ['/Mixer/Out12Pad'], self.chkDim12: ['/Mixer/Out12Dim'], self.chkMute34: ['/Mixer/Out34Mute'], self.chkHwCtrl34: ['/Mixer/Out34HwCtrl'], self.chkPad34: ['/Mixer/Out34Pad'], self.chkDim34: ['/Mixer/Out34Dim'], self.chkMute56: ['/Mixer/Out56Mute'], self.chkHwCtrl56: ['/Mixer/Out56HwCtrl'], self.chkPad56: ['/Mixer/Out56Pad'], self.chkDim56: ['/Mixer/Out56Dim'], self.chkMute78: ['/Mixer/Out78Mute'], self.chkHwCtrl78: ['/Mixer/Out78HwCtrl'], self.chkPad78: ['/Mixer/Out78Pad'], self.chkDim78: ['/Mixer/Out78Dim'], # direct monitoring self.chkMonitor1: ['/Mixer/DirectMonitorCH1'], self.chkMonitor2: ['/Mixer/DirectMonitorCH2'], self.chkMonitor3: ['/Mixer/DirectMonitorCH3'], self.chkMonitor4: ['/Mixer/DirectMonitorCH4'], self.chkMonitor5: ['/Mixer/DirectMonitorCH5'], self.chkMonitor6: ['/Mixer/DirectMonitorCH6'], self.chkMonitor7: ['/Mixer/DirectMonitorCH7'], self.chkMonitor8: ['/Mixer/DirectMonitorCH8'], } self.VolumeControlsLowRes={ self.sldOut12Level: ['/Mixer/Out12Level'], self.sldOut34Level: ['/Mixer/Out34Level'], self.sldOut56Level: ['/Mixer/Out56Level'], self.sldOut78Level: ['/Mixer/Out78Level'], } self.TriggerButtonControls={ self.btnReboot: ['/Control/Reboot'], self.btnIdentify: ['/Control/FlashLed'], self.btnSaveSettings: ['/Control/SaveSettings'], } self.TextControls={ } self.saveTextControls={ } self.ComboControls={ self.comboStandalone: ['/Control/StandaloneConfig'], } def updateMatrixVolume(self,a0): SaffireMixerBase.updateMatrixVolume(self,a0) def updateLowResVolume(self,a0): SaffireMixerBase.updateLowResVolume(self,a0) def updateSelector(self,a0): sender = self.sender() #if sender == self.chkAC3 and not self.my_parent.is_not_streaming: #msg = QMessageBox() #msg.question( msg, "Error", \ #"Change not permitted. Is streaming active?", \ #QMessageBox.Ok ) #self.chkAC3.setEnabled(False) #if a0: #self.chkAC3.setChecked(False) #else: #self.chkAC3.setChecked(True) #return if sender == self.chkMidiEnable and not self.my_parent.is_not_streaming: msg = QMessageBox() msg.question( msg, "Error", \ "Change not permitted. Is streaming active?", \ QMessageBox.Ok ) self.chkMidiEnable.setEnabled(False) state = self.hw.getDiscrete(self.SelectorControls[self.chkMidiEnable][0]) if state: self.chkMidiEnable.setChecked(True) else: self.chkMidiEnable.setChecked(False) return if sender == self.chkAdatDisable and not self.my_parent.is_not_streaming: msg = QMessageBox() msg.question( msg, "Error", \ "Change not permitted. Is streaming active?", \ QMessageBox.Ok ) self.chkAdatDisable.setEnabled(False) state = self.hw.getDiscrete(self.SelectorControls[self.chkAdatDisable][0]) if state: self.chkAdatDisable.setChecked(True) else: self.chkAdatDisable.setChecked(False) return SaffireMixerBase.updateSelector(self,a0) def triggerButton(self): sender = self.sender() if sender == self.btnReboot and not self.my_parent.is_not_streaming: msg = QMessageBox() msg.question( msg, "Error", \ "Operation not permitted. Is streaming active?", \ QMessageBox.Ok ) self.btnReboot.setEnabled(False) return SaffireMixerBase.triggerButton(self) def saveText(self): SaffireMixerBase.saveText(self) def initCombo(self, combo): SaffireMixerBase.initCombo(self,combo) def selectCombo(self, mode): SaffireMixerBase.selectCombo(self,mode) def updateValues(self): SaffireMixerBase.updateValues(self) def polledUpdate(self): #log.debug("polled update (small)") self.polledUpdateHwCtrl(self.chkHwCtrl12, self.sldOut12Level) self.polledUpdateHwCtrl(self.chkHwCtrl34, self.sldOut34Level) self.polledUpdateHwCtrl(self.chkHwCtrl56, self.sldOut56Level) self.polledUpdateHwCtrl(self.chkHwCtrl78, self.sldOut78Level) #make these inaccessible whenever streaming is running #self.chkAC3.setEnabled(self.my_parent.is_not_streaming) self.chkMidiEnable.setEnabled(self.my_parent.is_not_streaming) self.chkAdatDisable.setEnabled(self.my_parent.is_not_streaming) self.btnReboot.setEnabled(self.my_parent.is_not_streaming) def polledUpdateHwCtrl(self, selector, volctrl): state = selector.isChecked() if state: self.polledUpdateVolumeLowRes('/Mixer/MonitorDial', volctrl, 2) volctrl.setEnabled(False) else: volctrl.setEnabled(True) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/mixer/saffirepro_large.ui0000644000175000001440000044301114206145246024311 0ustar jwoitheusers Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. SaffireProMixerLargeUI 0 0 840 637 Focusrite Saffire Pro (10/26) Mixer Input Mix A&nalog 0 32767 10000 1000 Qt::Vertical 10000 R Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 3 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 R Qt::AlignCenter false L Qt::AlignCenter false L Qt::AlignCenter false R Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 R Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 L Qt::AlignCenter false L Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 6 Qt::AlignCenter false L Qt::AlignCenter false R Qt::AlignCenter false R Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 4 Qt::AlignCenter false R Qt::AlignCenter false R Qt::AlignCenter false 7 Qt::AlignCenter false L Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 L Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 L Qt::AlignCenter false 8 Qt::AlignCenter false 10 Qt::AlignCenter false L Qt::AlignCenter false R Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 5 Qt::AlignCenter false L Qt::AlignCenter false 9 Qt::AlignCenter false R Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 1 Qt::AlignCenter false &ADAT1 0 32767 10000 1000 Qt::Vertical 10000 2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 L Qt::AlignCenter false R Qt::AlignCenter false L Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 R Qt::AlignCenter false R Qt::AlignCenter false 7 Qt::AlignCenter false L Qt::AlignCenter false 6 Qt::AlignCenter false 4 Qt::AlignCenter false L Qt::AlignCenter false R Qt::AlignCenter false 5 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 R Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 R Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 R Qt::AlignCenter false 8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 L Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 L Qt::AlignCenter false R Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 L Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 1 Qt::AlignCenter false L Qt::AlignCenter false 3 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 ADAT2 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 R Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 R Qt::AlignCenter false 3 Qt::AlignCenter false L Qt::AlignCenter false 7 Qt::AlignCenter false L Qt::AlignCenter false 0 100 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 R Qt::AlignCenter false R Qt::AlignCenter false 2 Qt::AlignCenter false L Qt::AlignCenter false L Qt::AlignCenter false 6 Qt::AlignCenter false L Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 R Qt::AlignCenter false R Qt::AlignCenter false 1 Qt::AlignCenter false 5 Qt::AlignCenter false L Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 R Qt::AlignCenter false R Qt::AlignCenter false 4 Qt::AlignCenter false L Qt::AlignCenter false 8 Qt::AlignCenter false L Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 Output Mix 4 Out1/&2 Mute CH2 Direct Mon &Level Dim Alt+L CH1 Direct Mon Front dial control &Monitor Pad Alt+M Out1 Qt::AlignCenter false PC1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 IMixL Qt::AlignCenter false IMixR Qt::AlignCenter false Out2 Qt::AlignCenter false PC2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 Level Qt::AlignHCenter 0 80 0 127 1 16 Qt::Vertical 32 Qt::Horizontal QSizePolicy::Expanding 71 31 Out3/&4 CH3 Direct Mon Front dial control CH4 Direct Mon Mute Monitor Pad Level Dim IMixL Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 Out3 Qt::AlignCenter false PC3 Qt::AlignCenter false PC1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 PC4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IMixR Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 PC2 Qt::AlignCenter false Out4 Qt::AlignCenter false Level Qt::AlignHCenter 0 127 1 16 Qt::Vertical 32 Qt::Horizontal QSizePolicy::Expanding 101 21 Out5/&6 CH6 Direct Mon CH5 Direct Mon Level Dim Front dial control &Monitor Pad Alt+M Mute PC1 Qt::AlignCenter false IMixL Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 PC5 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 Out5 Qt::AlignCenter false PC6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IMixR Qt::AlignCenter false PC2 Qt::AlignCenter false Out6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 Level Qt::AlignHCenter 0 127 1 16 Qt::Vertical 32 Qt::Horizontal QSizePolicy::Expanding 61 21 Out7/&8 CH8 Direct Mon Level Dim Mute &Monitor Pad Alt+M CH7 Direct Mon Front dial control PC1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 PC7 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 Out7 Qt::AlignCenter false IMixL Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IMixR Qt::AlignCenter false Out8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 PC2 Qt::AlignCenter false PC8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 Level Qt::AlignHCenter 0 127 1 16 Qt::Vertical 32 Qt::Horizontal QSizePolicy::Expanding 81 21 Out9/&10 0 32767 10000 1000 Qt::Vertical 10000 PC1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IMixL Qt::AlignCenter false Out9 Qt::AlignCenter false PC9 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 Out10 Qt::AlignCenter false PC2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 PC10 Qt::AlignCenter false IMixR Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 Device Control Phantom: false IN 1-4 IN &5-8 Alt+5 Insert: false CH1 CH2 Disable ADAT I/O Enable MIDI false AC&3 Passthrough Alt+3 &MIDI Thru Alt+M &Use high voltage supply Alt+U Qt::Vertical QSizePolicy::Expanding 20 21 &Save Settings Alt+S Identif&y Device Alt+Y Re&boot Device Alt+B Refresh qPixmapFromMimeSource btnRefresh clicked() SaffireProMixerLargeUI updateValues() 20 20 20 20 libffado-2.4.5/support/mixer-qt4/ffado/mixer/saffirepro_small.ui0000644000175000001440000021312014206145246024323 0ustar jwoitheusers Copyright (C) 2005-2008 by Pieter Palmers This file is part of FFADO FFADO = Free FireWire (pro-)audio drivers for Linux FFADO is based upon FreeBoB. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. SaffireProMixerSmallUI 0 0 840 379 Focusrite Saffire Pro (10/26) Mixer Output Mix 4 Out1/&2 Mute CH2 Direct Mon &Level Dim Alt+L CH1 Direct Mon Front dial control &Monitor Pad Alt+M Out1 Qt::AlignCenter false PC1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 IMixL Qt::AlignCenter false IMixR Qt::AlignCenter false Out2 Qt::AlignCenter false PC2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 Level Qt::AlignHCenter 0 80 0 127 1 16 Qt::Vertical 32 Qt::Horizontal QSizePolicy::Expanding 71 31 Out3/&4 CH3 Direct Mon Front dial control CH4 Direct Mon Mute Monitor Pad Level Dim IMixL Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 Out3 Qt::AlignCenter false PC3 Qt::AlignCenter false PC1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 PC4 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IMixR Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 PC2 Qt::AlignCenter false Out4 Qt::AlignCenter false Level Qt::AlignHCenter 0 127 1 16 Qt::Vertical 32 Qt::Horizontal QSizePolicy::Expanding 101 21 Out5/&6 CH6 Direct Mon CH5 Direct Mon Level Dim Front dial control &Monitor Pad Alt+M Mute PC1 Qt::AlignCenter false IMixL Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 PC5 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 Out5 Qt::AlignCenter false PC6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IMixR Qt::AlignCenter false PC2 Qt::AlignCenter false Out6 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 Level Qt::AlignHCenter 0 127 1 16 Qt::Vertical 32 Qt::Horizontal QSizePolicy::Expanding 61 21 Out7/&8 CH8 Direct Mon Level Dim Mute &Monitor Pad Alt+M CH7 Direct Mon Front dial control PC1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 PC7 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 Out7 Qt::AlignCenter false IMixL Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IMixR Qt::AlignCenter false Out8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 PC2 Qt::AlignCenter false PC8 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 Level Qt::AlignHCenter 0 127 1 16 Qt::Vertical 32 Qt::Horizontal QSizePolicy::Expanding 81 21 Out9/&10 0 32767 10000 1000 Qt::Vertical 10000 PC1 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 IMixL Qt::AlignCenter false Out9 Qt::AlignCenter false PC9 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 0 32767 10000 1000 Qt::Vertical 10000 Out10 Qt::AlignCenter false PC2 Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 PC10 Qt::AlignCenter false IMixR Qt::AlignCenter false 0 32767 10000 1000 Qt::Vertical 10000 Device Control Phantom: false IN 1-4 IN 5-8 Insert: false CH1 CH2 Disable ADAT I/O Enable MIDI false AC&3 Passthrough Alt+3 &MIDI Thru Alt+M Use high voltage supply Qt::Vertical QSizePolicy::Expanding 20 21 &Save Settings Alt+S Identif&y Device Alt+Y Re&boot Device Alt+B Refresh qPixmapFromMimeSource btnRefresh clicked() SaffireProMixerSmallUI updateValues() 20 20 20 20 libffado-2.4.5/support/mixer-qt4/ffado/mixer/yamahago.py0000644000175000001440000001572014206145246022602 0ustar jwoitheusers# # Copyright (c) 2013 by Takashi Sakamoto # Copyright (C) 2005-2008 by Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QWidget from ffado.import_pyqt import * from math import log10 from ffado.config import * import logging log = logging.getLogger('YamahaGo') class YamahaGo(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) def getDisplayTitle(self): if self.configrom.getModelId() == 0x0010000B: return "GO44" else: return "GO46" # startup def initValues(self): uicLoad("ffado/mixer/yamahago", self) self.modelId = self.configrom.getModelId() # GO44 if self.modelId == 0x0010000B: self.setWindowTitle("Yamaha GO44 Control") self.box_ana_out.hide() self.is46 = False # GO46 elif self.modelId == 0x0010000C: self.setWindowTitle("Yamaha GO46 Control") self.box_ana_in_12_level.hide() self.is46 = True # common self.VolumeControls = { self.sld_mixer_strm_in_1: ('/Mixer/Feature_Volume_3', 1, self.sld_mixer_strm_in_2, 2, self.link_mixer_strm_in_12), self.sld_mixer_strm_in_2: ('/Mixer/Feature_Volume_3', 2, self.sld_mixer_strm_in_1, 1, self.link_mixer_strm_in_12), self.sld_mixer_strm_in_3: ('/Mixer/Feature_Volume_4', 1, self.sld_mixer_strm_in_4, 2, self.link_mixer_strm_in_34), self.sld_mixer_strm_in_4: ('/Mixer/Feature_Volume_4', 2, self.sld_mixer_strm_in_3, 1, self.link_mixer_strm_in_34), self.sld_mixer_strm_in_5: ('/Mixer/Feature_Volume_5', 1, self.sld_mixer_strm_in_6, 2, self.link_mixer_strm_in_56), self.sld_mixer_strm_in_6: ('/Mixer/Feature_Volume_5', 2, self.sld_mixer_strm_in_5, 1, self.link_mixer_strm_in_56), self.sld_mixer_ana_in_1: ('/Mixer/Feature_Volume_6', 1, self.sld_mixer_ana_in_2, 2, self.link_mixer_ana_in_12), self.sld_mixer_ana_in_2: ('/Mixer/Feature_Volume_6', 2, self.sld_mixer_ana_in_1, 1, self.link_mixer_ana_in_12), self.sld_mixer_dig_in_1: ('/Mixer/Feature_Volume_7', 1, self.sld_mixer_dig_in_2, 2, self.link_mixer_dig_in_12), self.sld_mixer_dig_in_2: ('/Mixer/Feature_Volume_7', 2, self.sld_mixer_dig_in_1, 1, self.link_mixer_dig_in_12) } self.JackSourceSelectors = { self.cmb_ana_out_12_route: '/Mixer/Selector_1', self.cmb_ana_out_34_route: '/Mixer/Selector_2', self.cmb_dig_out_12_route: '/Mixer/Selector_3' } if not self.is46: # volume for mixer output self.VolumeControls[self.sld_mixer_out_1] = ( '/Mixer/Feature_Volume_1', 1, self.sld_mixer_out_2, 2, self.link_mixer_out_12 ) self.VolumeControls[self.sld_mixer_out_2] = ( '/Mixer/Feature_Volume_1', 2, self.sld_mixer_out_1, 1, self.link_mixer_out_12 ) # analog out 3/4 is headphone out 1/2 self.label_ana_out_34_route.setText("Headphone out 1/2") else: # volume for mixer output self.VolumeControls[self.sld_mixer_out_1] = ( '/Mixer/Feature_Volume_2', 1, self.sld_mixer_out_2, 2, self.link_mixer_out_12 ) self.VolumeControls[self.sld_mixer_out_2] = ( '/Mixer/Feature_Volume_2', 2, self.sld_mixer_out_1, 1, self.link_mixer_out_12 ) # volume for analog output self.VolumeControls[self.sld_ana_out_1] = ( '/Mixer/Feature_Volume_1', 1, self.sld_ana_out_2, 2, self.link_ana_out_12 ) self.VolumeControls[self.sld_ana_out_2] = ( '/Mixer/Feature_Volume_1', 2, self.sld_ana_out_1, 1, self.link_ana_out_12 ) self.VolumeControls[self.sld_ana_out_3] = ( '/Mixer/Feature_Volume_1', 3, self.sld_ana_out_4, 4, self.link_ana_out_34 ) self.VolumeControls[self.sld_ana_out_4] = ( '/Mixer/Feature_Volume_1', 4, self.sld_ana_out_3, 3, self.link_ana_out_34 ) # gain control for ctl, params in list(self.VolumeControls.items()): path = params[0] idx = params[1] db = self.hw.getContignuous(path, idx) vol = self.db2vol(db) ctl.setValue(vol) link = params[4] pvol = self.db2vol(db) if pvol == vol: link.setChecked(True) ctl.valueChanged.connect(self.updateVolume) # source selector for jack output for ctl, param in list(self.JackSourceSelectors.items()): state = self.hw.getDiscrete(param) ctl.setCurrentIndex(state) ctl.activated.connect(self.updateSelector) if not self.is46: self.cmb_ana_in_12_level.activated.connect(self.updateMicLevel) # helper functions def vol2db(self, vol): return (log10(vol + 1) - 2) * 16384 def db2vol(self, db): return pow(10, db / 16384 + 2) - 1 def updateMicLevel(self, state): if state == 0: level = 0 elif state == 1: level = -1535 else: level = -3583 self.hw.setContignuous('/Mixer/Feature_Volume_2', level) def updateVolume(self, vol): sender = self.sender() path = self.VolumeControls[sender][0] idx = self.VolumeControls[sender][1] pair = self.VolumeControls[sender][2] link = self.VolumeControls[sender][4] db = self.vol2db(vol) self.hw.setContignuous(path, db, idx) if link.isChecked(): pair.setValue(vol) def updateSelector(self, state): sender = self.sender() path = self.JackSourceSelectors[sender] self.hw.setDiscrete(path, state) libffado-2.4.5/support/mixer-qt4/ffado/mixer/rme.ui0000644000175000001440000016311013003126231021544 0ustar jwoitheusers RmeMixerUI 0 0 635 933 0 0 RmeMixer QLayout::SetDefaultConstraint true 0 0 0 Control 0 0 QFrame::NoFrame QFrame::Raised 0 0 Input gains 0 0 Mic 1 65 1 Qt::Horizontal 0 0 Mic 2 65 Qt::Horizontal 0 0 Input 3 0 36 Qt::Horizontal 0 0 Input 4 0 36 Qt::Horizontal 0 0 Channel 3/4 options Chan 3 Instr Pad Chan 4 Pad Instr Qt::Vertical 20 0 QFrame::NoFrame QFrame::Raised Phantom Mic 1 Mic 3 Mic 2 Mic 4 Qt::Vertical 20 0 Level in Lo gain +4 dBu -10 dBV Qt::Vertical 20 0 Inputs 1 7 Front Rear Front+Rear 8 Front Rear Front+Rear Front Rear Front+Rear Qt::Vertical 20 0 Instrument options Drive Limiter Speaker emulation Qt::Vertical 20 0 0 0 QFrame::NoFrame QFrame::Raised SPDIF in ADAT2 Optical Coax Qt::Vertical QSizePolicy::MinimumExpanding 20 0 SPDIF out ADAT2 Optical Emphasis Professional Non-audio Qt::Vertical QSizePolicy::MinimumExpanding 20 0 Level out Hi gain +4 dBu -10 dBV Qt::Vertical QSizePolicy::MinimumExpanding 20 0 Phones level Hi gain +4 dBU -10 dBV Qt::Vertical QSizePolicy::MinimumExpanding 20 0 QFrame::NoFrame QFrame::Raised Clock mode Autosync Master Qt::Vertical 20 40 Pref sync ref Word clock ADAT1 in ADAT2 in SPDIF in TCO Qt::Vertical 20 0 Sync check Word No lock ADAT1 No lock ADAT2 No lock SPDIF No lock TCO No lock Qt::Vertical 20 0 Autosync ref Input [unset] Frequency [unset] Qt::Vertical 20 40 0 0 QFrame::NoFrame QFrame::Raised 0 0 SPDIF frequency 48 kHz Qt::Vertical QSizePolicy::MinimumExpanding 20 0 0 0 Word clock Single speed Qt::Vertical QSizePolicy::MinimumExpanding 20 0 0 0 System clock Mode Slave Frequency 44100 kHz Qt::Vertical QSizePolicy::MinimumExpanding 20 0 0 0 Limit bandwidth All channels Analog + SPDIF Analog 1-8 Qt::Vertical QSizePolicy::MinimumExpanding 20 0 Qt::Vertical QSizePolicy::MinimumExpanding 20 0 frame_4 frame frame_2 frame_3 igains_chan34_opts_frame true TCO LTC In 00 : 00 : 00 : 00 Qt::Horizontal 40 20 Input State LTC valid 25.00 fps Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop full frame Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Video no video WordClk none Frame Rate Frame Type Sync Source LTC Video Word Clock Status Locked Video Input 75 Ohm Term. Word Clock Conversion 1:1 44.1 kHz -> 48 kHz 48 kHz -> 44.1 kHz Qt::Vertical 20 40 Frame Rate 24 fps 25 fps 29.97 fps 29.97 dfps 30 fps 30 dfps Sample Rate false 44.1 kHz 48 kHz true 0 0 + 0.1 % - 0.1 % + 4 % - 4 % Frequency 48000 Hz Qt::Vertical 20 40 Qt::Horizontal 40 20 Input mixer Playback mixer Output levels 0 0 Device operations QLayout::SetDefaultConstraint 0 0 Flash Load control Save control Load mixer Save mixer 0 0 Mixer presets FFADO default Qt::Vertical QSizePolicy::MinimumExpanding 20 0 libffado-2.4.5/support/mixer-qt4/ffado/mixer/maudio_bebob_output.ui0000644000175000001440000000627712233355731025037 0ustar jwoitheusers MAudio_BeBoB_Output_Widget 0 0 94 250 Form 0 0 name Qt::AlignCenter 0 0 0 100 Qt::Vertical QSlider::NoTicks 50 0 0 0 100 Qt::Vertical QSlider::NoTicks 50 0 0 Mute true 0 0 Link true libffado-2.4.5/support/mixer-qt4/ffado/mixer/maudio_bebob_input.ui0000644000175000001440000001045212233355731024624 0ustar jwoitheusers MAudio_BeBoB_Input_Widget 0 0 100 253 Dialog 0 0 0 0 0 40 10 Qt::Vertical QSlider::NoTicks 50 10 name Qt::AlignCenter 0 0 0 100 Qt::Vertical QSlider::NoTicks 50 0 0 16777215 40 25.000000000000000 true 0 0 16777215 40 25.000000000000000 true 0 0 Link true 0 0 Mute true libffado-2.4.5/support/mixer-qt4/ffado/mixer/yamahago.ui0000644000175000001440000005254312233354302022564 0ustar jwoitheusers YamahaGoUI 0 0 794 355 0 0 Yamaha GO series Control 0 0 Output Routing Analog out 1/2 false Stream in 1/2 Stream in 3/4 Analog in 1/2 Digital in 1/2 Mixer out 1/2 Stream in 5/6 Analog out 3/4 false Stream in 1/2 Stream in 3/4 Analog in 1/2 Digital in 1/2 Mixer out 1/2 Stream in 5/6 Digital out 1/2 false Stream in 1/2 Stream in 3/4 Analog in 1/2 Digital in 1/2 Mixer out 1/2 Stream in 5/6 Microphone Level Analog in 1/2 false High Middle Low Qt::Horizontal 40 20 Hardware Mixer 0 99 10 10 Qt::Vertical 0 10 Qt::Vertical 0 99 10 10 Qt::Vertical 0 10 Qt::Vertical Digital in 1/2 Qt::AlignCenter 0 99 10 10 Qt::Vertical 0 10 Qt::Vertical Mixer Out 1/2 Qt::AlignCenter false Analog in 1/2 Qt::AlignCenter 0 0 0 100 Qt::LeftToRight 0 99 10 10 Qt::Vertical 0 10 Qt::Vertical link true link true link true link true link true Stream in 1/2 Qt::AlignCenter false link true Stream in 3/4 Qt::AlignCenter false 0 99 10 10 Qt::Vertical 0 10 Qt::Vertical Stream in 5/6 Qt::AlignCenter false 10 Qt::Vertical 0 80 0 99 10 10 Qt::Vertical 0 Analog Out 1/2 Qt::AlignCenter 3/4 Qt::AlignCenter 0 100 10 Qt::Vertical 10 Qt::Vertical 10 Qt::Vertical 10 Qt::Vertical link true link true qPixmapFromMimeSource libffado-2.4.5/support/mixer-qt4/ffado/mixer/profire2626_settings.ui0000644000175000001440000001634112230421702024674 0ustar jwoitheusers Profire2626Settings 0 0 763 542 Form false not implemented Peak meters Pre fader Post fader Mode Peak hold Off 1 second 3 seconds Infinite false not implemented Standalone mode Internal SPDIF Optical A Word Clock (BNC) Optical port B mode false Sample rate false Sync source Converter mode 44.1 kHz 48 kHz 88.2 kHz 96 kHz 176.4 kHz 192 kHz ADAT SPDIF A/D A/D - D/A Master volume knob Analog out 1/2 Analog out 3/4 Analog out 5/6 Analog out 7/8 <html><head/><body><p><span style=" font-weight:600;">Warning:</span> Unchecking a port will make it play at full volume and may damage your speakers and/or hearing!</p></body></html> true libffado-2.4.5/support/mixer-qt4/ffado/mixer/motu_mark3.ui0000644000175000001440000000061512222540720023047 0ustar jwoitheusers MotuMark3MixerUI 0 0 765 511 Form libffado-2.4.5/support/mixer-qt4/ffado/mixer/saffire_dice_monitoring.ui0000644000175000001440000002223011332123657025642 0ustar jwoitheusers Form 0 0 599 406 Form Qt::Vertical 0 0 0 0 0 Ch 1 Instrument 0 0 Ch 4 High Level 0 0 Ch 2 Instrument 0 0 Ch 3 High Level 0 0 Use ADAT-Out as S/PDIF 0 0 Pad Output 0 4 Volume false false 0 4 -127 0 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter dB -127 0 Dim Level: Qt::AlignCenter dimLevel 0 0 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter dB -127 0 0 10 75 true Dim true 0 10 75 true Mute true volumeKnob valueChanged(int) volumeBox setValue(int) 492 531 488 635 volumeBox valueChanged(int) volumeKnob setValue(int) 551 630 559 539 libffado-2.4.5/support/mixer-qt4/ffado/mixer/__init__.py0000644000175000001440000000000011246546617022544 0ustar jwoitheuserslibffado-2.4.5/support/mixer-qt4/ffado/mixer/nodevice.ui0000644000175000001440000000217211246546617022602 0ustar jwoitheusers NoDeviceMixerUI 0 0 611 218 Form 0 0 300 200 No supported device found. Please attach a supported device. Qt::AlignCenter true libffado-2.4.5/support/mixer-qt4/ffado/panelmanager.py0000644000175000001440000005603714206145246022330 0ustar jwoitheusers# # Copyright (C) 2005-2008 by Pieter Palmers # 2007-2008 by Arnold Krille # 2013 by Philippe Carriere # # 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 . # from ffado.config import * #FFADO_VERSION, FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH # from PyQt4.QtGui import QFrame, QWidget, QTabWidget, QVBoxLayout, QMainWindow, QIcon, QAction, qApp, QStyleOptionTabWidgetFrame, QFileDialog # from PyQt4.QtCore import QTimer, pyqtSignal from ffado.import_pyqt import * from ffado.dbus_util import * from ffado.registration import * from ffado.configuration import * from ffado.mixer.globalmixer import GlobalMixer from ffado.mixer.dummy import Dummy import sys import time import importlib import logging log = logging.getLogger('panelmanager') use_generic = False try: from mixer_generic import * log.info("The generic mixer is found, seems to be a developer using ffadomixer...") except ImportError: pass else: use_generic = True # pseudo-guid GUID_GENERIC_MIXER = 0 FILE_VERSION = '0.1' class HLine( QFrame ): def __init__( self, parent ): QFrame.__init__( self, parent ) self.setFrameShape( QFrame.HLine ) self.setLineWidth( 2 ) self.setMinimumHeight( 10 ) class PanelManagerStatus(QWidget): def __init__(self, parent): QWidget.__init__(self,parent) uicLoad("ffado/panelmanagerstatus", self) class OwnTabWidget(QTabWidget): def __init__(self,parent): QTabWidget.__init__(self,parent) def tabInserted(self,index): self.checkTabBar() def tabRemoved(self,index): self.checkTabBar() def checkTabBar(self): if self.count()<2: self.tabBar().hide() else: self.tabBar().show() class PanelManager(QWidget): connectionLost = pyqtSignal(name='connectionLost') def __init__(self, parent, devmgr=None): QWidget.__init__(self, parent) self.setObjectName("PanelManager") self.parent = parent # maps a device GUID to a QT panel self.panels = {} # a layout for ourselves self.layout = QVBoxLayout(self) # the tabs self.tabs = OwnTabWidget(self) self.tabs.hide() self.layout.addWidget(self.tabs) # a dialog that is shown during update self.status = PanelManagerStatus(self) self.layout.addWidget(self.status) self.status.show() self.devices = DeviceList( SYSTEM_CONFIG_FILE ) self.devices.updateFromFile( USER_CONFIG_FILE ) if devmgr is not None: self.setManager(devmgr) def __del__(self): print("PanelManager.__del__()") self.polltimer.stop() def setManager(self,devmgr): self.devmgr = devmgr self.devmgr.registerPreUpdateCallback(self.devlistPreUpdate) self.devmgr.registerPostUpdateCallback(self.devlistPostUpdate) self.devmgr.registerUpdateCallback(self.devlistUpdate) self.devmgr.registerDestroyedCallback(self.devmgrDestroyed) # create a timer to poll the panels self.polltimer = QTimer() self.polltimer.timeout.connect(self.pollPanels) self.polltimer.start( POLL_SLEEP_TIME_MSEC ) # create a timer to initialize the panel after the main form is shown # since initialization can take a while QTimer.singleShot( POLL_SLEEP_TIME_MSEC, self.updatePanels ) # live check timer self.alivetimer = QTimer() self.alivetimer.timeout.connect(self.commCheck) self.alivetimer.start( 2000 ) def count(self): return self.tabs.count() def pollPanels(self): #log.debug("PanelManager::pollPanels()") # only when not modifying the tabs try: if self.tabs.isEnabled(): for guid in self.panels.keys(): w = self.panels[guid] for child in w.children(): #log.debug("poll child %s,%s" % (guid,child)) if 'polledUpdate' in dir(child): child.polledUpdate() except: log.error("error in pollPanels") self.commCheck() def devlistPreUpdate(self): log.debug("devlistPreUpdate") self.tabs.setEnabled(False) self.tabs.hide() self.status.lblMessage.setText("Bus reconfiguration in progress, please wait...") self.status.show() #self.statusBar().showMessage("bus reconfiguration in progress...", 5000) def devlistPostUpdate(self): log.debug("devlistPostUpdate") # this can fail if multiple busresets happen in fast succession ntries = 10 while ntries > 0: try: self.updatePanels() return except: log.debug("devlistPostUpdate failed (%d)" % ntries) for guid in self.panels.keys(): w = self.panels[guid] del self.panels[guid] # remove from the list idx = self.tabs.indexOf(w) self.tabs.removeTab(idx) del w # GC might also take care of that ntries = ntries - 1 time.sleep(2) # sleep a few seconds log.debug("devlistPostUpdate failed completely") self.tabs.setEnabled(False) self.tabs.hide() self.status.lblMessage.setText("Error while reconfiguring. Please restart ffadomixer.") self.status.show() def devlistUpdate(self): log.debug("devlistUpdate") def devmgrDestroyed(self): log.debug("devmgrDestroyed") self.alivetimer.stop() self.tabs.setEnabled(False) self.tabs.hide() self.status.lblMessage.setText("DBUS server was shut down, please restart it and restart ffadomixer...") self.status.show() def commCheck(self): try: nbDevices = self.devmgr.getNbDevices() except: log.error("The communication with ffado-dbus-server was lost.") self.tabs.setEnabled(False) self.polltimer.stop() self.alivetimer.stop() keys = self.panels.keys() for panel in keys: w = self.panels[panel] del self.panels[panel] w.deleteLater() self.connectionLost.emit() def removePanel(self, guid): print( "Removing widget for device" + guid ) w = self.panels[guid] del self.panels[guid] # remove from the list idx = self.tabs.indexOf(w) self.tabs.removeTab(idx) w.deleteLater() self.parent.editmenu.removeAction(self.parent.devices[guid]) self.parent.devices.pop(guid, None) def addPanel(self, idx): path = self.devmgr.getDeviceName(idx) log.debug("Adding device %d: %s" % (idx, path)) if ffado.config.bypassdbus: cfgrom = ConfigRomInterface(FFADO_DBUS_SERVER, path) else: cfgrom = ConfigRomInterface(FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+'/DeviceManager/'+path) vendorId = cfgrom.getVendorId() modelId = cfgrom.getModelId() unitVersion = cfgrom.getUnitVersion() guid = cfgrom.getGUID() vendorName = cfgrom.getVendorName() modelName = cfgrom.getModelName() log.debug(" Found (%s, %X, %X) %s %s" % (str(guid), vendorId, modelId, vendorName, modelName)) # check whether this has already been registered at ffado.org if not ffado.config.bypassdbus: # As of June 2020, don't offer the registration option as the # statistics are no longer useful. Coincidently, a move to # different website infrastructure June 2020 would necessitate # some work to reimplement the interfaces used by the # registration process, which doesn't seem worthwhile. # # reg = ffado_registration(FFADO_VERSION, int(guid, 16), # vendorId, modelId, # vendorName, modelName) # reg.check_for_registration() # The MOTU devices use unitVersion to differentiate models. For # the moment though we don't need to know precisely which model # we're using beyond it being a pre-mark3 (modelId=0) or mark3 # (modelId=1) device. if vendorId == 0x1f2: # All MOTU devices with a unit version of 0x15 or greater are # mark3 devices if (unitVersion >= 0x15): modelId = 0x00000001 else: modelId = 0x00000000 # The RME devices use unitVersion to differentiate models. # Therefore in the configuration file we use the config file's # modelid field to store the unit version. As a result we must # override the modelId with the unit version here so the correct # configuration file entry (and hense mixer widget) is identified. if vendorId == 0xa35: modelId = unitVersion; dev = self.devices.getDeviceById( vendorId, modelId ) w = QWidget( ) l = QVBoxLayout( w ) # create a control object hw = ControlInterface(FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+'/DeviceManager/'+path) clockselect = ClockSelectInterface( FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+"/DeviceManager/"+path ) samplerateselect = SamplerateSelectInterface( FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+"/DeviceManager/"+path ) streamingstatus = StreamingStatusInterface( FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+"/DeviceManager/"+path ) nickname = TextInterface( FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+"/DeviceManager/"+path+"/Generic/Nickname" ) # # Generic elements for all mixers follow here: # globalmixer = GlobalMixer( w ) globalmixer.configrom = cfgrom globalmixer.clockselect = clockselect globalmixer.samplerateselect = samplerateselect globalmixer.streamingstatus = streamingstatus globalmixer.nickname = nickname globalmixer.hw = hw globalmixer.initValues() l.addWidget( globalmixer, 1 ) # # Line to separate # l.addWidget( HLine( w ) ) # # Specific (or dummy) mixer widgets get loaded in the following # found = False if 'mixer' in dev and dev['mixer'] != None: mixerapp = dev['mixer'] try: mixer_module = importlib.import_module("ffado.mixer.%s" % mixerapp.lower()) mixerwidget = getattr(mixer_module, mixerapp)(w) found = True except ImportError: log.debug("bypassdbus set, %s module not available: ignored" % mixerapp.lower()) if not found: mixerwidget = Dummy( w ) mixerapp = modelName+" (Dummy)" # # The same for all mixers # l.addWidget( mixerwidget, 10 ) mixerwidget.configrom = cfgrom mixerwidget.clockselect = clockselect mixerwidget.samplerateselect = samplerateselect mixerwidget.streamingstatus = streamingstatus mixerwidget.nickname = nickname mixerwidget.hw = hw if 'buildMixer' in dir(mixerwidget): mixerwidget.buildMixer() if 'initValues' in dir(mixerwidget): mixerwidget.initValues() if 'getDisplayTitle' in dir(mixerwidget): title = mixerwidget.getDisplayTitle() else: title = mixerapp mixer_icon = UIDIR + '/ffado/mixer/' + vendorName.replace(" ", "_").lower() + '.png' if os.path.exists(mixer_icon) : globalmixer.lblName.setPixmap(QPixmap(mixer_icon)) globalmixer.lblName.setToolTip(title) globalmixer.lblName.show() else : globalmixer.setName(title) self.tabs.addTab( w, title ) self.panels[guid] = w if 'onSamplerateChange' in dir(mixerwidget): log.debug("Updating Mixer on samplerate change required") globalmixer.onSamplerateChange = mixerwidget.onSamplerateChange w.gmixSaveSetgs = globalmixer.saveSettings w.gmixReadSetgs = globalmixer.readSettings if 'saveSettings' in dir(mixerwidget): w.smixSaveSetgs = mixerwidget.saveSettings self.parent.saveaction.setEnabled(True) if 'readSettings' in dir(mixerwidget): w.smixReadSetgs = mixerwidget.readSettings self.parent.openaction.setEnabled(True) self.parent.devices[guid] = QAction(QIcon.fromTheme("audio-card"), str(title), self.parent) # Ensure a standard type is passed to setDate() so a UserType is not # adopted by the QVariant which underpins the QAction data. "guid" # is a dbus.String object; it will be stored as a UserType if passed # directly, which makes it difficult to extract in setTabVisible(). self.parent.devices[guid].setData(str(guid)) self.parent.editmenu.addAction(self.parent.devices[guid]) self.parent.devices[guid].triggered.connect(self.setTabVisible) def setTabVisible(self) : action = self.sender() # Extract the action data and store as a dbus.String type so # it is usable as a key into self.panels[]. panel_key = dbus.String(action.data().toString() if ffado_pyqt_version == 4 else action.data()) self.tabs.setCurrentIndex(self.tabs.indexOf(self.panels[panel_key])) def displayPanels(self): # if there is no panel, add the no-device message # else make sure it is not present if self.count() == 0: self.tabs.hide() self.tabs.setEnabled(False) self.status.lblMessage.setText("No supported device found.") self.status.show() #self.statusBar().showMessage("No supported device found.", 5000) else: # Hide the status widget before showing the panel tab to prevent # the panel manager's vertical size including that of the status # widget. For some reason, hiding the status after showing the # tabs does not cause a recalculation of the panel manager's size, # and the window ends up being larger than it needs to be. self.status.hide() self.tabs.show() self.tabs.setEnabled(True) #self.statusBar().showMessage("Configured the mixer for %i devices." % self.tabs.count()) if use_generic: # # Show the generic (development) mixer if it is available # w = GenericMixer( devmgr.bus, FFADO_DBUS_SERVER, mw ) self.tabs.addTab( w, "Generic Mixer" ) self.panels[GUID_GENERIC_MIXER] = w def updatePanels(self): log.debug("PanelManager::updatePanels()") nbDevices = self.devmgr.getNbDevices() #self.statusBar().showMessage("Reconfiguring the mixer panels...") # list of panels present guids_with_tabs = self.panels.keys() # build list of guids on the bus now guids_present = [] guid_indexes = {} for idx in range(nbDevices): path = self.devmgr.getDeviceName(idx) if ffado.config.bypassdbus: cfgrom = ConfigRomInterface(FFADO_DBUS_SERVER, path) else: cfgrom = ConfigRomInterface(FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+'/DeviceManager/'+path) guid = cfgrom.getGUID() guids_present.append(guid) guid_indexes[guid] = idx # figure out what to remove # the special panel (generic) # that has (pseudo-)GUID 0 # is also automatically removed to_remove = [] for guid in guids_with_tabs: if not guid in guids_present: to_remove.append(guid) log.debug("going to remove %s" % str(guid)) else: log.debug("going to keep %s" % str(guid)) # figure out what to add to_add = [] for guid in guids_present: if not guid in guids_with_tabs: to_add.append(guid) log.debug("going to add %s" % str(guid)) # update the widget for guid in to_remove: self.removePanel(guid) for guid in to_add: # retrieve the device manager index idx = guid_indexes[guid] self.addPanel(idx) self.displayPanels() def refreshPanels(self): log.debug("PanelManager::refreshPanels()") nbDevices = self.devmgr.getNbDevices() #self.statusBar().showMessage("Reconfiguring the mixer panels...") # list of panels present guids_with_tabs = self.panels.keys() # build list of guids on the bus now guid_indexes = {} for idx in range(nbDevices): path = self.devmgr.getDeviceName(idx) cfgrom = ConfigRomInterface(FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+'/DeviceManager/'+path) guid = cfgrom.getGUID() guid_indexes[guid] = idx # remove/create the widget for guid in guids_with_tabs: self.removePanel(guid) idx = guid_indexes[guid] self.addPanel(idx) self.displayPanels() def saveSettings(self): saveString = [] saveString.append('\n') saveString.append('\n') saveString.append(' \n') saveString.append(' ' + str(FILE_VERSION).split('.')[0] + '\n') saveString.append(' \n') saveString.append(' \n') saveString.append(' ' + str(FILE_VERSION).split('.')[1] + '\n') saveString.append(' \n') saveString.append('\n') saveString.append('\n') saveString.append(' \n') saveString.append(' ' + str(str(FFADO_VERSION).split('-')[0]).split('.')[0] + '\n') saveString.append(' \n') saveString.append(' \n') saveString.append(' ' + str(str(FFADO_VERSION).split('-')[0]).split('.')[1] + '\n') saveString.append(' \n') saveString.append('\n') for guid in self.panels.keys(): saveString.append('\n') saveString.append(' \n') saveString.append(' ' + str(guid) + '\n') saveString.append(' \n') w = self.panels[guid] indent = " " saveString.extend(w.gmixSaveSetgs(indent)) if 'smixSaveSetgs' in dir(w): saveString.extend(w.smixSaveSetgs(indent)) saveString.append('\n') # file saving savefilename = QFileDialog.getSaveFileName(self, 'Save File', os.getenv('HOME')) if isinstance(savefilename, tuple): # newer PyQt5 savefilename = savefilename[0] try: f = open(savefilename, 'w') except IOError: print( "Unable to open save file" ) return for s in saveString: f.write(s) f.close() def readSettings(self): readfilename = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME')) if isinstance(readfilename, tuple): # newer PyQt5 readfilename = readfilename[0] try: f = open(readfilename, 'r') except IOError: print( "Unable to open file" ) return log.debug("Opening file %s" % readfilename) # discard useless whitespace characters readString = [] for line in f: readString.append(" ".join(str(line).split())) f.close() # Check it is a compatible "FFADO" file # It must start with the ') except Exception: print( "Data file should contain the version tag" ) return if readString[idx+1].find("") == -1: print( "Incompatible versioning of the file" ) if readString[idx+3].find("") == -1: print( "Not a valid xml file" ) if readString[idx+4].find("") == -1: print( "Incompatible versioning of the file" ) if readString[idx+6].find("") == -1: print( "Not a valid xml file" ) version_major = readString[idx+2] version = version_major + '.' + readString[idx+5] log.debug("File version: %s" % version) # File version newer than present if int(version_major) > int(str(FILE_VERSION).split('.')[0]): print( "File version is too recent: you should upgrade your FFADO installation" ) return # FIXME At a time it will be necessary to detect if an older major version is detected # # It looks like useless to check for the FFADO version # Add something here if you would like so # # Now search for devices nd = readString.count(''); n = readString.count(''); if n != nd: print( "Not a regular xml file: opening device tag must match closing ones" ) return while nd > 0: idxb = readString.index('') idxe = readString.index('') if idxe < idxb+1: print( "Not a regular xml file: data must be enclosed between a and tag" ) return stringDev = [] for s in readString[idxb:idxe]: stringDev.append(s) # determine the device guid try: idx = stringDev.index('') except Exception: print( "Device guid not found" ) return guid = stringDev[idx+1] log.debug("Device %s found" % guid) if guid in self.panels: w = self.panels[guid] w.gmixReadSetgs(stringDev) if 'smixReadSetgs' in dir(w): w.smixReadSetgs(stringDev) log.debug("Settings changed for device %s" % guid) else: log.debug("Device %s not present; settings ignored" % guid) del stringDev[:] del readString[idxb:idxe] nd -= 1 # vim: et libffado-2.4.5/support/mixer-qt4/ffado/regdialog.py0000644000175000001440000000772114206145246021627 0ustar jwoitheusers# -*- coding: utf-8 -*- # # Copyright (C) 2005-2008 by Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4.QtGui import QDialog from ffado.import_pyqt import * from ffado.config import * import logging log = logging.getLogger('registration') REGISTRATION_MESSAGE = """ \n

You are running this version of FFADO for the first time with this device.

If FFADO is useful to you, please consider a donation through http://www.ffado.org/?q=donations.

In order to collect usage statistics we would like to send some information about your system to ffado.org. It is very important for us to have good usage statistics. This is to convince vendors that Linux users do exist and is where you as a user can help the project.

The information collected is intended only for usage monitoring. The email address is optional, and will be used for FFADO related announcements only. If you provide one, please provide a valid one.

Note: This registration can also be performed on-line at http://www.ffado.org/?q=usage.

""" class ffadoRegDialog(QDialog): def __init__(self, vendor_name, vendor_id, model_name, model_id, guid, version, email="(optional)", parent = None): QDialog.__init__(self,parent) uicLoad("ffado/regdialog", self) self.txtVendorName.setText(vendor_name) self.txtVendorId.setText(vendor_id) self.txtModelName.setText(model_name) self.txtModelId.setText(model_id) self.txtGUID.setText(guid) self.txtVersion.setText(version) self.txtEmail.setText(email) self.txtMessage.setHtml(REGISTRATION_MESSAGE) self.choice = "nosend" self.btnSend.clicked.connect(self.buttonPressed) self.btnNoSend.clicked.connect(self.buttonPressed) self.btnNeverSend.clicked.connect(self.buttonPressed) def buttonPressed(self): sender = self.sender() if sender == self.btnSend: log.debug("user chose to send") self.choice = "send" elif sender == self.btnNoSend: log.debug("user chose not to send") self.choice = "nosend" elif sender == self.btnNeverSend: log.debug("user chose to never send") self.choice = "neversend" self.close() def getEmail(self): return self.txtEmail.text() # vim: et libffado-2.4.5/support/mixer-qt4/ffado/registration.py0000644000175000001440000002020614206145246022375 0ustar jwoitheusers# # Copyright (C) 2008-2009 by Pieter Palmers # 2009 by Arnold Krille # # 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 urllib import os # Python3 renamed ConfigParser to configparser. Deal with this in a way # which maintains compatibility with python2. try: from configparser import SafeConfigParser except: from ConfigParser import SafeConfigParser # The urlopen()/urlencode() functions from urllib in python2 are in # urllib.request and urllib.parse respectively under python2. try: import urllib.request, urllib.parse url_newapi = 1 except ImportError: import urllib url_newapi = 0 from sys import version_info from ffado.config import REGISTER_URL, INI_FILE_PATH, FFADO_CONFIG_DIR # from PyQt4.QtGui import QMessageBox # from PyQt4.QtCore import QByteArray from ffado.import_pyqt import * from ffado.regdialog import * import logging log = logging.getLogger('registration') class ffado_registration: def __init__(self, ffado_version, guid, vendor_id, model_id, vendor_string, model_string): # only use the section before the SVN mark # we don't need to keep track of all SVN version changes self.ffado_version = ffado_version.split('-')[0] self.guid = guid self.vendor_id = vendor_id self.model_id = model_id self.vendor_string = vendor_string self.model_string = model_string #check if config file path exists, if not, create it config_path = os.path.expanduser(FFADO_CONFIG_DIR) if not os.path.exists(config_path): os.makedirs(config_path) # parse the ini file self.config_filename = os.path.expanduser(INI_FILE_PATH) self.parser = SafeConfigParser() self.parser.read(self.config_filename) self.section_name = "%s:%X" % (self.ffado_version, self.guid) self.email = "(optional)" if self.parser.has_section("history") \ and self.parser.has_option("history", "email"): self.email = self.parser.get("history", "email") def register_ffado_usage(self): post_vals = {} post_vals['guid'] = self.guid post_vals['vendor_id'] = self.vendor_id post_vals['model_id'] = self.model_id post_vals['vendor_string'] = self.vendor_string post_vals['model_string'] = self.model_string post_vals['ffado_version'] = self.ffado_version post_vals['email'] = self.email try: if url_newapi == 1: response = urllib.request.urlopen(REGISTER_URL, urllib.parse.urlencode(post_vals).encode('ascii')) else: response = urllib.urlopen(REGISTER_URL, urllib.urlencode(post_vals)) except: log.error("failed, network error") return (-1, "Network Error") lines = response.readlines() ok = False errline = "Bad response from server" for i in range(len(lines)): if lines[i][0:10] == b"RESULT: OK": ok = True elif lines[i][0:12] == b"RESULT: FAIL": ok = False if len(lines)>i+1: errline = lines[i+1] if not ok: log.info("registration failed %s" % errline) return (-2, errline) else: return (0, "") def check_for(self, what): if not self.parser.has_section(self.section_name): return False if not self.parser.has_option(self.section_name, what): return False return self.parser.getboolean(self.section_name, what) def check_if_already_registered(self): return self.check_for("registered") def check_for_ignore(self): return self.check_for("ignore") def mark(self, what, value): if not self.parser.has_section(self.section_name): self.parser.add_section(self.section_name) self.parser.set(self.section_name, what, str(value)) def mark_version_registered(self): self.mark("registered", True) def mark_ignore_version(self): self.mark("ignore", True) def remember_email(self, email): if not self.parser.has_section("history"): self.parser.add_section("history") self.parser.set("history", "email", str(email)) def check_for_registration(self): if self.check_for_ignore(): log.debug("user requested to ignore registration") else: if self.check_if_already_registered(): log.debug("version/GUID combo already registered") else: log.debug("show dialog...") dlg = ffadoRegDialog(self.vendor_string, "0x%X" % self.vendor_id, self.model_string, "0x%X" % self.model_id, "0x%016X" % self.guid, self.ffado_version, self.email) dlg.exec_() if dlg.choice == "neversend": self.mark_ignore_version() elif dlg.choice == "send": if version_info[0] < 3: # Python 2.x asciiData = dlg.getEmail().toAscii() self.email = asciiData.data() else: # Python 3 and above self.email = ascii(dlg.getEmail()) self.remember_email(self.email) retval = self.register_ffado_usage() msg = QMessageBox() if retval[0] == 0: log.debug("registration successful") devinfomsg = "

Device: %s %s
Vendor/Model Id: %X/%X
Device GUID: %016X
FFADO Version: %s
E-Mail: %s

" % \ (self.vendor_string, self.model_string, self.vendor_id, self.model_id, self.guid, self.ffado_version, self.email) tmp = msg.question( msg, "Registration Successful", "Thank you." + "

The registration of the following information was successful:

" + devinfomsg + "

For this device you won't be asked to register again until you upgrade to a new version of FFADO.

", QMessageBox.Ok ) self.mark_version_registered() else: log.error("error: " + retval[1]) tmp = msg.question( msg, "Registration Failed", "The registration at ffado.org failed." + "

Error message:

" + retval[1] + "

Try again next time?

", QMessageBox.Yes, QMessageBox.No ) if tmp == 4: self.mark_ignore_version() elif dlg.choice == "nosend": pass # write the updated config f = open(self.config_filename, "w+") self.parser.write(f) f.close() # vim: et libffado-2.4.5/support/mixer-qt4/ffado/widgets/0000755000175000001440000000000014206145613020755 5ustar jwoitheuserslibffado-2.4.5/support/mixer-qt4/ffado/widgets/crossbarrouter.py0000644000175000001440000002341414206145246024414 0ustar jwoitheusers# # Copyright (C) 2009 by Arnold Krille # 2013 by Philippe Carriere # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4 import QtGui, QtCore # from PyQt4.QtCore import pyqtSignal # from PyQt4.QtGui import QFrame, QPainter, QGridLayout, QLabel, QComboBox # from PyQt4.QtGui import QWidget, QVBoxLayout, QHBoxLayout, QPushButton from ffado.import_pyqt import * import dbus, math import ffado.config import logging log = logging.getLogger("crossbarrouter") class VuMeter(QFrame): def __init__(self, interface, output, input=None, parent=None): QFrame.__init__(self, parent) self.setLineWidth(1) self.setFrameStyle(QFrame.Panel|QFrame.Sunken) self.setMinimumSize(20, 20) self.level = 0 self.interface = interface self.output = output def updateLevel(self, value): self.level = value self.update() def paintEvent(self, event): p = QPainter(self) value = self.level/4096 r = self.rect() r.setHeight(r.height() * math.sqrt(value)) r.moveBottom(self.rect().height()) p.fillRect(r, self.palette().highlight()) class OutputSwitcher(QFrame): """ The name is a bit misleading. This widget selectes sources for a specified destination. In mixer-usage this widget is at the top of the input-channel. Because the input of the mixer is an available output from the routers point. """ MixerRoutingChanged = pyqtSignal() def __init__(self, interface, outname, parent): QFrame.__init__(self, parent) self.interface = interface self.outname = outname self.lastin = "" self.setLineWidth(1) self.setFrameStyle(QFrame.Sunken|QFrame.Panel) self.layout = QGridLayout(self) self.setLayout(self.layout) self.lbl = QLabel(self.outname, self) self.lbl.setToolTip("The name of the destination that is to be controlled here.") self.layout.addWidget(self.lbl, 0, 0) self.vu = VuMeter(self.interface, outname, parent=self) self.layout.addWidget(self.vu, 0, 1) sources = self.interface.getSourceNames() self.combo = QComboBox(self) self.combo.setToolTip("Select the source for this destination.
Each destination can only receive sound from one source at a time. But one source can send sound to multiple destinations.
") self.layout.addWidget(self.combo, 1, 0, 1, 2) self.combo.addItem("Disconnected") self.combo.addItems(sources) src = self.interface.getSourceForDestination(self.outname) self.lastin = str(src) if src != "": self.combo.setCurrentIndex(self.combo.findText(src)) else: self.combo.setCurrentIndex(0) self.combo.activated[str].connect(self.comboCurrentChanged) def peakValue(self, value): self.vu.updateLevel(value) pass def comboCurrentChanged(self, inname): #log.debug("comboCurrentChanged( %s )" % inname) if inname == self.lastin: return if self.lastin != "": self.interface.setConnectionState(self.lastin, self.outname, False) if inname != "Disconnected": if self.interface.setConnectionState(str(inname), self.outname, True): if self.outname[:5] == "Mixer" or self.lastin[:5] == "Mixer" or str(inname)[:5] == "Mixer": self.MixerRoutingChanged.emit() self.lastin = str(inname) else: log.warning(" Failed to connect %s to %s" % (inname, self.outname)) else: self.lastin = "" class CrossbarRouter(QWidget): MixerRoutingChanged = pyqtSignal(name='MixerRoutingChanged') def __init__(self, servername, basepath, parent=None): QWidget.__init__(self, parent); if not ffado.config.bypassdbus: self.bus = dbus.SessionBus() self.dev = self.bus.get_object(servername, basepath) self.interface = dbus.Interface(self.dev, dbus_interface="org.ffado.Control.Element.CrossbarRouter") self.destinations = self.interface.getDestinationNames() else: self.destinations = [] self.settings = QtCore.QSettings(self) self.outgroups = [] for ch in self.destinations: tmp = str(ch).split(":")[0] if not tmp in self.outgroups: self.outgroups.append(tmp) self.biglayout = QVBoxLayout(self) self.setLayout(self.biglayout) self.toplayout = QHBoxLayout() self.biglayout.addLayout(self.toplayout) self.vubtn = QPushButton("Switch peak meters", self) self.vubtn.setCheckable(True) self.vubtn.toggled.connect(self.runVu) self.toplayout.addWidget(self.vubtn) self.layout = QGridLayout() self.biglayout.addLayout(self.layout) self.switchers = {} for out in self.destinations: btn = OutputSwitcher(self.interface, out, self) self.layout.addWidget(btn, int(out.split(":")[-1]) + 1, self.outgroups.index(out.split(":")[0])) self.switchers[out] = btn self.switchers[out].MixerRoutingChanged.connect(self.updateMixerRouting) self.timer = QtCore.QTimer(self) self.timer.setInterval(200) self.timer.timeout.connect(self.updateLevels) if ffado_pyqt_version == 4: self.vubtn.setChecked(self.settings.value("crossbarrouter/runvu", False).toBool()) else: self.vubtn.setChecked(self.settings.value("crossbarrouter/runvu", False) == u'true') def __del__(self): print( "CrossbarRouter.__del__()" ) self.settings.setValue("crossbarrouter/runvu", self.vubtn.isChecked()) def runVu(self, run=True): #log.debug("CrossbarRouter.runVu( %i )" % run) if run: self.timer.start() else: self.timer.stop() for sw in self.switchers: self.switchers[sw].peakValue(0) def updateLevels(self): #log.debug("CrossbarRouter.updateLevels()") if ffado.config.bypassdbus: return peakvalues = self.interface.getPeakValues() #log.debug("Got %i peaks" % len(peakvalues)) for peak in peakvalues: #log.debug("peak = [%s,%s]" % (str(peak[0]),str(peak[1]))) if peak[1] >= 0: self.switchers[peak[0]].peakValue(peak[1]) def updateMixerRouting(self): self.MixerRoutingChanged.emit() def saveSettings(self, indent): routerSaveString = [] routerSaveString.append('%s\n' % indent) routerSaveString.append('%s %d\n' % (indent, len(self.destinations))) routerSaveString.append('%s\n' % indent) routerSaveString.append('%s\n' % indent) routerSaveString.append('%s 2\n' % indent) routerSaveString.append('%s\n' % indent) routerSaveString.append('%s\n' % indent) for out in self.destinations: routerSaveString.append('%s ' % indent + out + ' ') routerSaveString.append(self.interface.getSourceForDestination(out) + '\n') routerSaveString.append('%s\n' % indent) return routerSaveString def readSettings(self, routerReadString): sources = str(self.interface.getSourceNames()) if routerReadString[0].find('') == -1: log.debug("Number of router destinations must be specified\n") return False if routerReadString[2].find('') == -1: log.debug("Incompatible xml file\n") return False n_dest = int(routerReadString[1]) if n_dest != len(self.destinations): log.debug("Caution: numbers of destinations mismatch") if routerReadString[3].find('') == -1: log.debug("Number of sources per destinations must be specified\n") return False if routerReadString[5].find('') == -1: log.debug("Incompatible xml file\n") return False n_spd = int(routerReadString[4]) if n_spd != 2: log.debug("Unable to handle more than one source for each destination;") try: idxb = routerReadString.index('') idxe = routerReadString.index('') except Exception: log.debug("Router connections not specified\n") idxb = -1 idxe = -1 return False if idxb >= 0: if idxe > idxb + 1: for s in routerReadString[idxb+1:idxe]: destination = s.split()[0] if str(self.destinations).find(destination) != -1: source = s.split()[1] if sources.find(source) != -1: idx = self.switchers[destination].combo.findText(source) self.switchers[destination].combo.setCurrentIndex(idx) self.switchers[destination].comboCurrentChanged(source) return True # # vim: sw=4 ts=4 et libffado-2.4.5/support/mixer-qt4/ffado/widgets/matrixmixer.py0000644000175000001440000016673214206145246023721 0ustar jwoitheusers# coding=utf8 # # Copyright (C) 2009 by Arnold Krille # 2013 by Philippe Carriere # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # from PyQt4 import QtGui, QtCore, Qt # from PyQt4.QtCore import pyqtSignal # from PyQt4.QtGui import QColor, QAbstractSlider, QDoubleSpinBox, QWidgetAction # from PyQt4.QtGui import QAction, QPainter, QWidget, QGridLayout, QLabel # from PyQt4.QtGui import QLayout, QSlider, QLineEdit, QPalette # from PyQt4.QtGui import QVBoxLayout, QHBoxLayout, QTabWidget, QToolBar # from PyQt4.QtGui import QComboBox, QScrollArea, QPushButton, QSizePolicy from ffado.import_pyqt import * import dbus, math, decimal import ffado.config import logging log = logging.getLogger("matrixmixer") def toDBvalue(value): n = int(value) c2p14 = 16384.0 if n > 164: return round(20.0*math.log10(float(n)/c2p14), 2) else: return -40.0 def fromDBvalue(value): v = float(value) c2p14 = 16384.0 if (v > -40): return int(round(math.pow(10.0, (value/20.0))*c2p14, 0)) else: return 0 # v, vl, vr in linear scale # b range in [-1:1] def getVolumeLeft(v, b): return int(round(0.5*v*(1.0-b),0)) def getVolumeRight(v, b): return v-int(round(0.5*v*(1.0-b),0)) def getStereoVolume(vl, vr): return int(round(vl+vr,0)) def getStereoBalance(vl, vr): if ((vl+vr) == 0): return 0 else: return round(float(vr-vl)/float(vr+vl),2) class ColorForNumber: def __init__(self): self.colors = dict() def addColor(self, n, color): self.colors[n] = color def getColor(self, n): #print( "ColorForNumber.getColor( %g )" % (n) ) keys = sorted(self.colors.keys()) low = keys[-1] high = keys[-1] for i in range(len(keys)-1): if keys[i] <= n and keys[i+1] > n: low = keys[i] high = keys[i+1] #print( "%g is between %g and %g" % (n, low, high) ) f = 0 if high != low: f = (n-low) / (high-low) lc = self.colors[low] hc = self.colors[high] return QColor( int((1-f)*lc.red() + f*hc.red()), int((1-f)*lc.green() + f*hc.green()), int((1-f)*lc.blue() + f*hc.blue()) ) class BckgrdColorForNumber(ColorForNumber): def __init__(self): ColorForNumber.__init__(self) self.addColor( 0.0, QColor( 0, 0, 0)) self.addColor( 1.0, QColor( 0, 0, 128)) self.addColor( math.pow(2,6), QColor( 0, 255, 0)) self.addColor( math.pow(2,14), QColor(255, 255, 0)) self.addColor(math.pow(2,16)-1, QColor(255, 0, 0)) def getFrgdColor(self, color): if color.valueF() < 0.6: return QColor(255, 255, 255) else: return QColor(0, 0, 0) class MixerNode(QAbstractSlider): nodeValueChanged = pyqtSignal(tuple) def __init__(self, input, output, value, max, muted, inverted, parent, matrix_obj): QAbstractSlider.__init__(self, parent) #log.debug("MixerNode.__init__( %i, %i, %i, %i, %s )" % (input, output, value, max, str(parent)) ) # Store a direct link back to the underlying matrix object so the mute # and invert interfaces can be easily found. By the time the matrix # has been set into the full widget hierarchy, its parent is unlikely # to still be the top-level matrix object. self.matrix_obj = matrix_obj; self.pos = QtCore.QPointF(0, 0) self.input = input self.output = output self.setOrientation(Qt.Vertical) if max == -1: max = pow(2, 16)-1 self.setRange(0, max) self.setValue(int(value)) self.valueChanged.connect(self.internalValueChanged) self.setSmall(False) self.bgcolors = BckgrdColorForNumber() self.setContextMenuPolicy(Qt.ActionsContextMenu) self.mapper = QtCore.QSignalMapper(self) self.mapper.mapped['QString'].connect(self.directValues) self.spinbox = QDoubleSpinBox(self) self.spinbox.setRange(-40, 12) self.spinbox.setSuffix(" dB") if value != 0: self.spinbox.setValue(toDBvalue(value)) self.spinbox.valueChanged.connect(self.directValues) action = QWidgetAction(self) action.setDefaultWidget(self.spinbox) self.addAction(action) for text in ["3 dB", "0 dB", "-3 dB", "-20 dB", "-inf dB"]: action = QAction(text, self) action.triggered.connect(self.mapper.map) self.mapper.setMapping(action, text) self.addAction(action) # Only show the mute menu item if a value has been supplied self.mute_action = None if (muted != None): action = QAction(text, self) action.setSeparator(True) self.addAction(action) self.mute_action = QAction("Mute", self) self.mute_action.setCheckable(True) self.mute_action.setChecked(muted) self.mute_action.triggered.connect(self.mapper.map) self.mapper.setMapping(self.mute_action, "Mute") self.addAction(self.mute_action) # Similarly, only show a phase inversion menu item if in use self.inv_action = None if (inverted != None): if (muted == None): action = QAction(text, self) action.setSeparator(True) self.addAction(action) self.inv_action = QAction("Invert", self) self.inv_action.setCheckable(True) self.inv_action.setChecked(inverted) self.inv_action.triggered.connect(self.mapper.map) self.mapper.setMapping(self.inv_action, "Invert") self.addAction(self.inv_action) def directValues(self,text): #log.debug("MixerNode.directValues( '%s' )" % text) if text == "Mute": #log.debug("Mute %d" % self.mute_action.isChecked()) self.update() self.matrix_obj.mutes_interface.setValue(self.output, self.input, self.mute_action.isChecked()) elif text == "Invert": #log.debug("Invert %d" % self.inv_action.isChecked()) self.update() self.matrix_obj.inverts_interface.setValue(self.output, self.input, self.inv_action.isChecked()) else: text = str(text).split(" ")[0].replace(",",".") n = fromDBvalue(float(text)) #log.debug(" linear value: %g" % n) self.setValue(n) def mousePressEvent(self, ev): if ev.buttons() & Qt.LeftButton: self.pos = ev.posF() if ffado_pyqt_version == 4 else ev.localPos() self.tmpvalue = self.value() ev.accept() #log.debug("MixerNode.mousePressEvent() %s" % str(self.pos)) def mouseMoveEvent(self, ev): if hasattr(self, "tmpvalue") and self.pos is not QtCore.QPointF(0, 0): newpos = ev.posF() if ffado_pyqt_version == 4 else ev.localPos() change = newpos.y() - self.pos.y() #log.debug("MixerNode.mouseReleaseEvent() change %s" % (str(change))) self.setValue( self.tmpvalue - math.copysign(pow(abs(change), 2), change) ) ev.accept() def mouseReleaseEvent(self, ev): if hasattr(self, "tmpvalue") and self.pos is not QtCore.QPointF(0, 0): newpos = ev.posF() if ffado_pyqt_version == 4 else ev.localPos() change = newpos.y() - self.pos.y() #log.debug("MixerNode.mouseReleaseEvent() change %s" % (str(change))) self.setValue( self.tmpvalue - math.copysign(pow(abs(change), 2), change) ) self.pos = QtCore.QPointF(0, 0) del self.tmpvalue ev.accept() # Wheel event is mainly for scrolling inside the mixer window # Additionnaly press Control key for wheel controling the values def wheelEvent (self, ev): if (ev.modifiers() & Qt.ControlModifier): tmpvalue = self.value() change = ev.delta()/8 self.setValue( tmpvalue + math.copysign(pow(abs(change), 2), change) ) ev.accept() else: ev.ignore() def paintEvent(self, ev): p = QPainter(self) rect = self.rect() v = self.value() if (self.mute_action!=None and self.mute_action.isChecked()): color = QColor(64, 64, 64) else: color = self.bgcolors.getColor(v) p.fillRect(rect, color) if self.small: return p.setPen(self.bgcolors.getFrgdColor(color)) lv=decimal.Decimal('-Infinity') if v != 0: lv = toDBvalue(v) #log.debug("new value is %g dB" % lv) text = "%.2g dB" % lv if v == 0: symb_inf = u"\u221E" text = "-" + symb_inf + " dB" if ffado_python3 or ffado_pyqt_version == 5: # Python3 uses native python UTF strings rather than QString. # This therefore appears to be the correct way to display this # UTF8 string, but testing may prove otherwise. p.drawText(rect, Qt.AlignCenter, text) else: p.drawText(rect, Qt.AlignCenter, QString.fromUtf8(text)) if (self.inv_action!=None and self.inv_action.isChecked()): if ffado_python3 or ffado_pyqt_version == 5: # Refer to the comment about about Python UTF8 strings. p.drawText(rect, Qt.AlignLeft|Qt.AlignTop, " ϕ") else: p.drawText(rect, Qt.AlignLeft|Qt.AlignTop, QString.fromUtf8(" ϕ")) def internalValueChanged(self, value): #log.debug("MixerNode.internalValueChanged( %i )" % value) if value != 0: dB = toDBvalue(value) if self.spinbox.value() is not dB: self.spinbox.setValue(dB) self.nodeValueChanged.emit((self.input, self.output, value)) self.update() def setSmall(self, small): self.small = small if small: self.setMinimumSize(10, 10) else: fontmetrics = self.fontMetrics() self.setMinimumSize(fontmetrics.boundingRect("-0.0 dB").size()*1.1) self.update() class MixerChannel(QWidget): hide = pyqtSignal(int, bool, name='hide') def __init__(self, number, parent=None, name="", smallFont=False): QWidget.__init__(self, parent) layout = QGridLayout(self) self.number = number self.name = name self.lbl = QLabel(self) self.lbl.setAlignment(Qt.AlignCenter) if (smallFont): font = self.lbl.font() font.setPointSize(int(font.pointSize()/1.5 + 0.5)) self.lbl.setFont(font) layout.addWidget(self.lbl, 0, 0, 1, 2) self.hideChannel(False) self.setContextMenuPolicy(Qt.ActionsContextMenu) action = QAction("Make this channel small", self) action.setCheckable(True) action.triggered.connect(self.hideChannel) self.addAction(action) def hideChannel(self, hide): if hide: self.lbl.setText("%i" % (self.number+1)); else: self.lbl.setText(self.name) self.hide.emit(self.number, hide) self.update() # Matrix view widget class MatrixControlView(QWidget): valueChanged = pyqtSignal([tuple]) def __init__(self, servername, basepath, parent=None, sliderMaxValue=-1, mutespath=None, invertspath=None, smallFont=False, shortname=False, shortcolname="Ch", shortrowname="Ch", transpose=False): QWidget.__init__(self, parent) if not ffado.config.bypassdbus: self.bus = dbus.SessionBus() self.dev = self.bus.get_object(servername, basepath) self.interface = dbus.Interface(self.dev, dbus_interface="org.ffado.Control.Element.MatrixMixer") self.transpose = transpose if (transpose): self.shortcolname = shortrowname self.shortrowname = shortcolname if ffado.config.bypassdbus: self.cols = 2 self.rows = 2 else: self.cols = self.interface.getRowCount() self.rows = self.interface.getColCount() else: self.shortcolname = shortcolname self.shortrowname = shortrowname if ffado.config.bypassdbus: self.cols = 2 self.rows = 2 else: self.cols = self.interface.getColCount() self.rows = self.interface.getRowCount() log.debug("Mixer has %i rows and %i columns" % (self.rows, self.cols)) self.mutes_dev = None self.mutes_interface = None if not ffado.config.bypassdbus and (mutespath != None): self.mutes_dev = self.bus.get_object(servername, mutespath) self.mutes_interface = dbus.Interface(self.mutes_dev, dbus_interface="org.ffado.Control.Element.MatrixMixer") self.inverts_dev = None self.inverts_interface = None if not ffado.config.bypassdbus and (invertspath != None): self.inverts_dev = self.bus.get_object(servername, invertspath) self.inverts_interface = dbus.Interface(self.inverts_dev, dbus_interface="org.ffado.Control.Element.MatrixMixer") layout = QGridLayout(self) layout.setSizeConstraint(QLayout.SetNoConstraint); self.setLayout(layout) self.rowHeaders = [] self.columnHeaders = [] self.items = [] self.shortname = shortname # Add row/column headers, but only if there's more than one # row/column if (self.cols > 1): for i in range(self.cols): ch = MixerChannel(i, self, self.getColName(i, self.shortname), smallFont) ch.hide.connect(self.hideColumn) layout.addWidget(ch, 0, i+1) self.columnHeaders.append( ch ) layout.setRowStretch(0, 0) layout.setRowStretch(1, 10) if (self.rows > 1): for i in range(self.rows): ch = MixerChannel(i, self, self.getRowName(i, self.shortname), smallFont) ch.hide.connect(self.hideRow) layout.addWidget(ch, i+1, 0) self.rowHeaders.append( ch ) # Add node-widgets for i in range(self.rows): self.items.append([]) for j in range(self.cols): if (transpose): mute_value = None if (self.mutes_interface != None): mute_value = self.mutes_interface.getValue(j,i) inv_value = None if (self.inverts_interface != None): inv_value = self.inverts_interface.getValue(j,i) if ffado.config.bypassdbus: val = 0 else: val = self.interface.getValue(j,i) node = MixerNode(i, j, val, sliderMaxValue, mute_value, inv_value, self, self) else: mute_value = None if (self.mutes_interface != None): mute_value = self.mutes_interface.getValue(i,j) inv_value = None if (self.inverts_interface != None): inv_value = self.inverts_interface.getValue(i,j) if ffado.config.bypassdbus: val = 0 else: val = self.interface.getValue(i,j) node = MixerNode(j, i, val, sliderMaxValue, mute_value, inv_value, self, self) if (smallFont): font = node.font() font.setPointSize(font.pointSize()/1.5) node.setFont(font) self.nodeConnect(node) layout.addWidget(node, i+1, j+1) self.items[i].append(node) self.hiddenRows = [] self.hiddenCols = [] def nodeConnect(self, node): node.nodeValueChanged.connect(self.valueChangedFn) def nodeDisconnect(self, node): node.nodeValueChanged.disconnect(self.valueChangedFn) def checkVisibilities(self): for x in range(len(self.items)): for y in range(len(self.items[x])): self.items[x][y].setSmall( (x in self.hiddenRows) | (y in self.hiddenCols) ) def hideColumn(self, column, hide): if hide: self.hiddenCols.append(column) else: self.hiddenCols.remove(column) self.checkVisibilities() def hideRow(self, row, hide): if hide: self.hiddenRows.append(row) else: self.hiddenRows.remove(row) self.checkVisibilities() # Columns and rows def getColName(self, i, shortname): if ffado.config.bypassdbus: return 'col ' + str(i) if (self.transpose): name = self.interface.getRowName(i) else: name = self.interface.getColName(i) self.shortname = shortname if (shortname or (name == '')): number = " %d" % (i+1) name = self.shortcolname + number return name def getRowName(self, j, shortname): if ffado.config.bypassdbus: return 'row ' + str(j) if (self.transpose): name = self.interface.getColName(j) else: name = self.interface.getRowName(j) self.shortname = shortname if (shortname or (name == '')): number = " %d" % (j+1) name = self.shortrowname + number return name def valueChangedFn(self, n): #log.debug("MatrixNode.valueChangedFn( %s )" % str(n)) if not ffado.config.bypassdbus: self.interface.setValue(n[1], n[0], n[2]) self.valueChanged.emit(n) # Update when routing is modified def updateRouting(self): if (self.cols > 1): for i in range(self.cols): last_name = self.columnHeaders[i].lbl.text() col_name = self.getColName(i, self.shortname) if last_name != col_name: #log.debug("MatrixControlView.updateRouting( %s )" % str(col_name)) self.columnHeaders[i].name = col_name self.columnHeaders[i].lbl.setText(col_name) if (self.rows > 1): for j in range(self.rows): last_name = self.rowHeaders[j].lbl.text() row_name = self.getRowName(j, self.shortname) if last_name != row_name: #log.debug("MatrixControlView.updateRouting( %s )" % str(row_name)) self.rowHeaders[j].name = row_name self.rowHeaders[j].lbl.setText(row_name) def updateValues(self, n): nbitems = len(n) // 3 for i in range(nbitems): n_0 = n[3*i] n_1 = n[3*i+1] n_2 = n[3*i+2] self.nodeDisconnect(self.items[n_0][n_1]) self.items[n_0][n_1].setValue(n_2) self.nodeConnect(self.items[n_0][n_1]) def refreshValues(self): if ffado.config.bypassdbus: return for x in range(len(self.items)): for y in range(len(self.items[x])): val = self.interface.getValue(x,y) if (self.transpose): self.items[y][x].setValue(val) self.items[y][x].internalValueChanged(val) else: self.items[x][y].setValue(val) self.items[x][y].internalValueChanged(val) def saveSettings(self, indent): matrixSaveString = [] matrixSaveString.append('%s \n' % indent) matrixSaveString.append('%s %d\n' % (indent, self.rows)) matrixSaveString.append('%s \n' % indent) matrixSaveString.append('%s \n' % indent) matrixSaveString.append('%s %d\n' % (indent, self.cols)) matrixSaveString.append('%s \n' % indent) matrixSaveString.append('%s \n' % indent) for i in range(self.rows): line = '%s ' % indent for j in range(self.cols): line += '%d ' % self.interface.getValue(i,j) line += '\n' matrixSaveString.append(line) matrixSaveString.append('%s \n' % indent) if (self.mutes_interface != None): matrixSaveString.append('%s \n' % indent) for i in range(self.rows): line = '%s ' % indent for j in range(self.cols): line += '%d ' % self.mutes_interface.getValue(i,j) line += '\n' matrixSaveString.append(line) matrixSaveString.append('%s \n' % indent) if (self.inverts_interface != None): matrixSaveString.append('%s \n' % indent) for i in range(self.rows): line = '%s ' % indent for j in range(self.cols): line += '%d ' % self.inverts_interface.getValue(i,j) line += '\n' matrixSaveString.append(line) matrixSaveString.append('%s \n' % indent) return matrixSaveString def readSettings(self, readMatrixString, transpose_coeff): if readMatrixString[0].find("") == -1: log.debug("Number of matrix rows must be specified") return False if readMatrixString[2].find("") == -1: log.debug("Non-conformal xml file") return False n_rows = int(readMatrixString[1]) if readMatrixString[3].find("") == -1: log.debug("Number of matrix columns must be specified") return False if readMatrixString[5].find("") == -1: log.debug("Non-conformal xml file") return False n_cols = int(readMatrixString[4]) if transpose_coeff: if n_rows > self.cols: n_rows = self.cols if n_cols > self.rows: n_cols = self.rows else: if n_rows > self.rows: n_rows = self.rows if n_cols > self.cols: n_cols = self.cols log.debug("Setting %d rows and %d columns coefficients" % (n_rows, n_cols)) try: idxb = readMatrixString.index('') idxe = readMatrixString.index('') except Exception: log.debug("No mixer matrix coefficients specified") idxb = -1 idxe = -1 if idxb >= 0: if idxe < idxb + n_rows + 1: log.debug("Incoherent number of rows in coefficients") return False i = 0 for s in readMatrixString[idxb+1:idxb + n_rows + 1]: coeffs = s.split() if len(coeffs) < n_cols: log.debug("Incoherent number of columns in coefficients") return False j = 0 for c in coeffs[0:n_cols]: if transpose_coeff: self.interface.setValue(j, i, int(c)) else: self.interface.setValue(i, j, int(c)) j += 1 i += 1 del coeffs try: idxb = readMatrixString.index('') idxe = readMatrixString.index('') except Exception: log.debug("No mixer mute coefficients specified") idxb = -1 idxe = -1 if idxb >= 0: if idxe < idxb + n_rows + 1: log.debug("Incoherent number of rows in mute") return false i = 0 for s in readMatrixString[idxb+1:idxb + n_rows + 1]: coeffs = s.split() if len(coeffs) < n_cols: log.debug("Incoherent number of columns in mute") return false j = 0 for c in coeffs[0:n_cols]: if transpose_coeff: self.mutes_interface.setValue(j, i, int(c)) else: self.mutes_interface.setValue(i, j, int(c)) j += 1 i += 1 del coeffs try: idxb = readMatrixString.index('') idxe = readMatrixString.index('') except Exception: log.debug("No mixer inverts coefficients specified") idxb = -1 idxe = -1 if idxb >= 0: if idxe < idxb + n_rows + 1: log.debug("Incoherent number of rows in inverts") return false i = 0 for s in readMatrixString[idxb+1:idxb + n_rows + 1]: coeffs = s.split() if len(coeffs) < n_cols: log.debug("Incoherent number of columns in inverts") return false j = 0 for c in coeffs[0:n_cols]: if transpose_coeff: self.inverts_interface.setValue(j, i, int(c)) else: self.inverts_interface.setValue(i, j, int(c)) j += 1 i += 1 del coeffs self.refreshValues() return True class VolumeSlider(QSlider): sliderChanged = pyqtSignal(tuple) def __init__(self, In, Out, value, parent): QSlider.__init__(self, QtCore.Qt.Vertical, parent) self.setTickPosition(QSlider.TicksBothSides) v_min = 10.0*toDBvalue(0) v_max = 10.0*toDBvalue(65536) self.setTickInterval(int((v_max-v_min)/10)) self.setMinimum(int(v_min)) self.setMaximum(int(v_max)) self.setSingleStep(1) self.sliderSetValue(value) self.In = In self.Out = Out self.valueChanged.connect(self.sliderValueChanged) def sliderSetValue(self, value): #log.debug("Volume slider value changed( %i )" % value) v = 10.0*toDBvalue(value) #log.debug("Volume slider value changed(dB: %g )" % (0.1*v)) self.setValue(int(v)) def sliderReadValue(self, value): return fromDBvalue(0.1*value) # Restore absolute value from DB # Emit signal for further use, especially for matrix view def sliderValueChanged(self, value): value = fromDBvalue(0.1*value) self.sliderChanged.emit((self.In, self.Out, value)) self.update() class VolumeSliderValueInfo(QLineEdit): def __init__(self, In, Out, value, parent): QLineEdit.__init__(self, parent) self.setReadOnly(True) self.setAlignment(Qt.AlignCenter) self.setAutoFillBackground(True) self.setFrame(False) self.sliderSetMinimalDim() self.bgcolors = BckgrdColorForNumber() self.sliderSetValue(value) def sliderSetMinimalDim(self): fontmetrics = self.fontMetrics() self.setMinimumSize(fontmetrics.boundingRect("-00.0 dB").size()*1.1) def sliderSetValue(self, value): color = self.bgcolors.getColor(value) palette = self.palette() palette.setColor(QPalette.Active, QPalette.Base, color) palette.setColor(QPalette.Active, QPalette.Text, self.bgcolors.getFrgdColor(color)) self.setPalette(palette) v = round(toDBvalue(value),1) if (v > -40): text = "%.1f dB" % v else: symb_inf = u"\u221E" text = "-" + symb_inf + " dB" self.setText(text) class BalanceSlider(QSlider): sliderChanged = pyqtSignal(tuple) def __init__(self, In, Out, value, parent): QSlider.__init__(self, QtCore.Qt.Horizontal, parent) v_min = -50 v_max = 50 self.setTickPosition(QSlider.TicksBothSides) self.setTickInterval((v_max-v_min)/2) self.setMinimum(v_min) self.setMaximum(v_max) self.setSingleStep(1) self.In = In self.Out = Out self.sliderSetValue(value) self.valueChanged.connect(self.sliderValueChanged) def sliderSetValue(self, value): #log.debug("Balance fader value set( %d, %d, %f )" % (self.In, self.Out, value)) v = int(round(50.0*value, 2)) self.setValue(v) def sliderReadValue(self): return float(round(self.value()/50.0, 2)) def sliderValueChanged(self, value): value = float(round(self.value()/50.0, 2)) #log.debug("Balance fader value changed( %d, %d, %f )" % (self.In, self.Out, value)) self.sliderChanged.emit((self.In, self.Out, value)) # Slider view widget class SliderControlView(QWidget): valueChanged = pyqtSignal(tuple) def __init__(self, parent, servername, basepath, rule="Columns_are_inputs", shortname=False, shortinname="Ch", shortoutname="Ch", stereochannels = []): QWidget.__init__(self, parent) if not ffado.config.bypassdbus: self.bus = dbus.SessionBus() self.dev = self.bus.get_object(servername, basepath) self.interface = dbus.Interface(self.dev, dbus_interface="org.ffado.Control.Element.MatrixMixer") self.rule = rule self.shortname = shortname self.shortinname = shortinname self.shortoutname = shortoutname self.stereochannels = stereochannels self.out = [] self.nbIn = self.getNbIn() self.nbOut = self.getNbOut() self.outmatrix = [] k = 0 for i in range(self.nbOut): widget = QWidget(parent) v_layout = QVBoxLayout(widget) v_layout.setAlignment(Qt.AlignCenter) widget.setLayout(v_layout) self.out.append(widget) self.out[i].is_stereo = False self.out[i].out_1 = k self.outmatrix.append(i) self.out[i].outname = "Out %d" % (k+1) if k in self.stereochannels: self.out[i].is_stereo = True self.out[i].outname += "+%d" % (k+2) self.outmatrix.append(i) self.out[i].lbl = [] # Mixer/Out info label if (self.nbOut > 1): lbl = QLabel(widget) lbl.setText(self.getOutName(i, self.shortname)) lbl.setAlignment(Qt.AlignCenter) v_layout.addWidget(lbl) self.out[i].lbl.append(lbl) h_layout_wid = QWidget(widget) h_layout = QHBoxLayout(h_layout_wid) h_layout.setAlignment(Qt.AlignCenter) h_layout_wid.setLayout(h_layout) v_layout.addWidget(h_layout_wid) self.out[i].volume = [] self.out[i].svl = [] self.out[i].balance = [] for j in range(self.nbIn): h_v_layout_wid = QWidget(h_layout_wid) h_v_layout = QVBoxLayout(h_v_layout_wid) h_v_layout.setAlignment(Qt.AlignCenter) h_v_layout_wid.setLayout(h_v_layout) h_layout.addWidget(h_v_layout_wid) # Mixer/In info label if (self.nbIn > 1): lbl = QLabel(h_v_layout_wid) lbl.setText(self.getInName(j, self.shortname)) lbl.setAlignment(Qt.AlignCenter) h_v_layout.addWidget(lbl) self.out[i].lbl.append(lbl) h_v_h_layout_wid = QWidget(h_v_layout_wid) h_v_h_layout = QHBoxLayout(h_v_h_layout_wid) h_v_h_layout.setAlignment(Qt.AlignCenter) h_v_h_layout_wid.setLayout(h_v_h_layout) h_v_layout.addWidget(h_v_h_layout_wid) volume = VolumeSlider(j, i, self.getVolumeValue(j,i), h_v_h_layout_wid) h_v_h_layout.addWidget(volume) self.out[i].volume.append(volume) self.volumeConnect(volume) # Volume slider info svl = VolumeSliderValueInfo(j, i, self.getVolumeValue(j,i), h_v_layout_wid) h_v_layout.addWidget(svl) self.out[i].svl.append(svl) # Balance fader if self.out[i].is_stereo: balance = BalanceSlider(j, i, self.getBalanceValue(j,i), h_v_layout_wid) h_v_layout.addWidget(balance) self.out[i].balance.append(balance) self.balanceConnect(balance) k += 1 if self.out[i].is_stereo: k += 1 def volumeConnect(self, volume): volume.sliderChanged.connect(self.valueChangedVolume) def volumeDisconnect(self, volume): volume.sliderChanged.disconnect(self.valueChangedVolume) def balanceConnect(self, balance): balance.sliderChanged.connect(self.valueChangedBalance) def balanceDisconnect(self, balance): balance.sliderChanged.disconnect(self.valueChangedBalance) def getNbIn(self): if ffado.config.bypassdbus: return 2 if (self.rule == "Columns_are_inputs"): return self.interface.getColCount() else: return self.interface.getRowCount() def getNbOut(self): if ffado.config.bypassdbus: return 2 if (self.rule == "Columns_are_inputs"): nbout = self.interface.getRowCount() else: nbout = self.interface.getColCount() return nbout-len(self.stereochannels) def getVolumeValue(self, In, i): if ffado.config.bypassdbus: return 1 Out = self.out[i].out_1 if (self.rule == "Columns_are_inputs"): vl = self.interface.getValue(Out, In) else: vl = self.interface.getValue(In, Out) if (self.out[i].is_stereo): if (self.rule == "Columns_are_inputs"): vr = self.interface.getValue(Out+1, In) else: vr = self.interface.getValue(In, Out+1) return getStereoVolume(vl, vr) else: return vl; def getBalanceValue(self, In, i): if ffado.config.bypassdbus: return 0.5 Out = self.out[i].out_1 if (self.rule == "Columns_are_inputs"): vl = self.interface.getValue(Out, In) vr = self.interface.getValue(Out+1, In) else: vl = self.interface.getValue(In, Out) vr = self.interface.getValue(In, Out+1) return getStereoBalance(vl, vr) def setValue(self, In, Out, val): if ffado.config.bypassdbus: return if (self.rule == "Columns_are_inputs"): return self.interface.setValue(Out, In, val) else: return self.interface.setValue(In, Out, val) def updateValues(self, n): nbitems = len(n) // 3 for j in range(nbitems): n_0 = n[3*j] n_1 = n[3*j+1] n_2 = n[3*j+2] i = self.outmatrix[n_1] if (self.out[i].is_stereo): v = self.getVolumeValue(n_0, i) self.volumeDisconnect(self.out[i].volume[n_0]) self.balanceDisconnect(self.out[i].balance[n_0]) self.out[i].volume[n_0].sliderSetValue(v) self.out[i].svl[n_0].sliderSetValue(v) b = self.getBalanceValue(n_0, i) # log.debug("update Value (%d %d %d %f)" % (n_0, i, v, b)) self.out[i].balance[n_0].sliderSetValue(b) self.volumeConnect(self.out[i].volume[n_0]) self.balanceConnect(self.out[i].balance[n_0]) else: v = n_2 # log.debug("update Value (%d %d %d)" % (n_0, i, v)) self.volumeDisconnect(self.out[i].volume[n_0]) self.out[i].volume[n_0].sliderSetValue(v) self.out[i].svl[n_0].sliderSetValue(v) self.volumeConnect(self.out[i].volume[n_0]) def valueChangedVolume(self, n): #log.debug("VolumeSlider.valueChanged( %s )" % str(n)) v = n[2] n1 = self.out[n[1]].out_1 if (self.out[n[1]].is_stereo): b = self.out[n[1]].balance[n[0]].value()/50.0 vl = int(getVolumeLeft(v, b)) self.setValue(n[0], n1, vl) n2 = n1+1 vr = int(getVolumeRight(v, b)) self.setValue(n[0], n2, vr) n_t = (n[0], n1, vl, n[0], n2, vr) self.valueChanged.emit(n_t) else: self.setValue(n[0], n1, v) n_t = (n[0], n1, v) self.valueChanged.emit(n_t) self.out[n[1]].svl[n[0]].sliderSetValue(v) def valueChangedBalance(self, n): #log.debug("BalanceSlider.valueChanged( %s )" % str(n)) n1 = self.out[n[1]].out_1 v = fromDBvalue(0.1*self.out[n[1]].volume[n[0]].value()) b = n[2] vl = int(getVolumeLeft(v, b)) self.setValue(n[0], n1, vl) n2 = n1+1 vr = int(getVolumeRight(v, b)) self.setValue(n[0], n2, vr) n_t = (n[0], n1, vl, n[0], n2, vr) self.valueChanged.emit(n_t) def getOutName(self, i, shortname): self.shortname = shortname k = self.out[i].out_1 if (shortname): if (self.out[i].is_stereo): number = " %d+%d" % (k+1, k+2) else: number = " %d" % (k+1) name = self.shortoutname + number return name else: if ffado.config.bypassdbus: return 'OutName ' + str(i) if (self.rule == "Columns_are_inputs"): if (self.out[i].is_stereo): name = self.interface.getRowName(k).replace('\n','')+" + "+self.interface.getRowName(k+1).replace('\n','') else: name = self.interface.getRowName(k).replace('\n','') return name else: if (self.out[i].is_stereo): name = self.interface.getColName(k).replace('\n','')+" + "+self.interface.getColName(k+1).replace('\n','') else: name = self.interface.getColName(k).replace('\n','') return name def getInName(self, j, shortname): self.shortname = shortname if (shortname): number = " %d" % (j+1) name = self.shortinname + number return name else: if ffado.config.bypassdbus: return 'InName ' + str(j) if (self.rule == "Columns_are_inputs"): return self.interface.getColName(j) else: return self.interface.getRowName(j) # Update when routing is modified def updateRouting(self): for i in range(self.nbOut): if (self.nbOut > 1): last_name = self.out[i].lbl[0].text() out_name = self.getOutName(i, self.shortname) if last_name != out_name: #log.debug("SliderControlView.updateRouting( %s )" % str(out_name)) self.out[i].lbl[0].setText(out_name) if (self.nbIn > 1): for j in range(self.nbIn): last_name = self.out[i].lbl[j+1].text() in_name = self.getInName(j, self.shortname) if last_name != in_name: #log.debug("SliderControlView.updateRouting( %s )" % str(in_name)) self.out[i].lbl[j+1].setText(in_name) def refreshValues(self): for n_out in range(self.nbOut): for n_in in range(self.nbIn): i = self.outmatrix[n_out] v = self.getVolumeValue(n_in, i) if (self.out[i].is_stereo): self.volumeDisconnect(self.out[i].volume[n_in]) self.out[i].volume[n_in].sliderSetValue(v) self.out[i].svl[n_in].sliderSetValue(v) b = self.getBalanceValue(n_in, i) # log.debug("update Value (%d %d %d %f)" % (n_0, i, v, b)) self.out[i].balance[n_in].sliderSetValue(b) self.volumeConnect(self.out[i].volume[n_in]) else: # log.debug("update Value (%d %d %d)" % (n_0, i, v)) self.out[i].volume[n_in].sliderSetValue(v) self.out[i].svl[n_in].sliderSetValue(v) def saveSettings(self, indent): if ffado.config.bypassdbus: rows = 2 cols = 2 else: rows = self.interface.getRowCount() cols = self.interface.getColCount() matrixSaveString = [] matrixSaveString.append('%s \n' % indent) matrixSaveString.append('%s %d\n' % (indent, rows)) matrixSaveString.append('%s \n' % indent) matrixSaveString.append('%s \n' % indent) matrixSaveString.append('%s %d\n' % (indent, cols)) matrixSaveString.append('%s \n' % indent) matrixSaveString.append('%s \n' % indent) for i in range(rows): line = '%s ' % indent for j in range(cols): line += '%d ' % self.interface.getValue(i,j) line += '\n' matrixSaveString.append(line) matrixSaveString.append('%s \n' % indent) return matrixSaveString def readSettings(self, readMatrixString, transpose_coeff): if ffado.config.bypassdbus: rows = 2 cols = 2 else: rows = self.interface.getRowCount() cols = self.interface.getColCount() if readMatrixString[0].find("") == -1: log.debug("Number of matrix rows must be specified") return False if readMatrixString[2].find("") == -1: log.debug("Non-conformal xml file") return False n_rows = int(readMatrixString[1]) if readMatrixString[3].find("") == -1: log.debug("Number of matrix columns must be specified") return False if readMatrixString[5].find("") == -1: log.debug("Non-conformal xml file") return False n_cols = int(readMatrixString[4]) if transpose_coeff: if n_rows > cols: n_rows = cols if n_cols > rows: n_cols = rows else: if n_rows > rows: n_rows = rows if n_cols > cols: n_cols = cols log.debug("Setting %d rows and %d columns coefficients" % (n_rows, n_cols)) try: idxb = readMatrixString.index('') idxe = readMatrixString.index('') except Exception: log.debug("No mixer matrix coefficients specified") idxb = -1 idxe = -1 if idxb >= 0: if idxe < idxb + n_rows + 1: log.debug("Incoherent number of rows in coefficients") return False i = 0 for s in readMatrixString[idxb+1:idxb + n_rows + 1]: coeffs = s.split() if len(coeffs) < n_cols: log.debug("Incoherent number of columns in coefficients") return False j = 0 for c in coeffs[0:n_cols]: if transpose_coeff: self.interface.setValue(j, i, int(c)) else: self.interface.setValue(i, j, int(c)) j += 1 i += 1 del coeffs self.refreshValues() return True from functools import partial class MatrixMixer(QWidget): def __init__(self, servername, basepath, parent=None, rule="Columns_are_inputs", sliderMaxValue=-1, mutespath=None, invertspath=None, smallFont=False, taborientation=QTabWidget.West, tabshape=QTabWidget.Triangular): QWidget.__init__(self, parent) self.servername = servername self.basepath = basepath self.sliderMaxValue = sliderMaxValue self.mutespath = mutespath self.invertspath = invertspath self.smallFont = smallFont self.layout = QVBoxLayout(self) self.setLayout(self.layout) # Mixer view Tool bar mxv_set = QToolBar("View settings", self) # Here is a hack; the first action button appears to behaves strangely, # possibly a PyQt bug (or an unsufficient fair implementation of it) # Feel free to remove the next three lines at a time in the future hack = QAction(" ", mxv_set) hack.setDisabled(True) mxv_set.addAction(hack) transpose_matrix = QAction("Transpose", mxv_set) self.transpose = False transpose_matrix.setShortcut('Ctrl+T') transpose_matrix.setToolTip("Invert rows and columns in Matrix view") mxv_set.addAction(transpose_matrix) transpose_matrix.triggered.connect(self.transposeMatrixView) mxv_set.addSeparator() self.hide_matrix = QAction("Hide matrix", mxv_set) self.hide_matrix_bool = False mxv_set.addAction(self.hide_matrix) self.hide_matrix.triggered.connect(self.hideMatrixView) mxv_set.addSeparator() self.hide_per_output = QAction("Hide per Output", mxv_set) self.hide_per_output_bool = False mxv_set.addAction(self.hide_per_output) self.hide_per_output.triggered.connect(self.hidePerOutputView) mxv_set.addSeparator() self.use_short_names = QAction("Short names", mxv_set) self.short_names_bool = False mxv_set.addAction(self.use_short_names) self.use_short_names.setToolTip("Use short or full names for input and output channels") self.use_short_names.triggered.connect(self.shortChannelNames) mxv_set.addSeparator() font_switch_lbl = QLabel(mxv_set) font_switch_lbl.setText("Font size ") mxv_set.addWidget(font_switch_lbl) font_switch = QComboBox(mxv_set) font_switch.setToolTip("Labels font size") font = font_switch.font() for i in range(10): font_switch.addItem(" %d " % (font.pointSize()+4-i)) font_switch.setCurrentIndex(font_switch.findText(" %d " % font.pointSize())) mxv_set.addWidget(font_switch) mxv_set.addSeparator() font_switch.activated.connect(self.changeFontSize) self.layout.addWidget(mxv_set) self.mxv_set = mxv_set # First tab is for matrix view # Next are for "per Out" view self.tabs = QTabWidget(self) self.tabs.setTabPosition(taborientation) self.tabs.setTabShape(tabshape) self.layout.addWidget(self.tabs) # Inputs/Outputs versus rows/columns rule self.rule = rule # Matrix view tab if (rule == "Columns_are_inputs"): self.matrix = MatrixControlView(servername, basepath, self, sliderMaxValue, mutespath, invertspath, smallFont, self.short_names_bool, "In", "Out", self.transpose) else: self.matrix = MatrixControlView(servername, basepath, self, sliderMaxValue, mutespath, invertspath, smallFont, self.short_names_bool, "Out", "In", self.transpose) self.matrix.valueChanged.connect(self.matrixControlChanged) self.scrollarea_matrix = QScrollArea(self.tabs) self.scrollarea_matrix.setWidgetResizable(True) self.scrollarea_matrix.setWidget(self.matrix) self.tabs.addTab(self.scrollarea_matrix, " Matrix ") # Add stereo/mono output choice in tool bar if (rule == "Columns_are_inputs"): if (self.transpose): nb_out_mono = self.matrix.cols else: nb_out_mono = self.matrix.rows else: if (self.transpose): nb_out_mono = self.matrix.rows else: nb_out_mono = self.matrix.cols stereo_switch_lbl = QLabel(mxv_set) stereo_switch_lbl.setText("Stereo: ") mxv_set.addWidget(stereo_switch_lbl) self.stereo_channels = [] self.stereo_switch = [] for i in range(int(nb_out_mono/2)): stereo_switch = QPushButton("%d+%d" % (2*i+1, 2*i+2), mxv_set) stereo_switch.setToolTip("Set these output channels as stereo") stereo_switch.setCheckable(True) stereo_switch.clicked.connect(partial(self.switchStereoChannel, i)) stereo_switch.setMinimumSize(stereo_switch_lbl.fontMetrics().boundingRect("%d+%d" % (nb_out_mono, nb_out_mono)).size()*1.05) stereo_switch.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)) stereo_switch.is_stereo = False mxv_set.addWidget(stereo_switch) self.stereo_switch.append(stereo_switch) mxv_set.addSeparator() # Per out view tabs self.perOut = SliderControlView(self, servername, basepath, rule, self.short_names_bool, "In", "Out", self.stereo_channels) self.perOut.valueChanged.connect(self.sliderControlChanged) for i in range(self.perOut.nbOut): self.perOut.out[i].scrollarea = QScrollArea(self.tabs) self.perOut.out[i].scrollarea.setWidgetResizable(True) self.perOut.out[i].scrollarea.setWidget(self.perOut.out[i]) self.tabs.addTab(self.perOut.out[i].scrollarea, " %s " % self.perOut.out[i].outname) def transposeMatrixView(self): self.transpose = not(self.transpose) self.tabs.removeTab(0) self.scrollarea_matrix.destroy() if (self.rule == "Columns_are_inputs"): self.matrix = MatrixControlView(self.servername, self.basepath, self, self.sliderMaxValue, self.mutespath, self.invertspath, self.smallFont, self.short_names_bool, "In", "Out", self.transpose) else: self.matrix = MatrixControlView(self.servername, self.basepath, self, self.sliderMaxValue, self.mutespath, self.invertspath, self.smallFont, self.short_names_bool, "Out", "In", self.transpose) self.matrix.valueChanged.connect(self.matrixControlChanged) self.scrollarea_matrix = QScrollArea(self.tabs) self.scrollarea_matrix.setWidgetResizable(True) self.scrollarea_matrix.setWidget(self.matrix) self.tabs.insertTab(0, self.scrollarea_matrix, "Matrix") self.tabs.setCurrentIndex(0) def hideMatrixView(self): self.hide_matrix_bool = not(self.hide_matrix_bool) if (self.hide_matrix_bool): self.tabs.removeTab(0) self.hide_matrix.setText("Show Matrix") else: self.tabs.insertTab(0, self.scrollarea_matrix, "Matrix") self.tabs.setCurrentIndex(0) self.hide_matrix.setText("Hide Matrix") def hidePerOutputView(self): self.hide_per_output_bool = not(self.hide_per_output_bool) if (self.hide_per_output_bool): index_0 = 1 if (self.hide_matrix_bool): index_0 = 0 for i in range(self.perOut.nbOut): self.tabs.removeTab(index_0) self.hide_per_output.setText("Show per Output") else: for i in range(self.perOut.nbOut): self.tabs.insertTab(i+1, self.perOut.out[i].scrollarea, " %s " % self.perOut.out[i].outname) self.hide_per_output.setText("Hide per Output") # Font size for channel names def changeFontSize(self, size): font = self.mxv_set.font() font.setPointSize(int(size)) self.mxv_set.setFont(font) font = self.tabs.font() font.setPointSize(int(size)) self.tabs.setFont(font) font = self.matrix.font() font.setPointSize(int(size)) self.matrix.setFont(font) font = self.perOut.font() font.setPointSize(int(size)) self.perOut.setFont(font) for i in range(self.perOut.nbOut): for j in range(self.perOut.nbIn): self.perOut.out[i].svl[j].sliderSetMinimalDim() # Allows long name for Mixer/Out and /In to be hidden def shortChannelNames(self): checked = not(self.short_names_bool) if (self.matrix.cols > 1): for i in range(self.matrix.cols): self.matrix.columnHeaders[i].name = self.matrix.getColName(i, checked) self.matrix.columnHeaders[i].lbl.setText(self.matrix.columnHeaders[i].name) if (self.matrix.rows > 1): for j in range(self.matrix.rows): self.matrix.rowHeaders[j].name = self.matrix.getRowName(j, checked) self.matrix.rowHeaders[j].lbl.setText(self.matrix.rowHeaders[j].name) for i in range(self.perOut.nbOut): if (self.perOut.nbOut > 1): self.perOut.out[i].lbl[0].setText(self.perOut.getOutName(i, checked)) if (self.perOut.nbIn > 1): for j in range(self.perOut.nbIn): self.perOut.out[i].lbl[j+1].setText(self.perOut.getInName(j, checked)) # Care for hidden columns if (self.matrix.cols > 1): for i in self.matrix.hiddenCols: self.matrix.columnHeaders[i].lbl.setText("%d" % (i+1)) # Care for hidden rows if (self.matrix.rows > 1): for j in self.matrix.hiddenRows: self.matrix.rowHeaders[j].lbl.setText("%d" % (j+1)) self.short_names_bool = checked if (self.short_names_bool): self.use_short_names.setText("Long names") else: self.use_short_names.setText("Short names") # Sliders value change # Care that some recursive process is involved and only stop when exactly same values are involved # Matrix view def matrixControlChanged(self, n): # Update value needed for "per Out" view #log.debug("Update per Output( %s )" % str(n)) nbitems = len(n) // 3 if (self.rule == "Columns_are_inputs"): n_t = n else: n_t = () for i in range(nbitems): n_t += (n[3*i+1], n[3*i], n[3*i+2]) self.perOut.updateValues(n_t) # "per Out" view def sliderControlChanged(self, n): # Update value needed for matrix view #log.debug("Update Matrix( %s )" % str(n)) nbitems = len(n) // 3 if (((self.rule == "Columns_are_inputs") and not self.transpose) or ((self.rule != "Columns_are_inputs") and self.transpose)): n_t = () for i in range(nbitems): n_t += (n[3*i+1], n[3*i], n[3*i+2]) else: n_t = n self.matrix.updateValues(n_t) def refreshValues(self): # Refreshing matrix coefficient should be sufficient, # propagating the changes to perOut view self.matrix.refreshValues() def switchStereoChannel(self, channel, is_stereo): #log.debug(" switching channels %d+%d to stereo/mono" % (2*channel, 2*channel+1)) self.stereo_switch[channel].is_stereo = self.stereo_switch[channel].isChecked(); if (self.stereo_switch[channel].is_stereo): self.stereo_channels.append(2*channel) else: self.stereo_channels.remove(2*channel) # tab 0 is for matrix except if it is hidden index_0 = 1 if (self.hide_matrix_bool): index_0 = 0 for i in range(self.perOut.nbOut): self.tabs.removeTab(index_0) self.perOut.destroy() self.perOut = SliderControlView(self, self.servername, self.basepath, self.rule, self.short_names_bool, "In", "Out", self.stereo_channels) self.perOut.valueChanged.connect(self.sliderControlChanged) current = 0 for i in range(self.perOut.nbOut): self.perOut.out[i].scrollarea = QScrollArea(self.tabs) self.perOut.out[i].scrollarea.setWidgetResizable(True) self.perOut.out[i].scrollarea.setWidget(self.perOut.out[i]) self.tabs.addTab(self.perOut.out[i].scrollarea, " %s " % self.perOut.out[i].outname) if self.perOut.out[i].out_1 == 2*channel: current = i self.tabs.setCurrentWidget(self.perOut.out[current].scrollarea) # Update when routing is modified def updateRouting(self): self.matrix.updateRouting() self.perOut.updateRouting() def saveSettings(self, indent): mixerString = [] mixerString.append("%s\n" % indent) mixerString.extend(self.matrix.saveSettings(indent)) mixerString.append("%s\n" % indent) mixerString.append("%s\n" % indent) mixerString.append("%s \n" % indent) n = len(self.stereo_channels) mixerString.append("%s %d\n" % (indent, n)) mixerString.append("%s \n" % indent) if n > 0: mixerString.append("%s \n" % indent) for i in self.stereo_channels: mixerString.append("%s %d %d\n" % (indent, i+1, i+2)) mixerString.append("%s \n" % indent) mixerString.append("%s\n" % indent) return mixerString def readSettings(self, readMixerString, transpose_coeff): try: idxb = readMixerString.index('') idxe = readMixerString.index('') except Exception: log.debug("No matrices found") idxb = -1 idxe = -1 if idxb >= 0: if idxe > idxb+1: readString = [] for s in readMixerString[idxb+1:idxe]: readString.append(s) if self.matrix.readSettings(readString, transpose_coeff): log.debug("Mixer matrices settings modified") del readString try: idx = readMixerString.index('') except Exception: log.debug("No stereo outputs channels information found") idx = -1 if idx >= 0: if readMixerString[idx+1].find('') == -1: log.debug("Number of stereo output channels must be specified") return False n = int(readMixerString[idx+2]) if n > self.perOut.nbOut // 2: log.debug("Incoherent number of stereo channels") return False if n > 0: if readMixerString[idx+3].find('') == -1: log.debug("No tag found") return False if readMixerString[idx+4].find('') == -1: log.debug("No tag found") return False for s in readMixerString[idx+5:idx+5+n]: i = (int(s.split()[0]) - 1)/2 self.stereo_switch[i].setChecked(True); self.switchStereoChannel(i, True) return True # # vim: et ts=4 sw=4 fileencoding=utf8 libffado-2.4.5/support/mixer-qt4/ffado/widgets/__init__.py0000644000175000001440000000000011246571010023047 0ustar jwoitheuserslibffado-2.4.5/support/mixer-qt4/ffado/config.py.in0000644000175000001440000000162113114471135021531 0ustar jwoitheusers# -*- coding: utf-8 -*- REGISTER_URL = '$REGISTRATION_URL' INI_FILE_PATH = "$CONFIGDIR/registration.ini" FFADO_CONFIG_DIR = "$CONFIGDIR" FFADO_VERSION="$VERSION-$REVISION" FFADO_DBUS_SERVER = 'org.ffado.Control' FFADO_DBUS_BASEPATH = '/org/ffado/Control' POLL_SLEEP_TIME_MSEC = 100 # 100ms PYTHONDIR = "$PYPKGDIR" SHAREDIR = "$SHAREDIR" USER_CONFIG_FILE = "$USER_CONFIG_FILE" SYSTEM_CONFIG_FILE = "$SYSTEM_CONFIG_FILE" DEBUG = $DEBUG # If set true it will open all mixers bypassdbus = False UIDIR = PYTHONDIR import os.path if os.path.exists('support/mixer-qt4/ffado/mixer/globalmixer.ui'): UIDIR = "support/mixer-qt4" if os.path.exists('ffado/mixer/globalmixer.ui'): UIDIR = "." import os.path # from PyQt4 import uic from ffado.import_pyqt import * def uicLoad(file, object): if not file.endswith(".ui"): file += ".ui" uic.loadUi(os.path.join(UIDIR,file), object) # vim: et libffado-2.4.5/support/mixer-qt4/ffado/.gitignore0000644000175000001440000000001212132617070021265 0ustar jwoitheusersconfig.py libffado-2.4.5/support/mixer-qt4/ffado/panelmanagerstatus.ui0000644000175000001440000000220612130260370023534 0ustar jwoitheusers PanelManagerStatusUI 0 0 611 218 Form 0 0 300 0 Bus reconfiguration in progress, please wait... Qt::AlignCenter true libffado-2.4.5/support/mixer-qt4/ffado/__init__.py0000644000175000001440000000000011246546617021420 0ustar jwoitheuserslibffado-2.4.5/support/mixer-qt4/ffado/regdialog.ui0000644000175000001440000001573111246546617021624 0ustar jwoitheusers ffadoRegDialogUI 0 0 577 497 FFADO Usage Statistics true Information being sent Vendor Name: false txtVendorName true Qt::Horizontal 16 20 Id: false txtVendorId 0x000aac true Model Name: false txtModelName true Qt::Horizontal 16 20 Id: false txtModelId 0x00010065 true GUID: false txtGUID true FFADO Version: false txtVersion true Your E-Mail: false txtEmail (optional) Never Not now 0 0 Send true libffado-2.4.5/support/mixer-qt4/ffado-mixer-profiler.in0000755000175000001440000000210414206145246022603 0ustar jwoitheusers#!/usr/bin/python # # Copyright (C) 2005-2009 by Pieter Palmers # 2007-2009 by Arnold Krille # # 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. # # 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 sys # Add the path of the installed ffado-modules # for path in sys.path: sys.path.insert(0, "$PYPKGDIR" ) from ffado.ffadowindow import * import cProfile if __name__ == "__main__": cProfile.run('ffadomain(sys.argv)', sort='time') # # vim: ts=4 sw=4 et libffado-2.4.5/support/mixer-qt4/ffado-mixer.in0000755000175000001440000000211514206145246020765 0ustar jwoitheusers#!$PYTHON_INTERPRETER # # Copyright (C) 2005-2008 by Pieter Palmers # 2007-2009 by Arnold Krille # # 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 sys # Add the path of the installed ffado-modules # for path in sys.path: sys.path.insert(0, "$PYPKGDIR" ) from ffado.ffadowindow import * if __name__ == "__main__": sys.exit(ffadomain(sys.argv)) # # vim: ts=4 sw=4 et libffado-2.4.5/support/mixer-qt4/ffado-mixer.10000644000175000001440000000170013115517174020514 0ustar jwoitheusers.TH FFADO-MIXER 1 09-Jan-2017 "ffado-mixer" .SH NAME ffado-mixer \- a graphical front-end for the FFADO mixer DBus controls exposed by ffado-dbus-server. .SH SYNOPSIS .BI "ffado-mixer [options]" .sp Available options: .B "[\-b]" .sp .SH DESCRIPTION .B ffado-mixer presents a graphical application allowing a FFADO device to be controlled. The extent of the control is determined by the level of support for the device in FFADO and in .B ffado-dbus-server. Typical controls offered by .B ffado-mixer include faders for the on-board mixer, phantom power control, mode switches and so on. .SH "OPTIONS" .TP .B "\-b" Do not query ffado-dbus-server for an active interface. Instead, create an instance of a mixer for every supported interface as defined in FFADO's "configuration" file with stubs providing placeholder values in place of settings obtained from the interface. This is primarily to assist with debugging. .SH "SEE ALSO" .BR ffado-dbus-server (1) libffado-2.4.5/support/mixer-qt4/.gitignore0000644000175000001440000000004112132617070020210 0ustar jwoitheusersffado-mixer ffado-mixer-profiler libffado-2.4.5/support/mixer-qt4/configtest.cfg0000644000175000001440000000102311116770400021045 0ustar jwoitheusersieee1394 : { min_split_timeout_usecs = 1000000; isomanager : { iso_receive_mode = 0; }; }; device_definitions = ( { vendorid = 0xAAC; modelid = 0x3; vendorname = "TerraTec Electronic GmbH"; modelname = "Phase 88 FW"; driver = 1; }, { vendorid = 0x130E; modelid = 0x3; vendorname = "Focusrite"; modelname = "Saffire Pro26IO"; driver = 1; }, { vendorid = 0x1486; modelid = 0xAF2; vendorname = "Echo"; modelname = "AudioFire2"; driver = 2; } ); libffado-2.4.5/support/tools/0000755000175000001440000000000014206145613015536 5ustar jwoitheuserslibffado-2.4.5/support/tools/SConscript0000644000175000001440000000355514206145246017562 0ustar jwoitheusers#!/usr/bin/python # # Copyright (C) 2007-2008 Arnold Krille # Copyright (C) 2007-2008 Pieter Palmers # Copyright (C) 2012 Jonathan Woithe # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os from string import Template Import( 'env' ) e = env.Clone() # Needed to build ffado-set-nickname e.MergeFlags( "-I#/ -I#/src -L%ssrc -lffado" % env['build_base'] ) if not e.GetOption( "clean" ): if not env['SERIALIZE_USE_EXPAT']: if 'LIBXML30_FLAGS' in env : e.MergeFlags( env['LIBXML30_FLAGS'].decode() ) if not('LIBXML30_FLAGS' in env) : e.MergeFlags( env['LIBXML26_FLAGS'].decode() ) else: e.PrependUnique( LIBS=["expat"] ) e.Command( "static_info.txt", "ffado-diag", "support/tools/ffado-diag --static > $TARGET" ) e.ScanReplace( "ffado-diag.in" ) e.Install( "$bindir", "ffado-diag" ) e.Install( "$libdatadir", "static_info.txt" ) if env['ENABLE_DICE']: e.Program( target = "ffado-set-nickname", source = "ffado-set-nickname.cpp" ) e.Install( "$bindir", "ffado-set-nickname" ) # Install manpages in section 1 dest = os.path.join("$mandir", "man1", "ffado-diag.1") env.InstallAs(source="ffado-diag.1", target=dest) libffado-2.4.5/support/tools/ffado-diag.10000644000175000001440000000162414206145246017606 0ustar jwoitheusers.TH FFADO\-DIAG 1 27\-Mar\-2012 "ffado\-diag" .SH NAME ffado\-diag \- print system diagnostic information related to FFADO. \" .SH SYNOPSIS .BR ffado\-diag [\| \-\-static \||\| \-V \||\| \-\-version \||\| \-\-usage \|] \" .SH DESCRIPTION .B ffado\-diag prints out an extensive collection of diagnostic information about the computer it is run on. Information included is the FFADO version number, the version number of libraries which FFADO depends on, the FireWire interface card, interrupt usage, and so on. This is useful for developers to know when giving support via the FFADO mailing lists. For anything other than trivial issues, the output of .B ffado\-diag will be one of the first things asked fro when debugging a problem. \" .SH OPTIONS .BR \-V ", " \-\-version Display version information. .B \-\-usage Print a short usage message and exit. .B \-\-static Only display executable paths and libraries. libffado-2.4.5/support/tools/ffado-diag.in0000755000175000001440000002513114206145246020056 0ustar jwoitheusers#!$PYTHON_INTERPRETER # Dollar variables are preprocessed by SConscript at build time. version_info = "FFADO diagnostic utility $VERSION$REVISIONSTRING" copyright_info = """ (C) 2008 Pieter Palmers 2009-2010 Arnold Krille 2018 Nicolas Boulenguez, Jonathan Woithe """ static_info = "$LIBDATADIR/static_info.txt" # 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, version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Test for common FFADO problems import glob import os.path import re import subprocess import sys # Prefer reproducible output with few non-ASCII characters. os.environ ["LC_ALL"] = "C" # Consistent formatting. def show_pair (key, value): # rstrip () for convenience, but do not assume that value is a string. print ("{:25} {}".format (key, value).rstrip ()) def indent (lines): print (" {}".format (lines.rstrip ().replace ("\n", "\n "))) # Convenient shortcuts. def stdout (*args): return subprocess.check_output (args).decode ("utf8") def which (command): popen = subprocess.Popen (("which", command), stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = popen.communicate () if popen.returncode == 0: return stdout.decode ("utf8").rstrip () elif popen.returncode == 1: return None else: print (stderr) sys.exit (1) # Parse command line. usage = """Usage: ffado-diag [--static | -V | --version | --usage] --static Only display executable paths and libraries. -V, --version Display version information. --usage Print a short usage message and exit.""" if len (sys.argv) == 1: static_option = False elif len (sys.argv) == 2: if sys.argv [1] == "--static": static_option = True elif sys.argv [1] in ("-V", "--version"): print (version_info) sys.exit (0) elif sys.argv [1] == "--usage": print (usage) sys.exit (0) else: print (usage) sys.exit (1) else: print (usage) sys.exit (1) if not(static_option): print (version_info) print (copyright_info) for command in ("gcc", "g++", "pyuic4", "pyuic5"): path = which (command) show_pair (command, path) if path: version = stdout (path, "--version") show_pair ('', version [:version.find ("\n")]) # jackd --version exits with a non-zero status (tested with jackd 1.9.10). path = which ("jackd") show_pair ("jackd", path) if path: popen = subprocess.Popen ((path, "--version"), stdout=subprocess.PIPE) version, _ = popen.communicate () version = version.decode ("utf8") show_pair ('', version [:version.find ("\n")]) pkg_config = which ("pkg-config") show_pair ("pkg-config", pkg_config) if pkg_config: for lib in ("jack", "libraw1394", "libavc1394", "libiec61883", "libxml++-2.6", "dbus-1"): if subprocess.call ((pkg_config, "--exists", lib)): show_pair (lib, "not found") else: show_pair (lib, stdout (pkg_config, "--modversion", lib)) show_pair ('', stdout (pkg_config, "--cflags", "--libs", lib)) # If the "static" command line argument has been given, stop here. # Else, attempt to display static_info.txt and go on. if static_option: sys.exit (0) print ('') show_pair ("Build time info", static_info) try: with open (static_info, "r" ) as f: for line in f: indent (line) except: indent ("Failed to read build time info.") print ('') kernel_version = stdout ("uname", "-r").rstrip () show_pair ("kernel version", kernel_version) uname_v = stdout ("uname", "-v") show_pair ("Preempt (low latency)", " PREEMPT " in uname_v and not " RT " in uname_v) show_pair ("RT patched", "PREEMPT RT" in uname_v) # Hint: # The main parts of the rt patches are in mainline-kernels nowadays. # Performance with stock kernels is sufficient... fw_devices = glob.glob ("/dev/fw*") show_pair ("/dev/fw*", fw_devices) if fw_devices: indent (stdout (*(["ls", "-lh"] + fw_devices))) show_pair ("User IDs", stdout ("id")) show_pair ("uname -a", stdout ("uname", "-a")) lspci = which ("lspci") if not lspci and os.path.exists ("/sbin/lspci"): lspci = "/sbin/lspci" show_pair ("lspci", lspci) if lspci: for m in re.findall ("^([^ ]*).*1394.*", stdout (lspci), re.MULTILINE): indent (stdout (lspci, "-vv", "-nn", "-s", m)) lscpu = which ("lscpu") show_pair ("lscpu", lscpu) if lscpu: indent (stdout (lscpu)) else: print ("/proc/cpuinfo") with open ("/proc/cpuinfo") as f: for l in f: indent (l) ###################################################################### class IRQ: def __init__(self, number): self.number = number self.pid = "" self.scheduling_class = "" self.scheduling_priority = "" self.drivers = "" self.cpu_counts = "" def description (self): return "IRQ{:>4} PID{:>5} count{:>18} Sched{:>4} priority{:>4} drivers {}"\ .format (self.number, self.pid, self.cpu_counts, self.scheduling_class, self.scheduling_priority, self.drivers) class SoftIRQ: def __init__(self, pid, scheduling_class, scheduling_priority, fullname): self.pid = pid self.fullname = fullname self.scheduling_class = scheduling_class self.scheduling_priority = scheduling_priority def name (self): return "{}-{}".format (self.fullname, self.pid) def description (self): return "SoftIRQ{:>12} PID{:>6} Sched{:>4} priority{:>4}) name softirq-{}"\ .format (self.name (), self.pid, self.scheduling_class, self.scheduling_priority, self.fullname) # get PID info outtext = stdout ("ps", "-eLo", "pid,cmd,class,rtprio") softIRQs = {} for m in re.findall (r"^([0-9]+) +\[softirq-(.*)\] +([A-Z]+) +([-0-9]+)", outtext, re.MULTILINE): irq = SoftIRQ (pid = m.group (1), fullname = m.group (2), scheduling_class = m.group (3), scheduling_priority = m.group (4)) softIRQs [irq.name ()] = irq IRQs = {} for m in re.findall (r"^([0-9]+) +\[IRQ-([0-9]+)\] +([A-Z]{2}) +([-0-9]+)", outtext, re.MULTILINE): irq = IRQ (number = int (m.group (2))) IRQs [irq.number] = irq irq.pid = m.group (1) irq.scheduling_class = m.group (3) irq.scheduling_priority = m.group (4) # get irq info regex_irq = re.compile (r"^ *([0-9]+): *((?:[0-9]+ +)+)(.*)$") with open ("/proc/interrupts") as f: for line in f: m = regex_irq.search (line) if m: irq_number = int (m.group(1)) if irq_number in IRQs: irq = IRQs [irq_number] else: irq = IRQ (number = irq_number) IRQs [irq_number] = irq irq.cpu_counts = ",".join (m.group (2).split ()) irq.drivers = ",".join (m.group (3).split ()) print ("\nHardware interrupts") for _, irq in sorted (IRQs.items ()): indent (irq.description ()) print ("\nSoftware interrupts") for _, irq in sorted (softIRQs.items ()): indent (irq.description ()) print ('') ###################################################################### def module_loaded (module_name, procfile): with open (procfile) as f: for l in f: if module_name in l or module_name.replace ("-", "_") in l: return True return False module_dir = "/lib/modules/" + kernel_version show_pair ("module directory", module_dir) class Stack: def __init__ (self, modules, main_module): self.present = True self.loaded = True for module_name in modules: if module_name in stdout ("find", module_dir, "-name", module_name + ".ko"): indent (module_name + " present") else: indent (module_name + " not present") self.present = False if module_loaded (module_name, "/proc/modules"): indent (module_name + " loaded") else: indent (module_name + " not loaded") self.loaded = False self.active = self.loaded and module_loaded (main_module, "/proc/interrupts") show_pair ("stack active", self.active) self.statically_linked = not self.loaded and os.access ("/sys/module/" + main_module, os.F_OK) show_pair ("statically linked", self.statically_linked) print ("Old 1394 stack") oldstack = Stack (("ieee1394", "ohci1394", "raw1394"), "ohci1394") print ("New 1394 stack") newstack = Stack (("firewire-core", "firewire-ohci"), "firewire-ohci") print ("Kernel support:") if (oldstack.loaded or oldstack.statically_linked) and \ (newstack.loaded or newstack.statically_linked): indent ("""Both old and new FireWire kernel modules are loaded, your system configuration is bogus.""") sys.exit (1) elif newstack.loaded or newstack.statically_linked: indent ("""The new FireWire kernel stack is loaded. If running a kernel earlier than 2.6.37 and problems are experienced, either try with the old FireWire kernel stack or upgrade to a newer kernel (preferrably 2.6.37 or later).""") sys.exit (1) elif oldstack.statically_linked: indent ("[PASS] Kernel drivers statically linked into the kernel.") elif not oldstack.present: indent ("""FireWire kernel module(s) not found. Please ensure that the raw1394 module is loaded.""") sys.exit (1) elif not oldstack.loaded: indent ("""FireWire kernel stack not present. Please compile the kernel with FireWire support.""") sys.exit (1) else: indent ("[PASS] Kernel modules present and correctly loaded.") ###################################################################### print ("/dev/raw1394 devices:") if not os.path.exists ("/dev/raw1394"): indent ("""/dev/raw1394 device node not present. Please fix your udev configuration or add it as a static node.""") sys.exit (1) try: with open ("/dev/raw1394", "w"): pass except: indent ("""Not enough permissions to access /dev/raw1394 device. Please fix your udev configuration, the node permissions or the user/group permissions.""") sys.exit (1) indent ("[PASS] /dev/raw1394 node present and accessible.") libffado-2.4.5/support/tools/ffado-set-nickname.cpp0000644000175000001440000001345114206145246021703 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2012 Bent Bisballe Nyeng * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "devicemanager.h" #include "dice/dice_avdevice.h" using namespace Dice; #include #include #include #include #include #include int run; static void sighandler(int sig) { run = 0; } using namespace std; using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 1000 //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "ffado-set-nickname 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "ffado-set-nickname -- Set device nickname from commandline."; static char args_doc[] = "NODE_ID"; static struct argp_option options[] = { {"nickname", 'N', "NICKNAME", 0, "Set device nickname" }, {"verbose", 'v', "LEVEL", 0, "Produce verbose output" }, {"port", 'p', "PORT", 0, "Set port" }, {"node", 'n', "NODE", 0, "Set node" }, { 0 } }; struct arguments { arguments() : nargs ( 0 ) , verbose( -1/*DEBUG_LEVEL_MESSAGE*/ ) , port( -1 ) , node( -1 ) , counts( -1 ) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; int verbose; int port; int node; int counts; std::string nickname; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': arguments->verbose = strtol(arg, &tail, 0); break; case 'p': arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'n': arguments->node = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'N': arguments->nickname = arg; break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: if(arguments->nargs<0) { printf("not enough arguments\n"); return -1; } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { run=1; signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { printMessage("Could not parse command line\n" ); exit(-1); } errno = 0; DeviceManager *m_deviceManager = new DeviceManager(); if ( !m_deviceManager ) { printMessage("Could not allocate device manager\n" ); return -1; } if ( arguments.verbose ) { m_deviceManager->setVerboseLevel(arguments.verbose); } if ( !m_deviceManager->initialize() ) { printMessage("Could not initialize device manager\n" ); delete m_deviceManager; return -1; } char s[1024]; if(arguments.node > -1) { snprintf(s, 1024, "hw:%d,%d", arguments.port, arguments.node); if ( !m_deviceManager->addSpecString(s) ) { printMessage("Could not add spec string %s to device manager\n", s ); delete m_deviceManager; return -1; } } else { snprintf(s, 1024, "hw:%d", arguments.port); if ( !m_deviceManager->addSpecString(s) ) { printMessage("Could not add spec string %s to device manager\n", s ); delete m_deviceManager; return -1; } } if ( !m_deviceManager->discover(false) ) { printMessage("Could not discover devices\n" ); delete m_deviceManager; return -1; } if(m_deviceManager->getAvDeviceCount() == 0) { printMessage("No devices found\n"); delete m_deviceManager; return -1; } Dice::Device* avDevice = dynamic_cast(m_deviceManager->getAvDeviceByIndex(0)); if(avDevice == NULL) { printMessage("Device is not a DICE device\n" ); delete m_deviceManager; return -1; } if(arguments.nickname != "") { printf("Setting nickname to '%s'\n", arguments.nickname.c_str()); avDevice->setNickname(arguments.nickname); } else { printf("Nickname: '%s'\n", avDevice->getNickname().c_str()); } // cleanup delete m_deviceManager; return 0; } libffado-2.4.5/support/tools/ffado-sandbox-install.py0000644000175000001440000003300113246707102022264 0ustar jwoitheusers#!/usr/bin/python # # # Copyright (C) 2008 Pieter Palmers # # 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, version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # # A script to install FFADO in a 'sandbox', i.e. without changing the # system installation. # import os.path import shutil import subprocess try: raw_input # Error in python3. except: raw_input = input FFADOSBI_VERSION = '0.1' LATEST_FFADO_RELEASE_URL = 'http://www.ffado.org/files/libffado-2.0-beta7.tar.gz' LATEST_FFADO_RELEASE_UNPACK_DIR = 'libffado-2.0-beta7' LATEST_JACK1_RELEASE_URL = 'http://jackaudio.org/downloads/jack-audio-connection-kit-0.109.2.tar.gz' LATEST_JACK1_RELEASE_UNPACK_DIR = 'jack-audio-connection-kit-0.109.2' def ask_for_dir(descr, suggestion): ret_dir = None while True: ret_dir = raw_input("Please specify a %s directory [%s]: " % (descr, suggestion)) if ret_dir == "": ret_dir = suggestion if not os.path.exists(ret_dir): try: os.makedirs(ret_dir) except: yesno = raw_input("Could not create the %s directory. Try again? [yes/no] " % descr) if yesno == "" or yesno[0] != 'y': return None else: continue break else: yesno = raw_input("WARNING: the %s directory at %s already exists. Do you want to overwrite it? [yes/no] " % (descr, ret_dir)) if yesno == "" or yesno[0] != 'y': yesno = raw_input("Specify new %s directory? [yes/no] " % descr) if yesno == "" or yesno[0] != 'y': return None else: continue else: yesno = raw_input("WARNING: about to remove the old %s directory at %s. Proceed? [yes/no] " % (descr, ret_dir)) if yesno == "" or yesno[0] != 'y': yesno = raw_input("Specify new %s directory? [yes/no] " % descr) if yesno == "" or yesno[0] != 'y': return None else: continue else: shutil.rmtree (ret_dir) os.makedirs(ret_dir) break return ret_dir def fetch_source(build_dir, source_descriptor, target): logfile = "%s/%s.log" % (build_dir, target) with open (logfile, 'w') as log: if source_descriptor[1] == 'svn': print( " Checking out SVN repository: %s" % source_descriptor[2] ) cwd = os.getcwd() os.chdir(build_dir) retval = subprocess.call (('svn', 'co', source_descriptor[2], target), stdout=log) os.chdir(cwd) if retval: print( " Failed to checkout the SVN repository. Inspect %s for details. (is subversion installed?)" % logfile ) return False return True elif source_descriptor[1] == 'tar.gz': print( " Downloading tarball: %s" % source_descriptor[2] ) import urllib tmp_file = '%s/tmp.tar.gz' % build_dir try: urllib.urlretrieve(source_descriptor[2], tmp_file) except: print( " Could not retrieve source tarball." ) return False cwd = os.getcwd() os.chdir(build_dir) print( " extracting tarball..." ) retval = subprocess.call (('tar', '-zxf', tmp_file), stdout=log) if retval: print( " Failed to extract the source tarball. Inspect %s for details." % logfile ) os.chdir(cwd) return False if source_descriptor[3]: try: os.rename (source_descriptor[3], target) except: print( " Failed to move the extracted tarball" ) os.chdir(cwd) return False os.chdir(cwd) return True else: print( "bad source type" ) return False welcome_msg = """ FFADO sandbox install utility """ + FFADOSBI_VERSION + """ ================================= (C) 2008 Pieter Palmers This utility will automatically build and install a sandbox environment that can be used to evaluate FFADO. The required FFADO and/or jack packages are downloaded. NOTE: The build dependencies are not automatically installed. Please consult http://subversion.ffado.org/wiki/Dependencies for more information. The subversion tool has to be installed. NOTE: This tool assumes that an internet connection is available while it runs. Once everything is built, no connection is required. You can exit the tool at any time using CTRL-C. """ print( welcome_msg ) # get the paths to be used if 'HOME' in os.environ.keys(): suggestion_sandbox = "%s/ffadosandbox" % os.environ['HOME'] else: suggestion_sandbox = "/home/myuser/ffadosandbox" sandbox_dir_msg = """ SANDBOX DIRECTORY ================= The sandbox directory is the directory where all built files are installed into. It should be writable by your user, and will contain the evaluation binaries if this tool is successful. If it doesn't exist, it will be created. Once you are finished with evaluating FFADO you can simply remove this directory and things are as if nothing happened. Suggestion: %s NOTE: if you specify a system directory here, the tool will install system-wide (if run as root). This is not recommended, but can be useful for automated installs. Uninstall will be a lot harder though. """ % suggestion_sandbox if 'HOME' in os.environ.keys(): suggestion_build = "%s/ffadobuild" % os.environ['HOME'] else: suggestion_build = "/home/myuser/ffadobuild" build_dir_msg = """ BUILD DIRECTORY =============== The build directory is where all temporary files are stored while building FFADO and jack. It should be writable by your user. If it doesn't exist, it will be created. The build directory can be removed as soon as this tool has finished. It is not automatically removed. Suggestion: %s """ % suggestion_build print( sandbox_dir_msg ) sandbox_dir = ask_for_dir('sandbox', suggestion_sandbox) if sandbox_dir == None: print( "Cannot proceed without valid sandbox directory." ) exit(-1) print(" using %s as sandbox directory" % sandbox_dir) print( build_dir_msg ) build_dir = ask_for_dir('build', suggestion_build) if build_dir == None: print( "Cannot proceed without valid build directory." ) exit(-1) print(" using %s as build directory" % build_dir) # figure out what version of FFADO to build # Note: format: # ['title', type=svn or tar.gz, url, (optional) dir that contains target] ffado_versions = {} ffado_versions[0] = ['SVN trunk', 'svn', 'http://subversion.ffado.org/ffado/trunk/libffado', None] ffado_versions[1] = ['SVN libffado-2.0 (recommended)', 'svn', 'http://subversion.ffado.org/ffado/branches/libffado-2.0', None] ffado_versions[2] = ['latest release', 'tar.gz', LATEST_FFADO_RELEASE_URL, LATEST_FFADO_RELEASE_UNPACK_DIR] ffado_versions_msg = """ Available FFADO versions: """ for key in sorted(ffado_versions.keys()): ffado_versions_msg += " %2s: %s\n" % (key, ffado_versions[key][0]) print( ffado_versions_msg ) while True: ffado_version = raw_input("Please select a FFADO version: ") try: ffado_version_int = int (ffado_version) if ffado_version_int not in [0, 1, 2]: raise except: yesno = raw_input("Invalid FFADO version specified. Try again? [yes/no] ") if yesno == "" or yesno[0] != 'y': print( "Cannot proceed without valid FFADO version." ) exit(-1) else: continue break use_ffado_version = ffado_versions[ffado_version_int] # figure out what version of jack to build jack_versions = {} jack_versions[0] = ['jack1 SVN trunk', 'svn', 'http://subversion.jackaudio.org/jack/trunk/jack', None] # note: latest release is no good. #jack_versions[1] = ['jack1 latest release', 'tar.gz', LATEST_JACK1_RELEASE_URL, LATEST_JACK_RELEASE_UNPACK_DIR] jack_versions_msg = """ Available jack versions: """ for key in sorted(jack_versions.keys()): jack_versions_msg += " %2s: %s\n" % (key, jack_versions[key][0]) print( jack_versions_msg ) while True: jack_version = raw_input("Please select a jack version: ") try: jack_version_int = int (jack_version) if jack_version_int not in [0, 1, 2]: raise except: yesno = raw_input("Invalid jack version specified. Try again? [yes/no] ") if yesno == "" or yesno[0] != 'y': print( "Cannot proceed without valid jack version." ) exit(-1) else: continue break use_jack_version = jack_versions[jack_version_int] # print( a summary ) print( """ ) SUMMARY ======= Sandbox directory : %s Build directory : %s FFADO version : %s Jack version : %s """ % (sandbox_dir, build_dir, use_ffado_version[0], use_jack_version[0]) # get the ffado source print( "Fetching FFADO source..." ) if not fetch_source(build_dir, use_ffado_version, 'libffado'): print( "Could not fetch FFADO source" ) exit(-1) print( " Successfully fetched FFADO source" ) print( "Fetching jack source..." ) if not fetch_source(build_dir, use_jack_version, 'jack'): print( "Could not fetch jack source" ) exit(-1) print( " Successfully fetched jack source" ) cwd = os.getcwd() ffado_log = "%s/ffadobuild.log" % build_dir ffado_scons_options = ("-j2",) # TODO: interactive config of the build with open (ffado_log, 'w') as log: # configure FFADO os.chdir("%s/libffado/" % build_dir) print( "Building FFADO..." ) print( " Compiling..." ) if subprocess.call (('scons', 'PREFIX="' + sandbox_dir + '"') + ffado_scons_options, stdout=log): print( """ ) Failed to configure/build FFADO. Most likely this is due to uninstalled dependencies. Check %s for details. """ % ffado_log exit(-1) # install FFADO print( " Installing into %s..." % sandbox_dir ) if subprocess.check_output (('scons', 'install'), stdout=log): print( "Failed to install FFADO. Check %s for details." % ffado_log ) exit(-1) # configure JACK os.chdir("%s/jack/" % build_dir) jack_log = "%s/jackbuild.log" % build_dir with open (jack_log, 'w') as log: log.write ('\n') print( "Building Jack..." ) if use_jack_version[1] == 'svn': print( " Initializing build system..." ) if subprocess.check_output (('./autogen.sh',), stdout=log): print( """ ) Failed to initialize the jack build system. Most likely this is due to uninstalled dependencies. Check %s for details. """ % jack_log exit(-1) print( " Configuring build..." ) if subprocess.check_output (('./configure', '--prefix="' + sandbox_dir + '"'), stdout=log): print( """ ) Failed to configure the jack build. Most likely this is due to uninstalled dependencies. Check %s for details. """ % jack_log exit(-1) # build and install jack print( " Compiling..." ) if subprocess.check_output (('make',), stdout=log): print( "Failed to build jack. Check %s for details." % jack_log ) exit(-1) print( " Installing into %s..." % sandbox_dir ) if subprocess.check_output (('make', 'install'), stdout=log): print( "Failed to install jack. Check %s for details." % jack_log ) exit(-1) # write the bashrc file sandbox_bashrc = """ #!/bin/bash # LD_LIBRARY_PATH="%s/lib:$LD_LIBRARY_PATH" PKG_CONFIG_PATH="%s/lib/pkgconfig:$PKG_CONFIG_PATH" PATH="%s/bin:$PATH" export LD_LIBRARY_PATH export PKG_CONFIG_PATH export PATH """ % (sandbox_dir, sandbox_dir, sandbox_dir) print( "Writing shell configuration file..." ) sandbox_rc_file = "%s/ffado.rc" % sandbox_dir try: with open (sandbox_rc_file, "w") as fid: fid.write(sandbox_bashrc) except: print( "Could not write the sandbox rc file." ) exit(-1) os.chdir(cwd) print( """ ) FFADO and jack are installed into %s. If you want to use the versions in the sandbox, you have to alter your environment such that it uses them instead of the system versions. If you use the bash shell (or compatible) you can use the following rc script: %s. The procedure to use the sandboxed ffado+jack would be: $ source %s $ jackd -R -d firewire If you don't use the bash shell, you have to alter your environment manually. Look at the %s script for inspiration. Note that you have to source the settings script for every terminal you open. If you want a client application to use the sandboxed jack you have to source the settings. E.g.: terminal 1: $ source %s $ jackd -R -d firewire terminal 2: $ source %s $ ardour2 terminal 3: $ source %s $ qjackctl & $ hydrogen If you want to use the sandboxed version as default, you have to ensure that the default environment is changed. This can be done e.g. by adding the settings script to ~/.bashrc. The build directory %s can now be deleted. To uninstall this sandbox, delete the %s directory. """ % (sandbox_dir, sandbox_rc_file, sandbox_rc_file, \ sandbox_rc_file, sandbox_rc_file, sandbox_rc_file, \ sandbox_rc_file, build_dir, sandbox_dir) libffado-2.4.5/support/tools/.gitignore0000644000175000001440000000005612132617070017524 0ustar jwoitheusersffado-diag ffado-set-nickname static_info.txt libffado-2.4.5/support/xdg/0000755000175000001440000000000014206145613015160 5ustar jwoitheuserslibffado-2.4.5/support/xdg/ffado-mixer.appdata.xml0000644000175000001440000000223614206145246021521 0ustar jwoitheusers ffado.org-ffadomixer.desktop CC0-1.0 GPL-2.0 OR GPL-3.0 ffado-mixer A graphical front-end for the FFADO mixer DBus controls exposed by ffado-dbus-server

ffado-mixer presents a graphical application allowing a FireWire audio interface to be controlled. The extent of the control is determined by the level of support for the device in FFADO and in ffado-dbus-server. Typical controls offered by ffado-mixer include faders for the on-board mixer, phantom power control, mode switches and so on.

http://ffado.org/appdata/ffado-mixer-screenshot.png Example of main ffado-mixer window. http://ffado.org/ ffado-devel@lists.sourceforge.net
libffado-2.4.5/support/xdg/ffado.org-ffadomixer.desktop0000644000175000001440000000123014206145246022540 0ustar jwoitheusers[Desktop Entry] Name=FFADO Mixer Name[ru]=Микшер FFADO Name[fr]=Mixeur FFADO Name[nl]=FFADO mixer Comment=Audio mixer for FireWire devices Comment[ru]=Микшер для звуковых устройств с интерфейсом FireWire Comment[fr]=Mixeur audio pour les interfaces FireWire Comment[nl]=Geluidsmixer voor apparaten met FireWire Exec=ffado-mixer GenericName=FireWire audio mixer GenericName[ru]=Звуковой микшер FireWire GenericName[fr]=Mixeur audio FireWire GenericName[nl]=FireWire geluidsmixer Icon=hi64-apps-ffado Type=Application Categories=Mixer;Audio;AudioVideo;HardwareSettings;Qt; Keywords=FireWire;Sound Interface; libffado-2.4.5/support/xdg/hi64-apps-ffado.png0000644000175000001440000000727010767260611020470 0ustar jwoitheusersPNG  IHDR@@iqsRGBbKGD pHYsUtIME /O`8IDATx{\}?>>ݵ.F6PZS< HiJiR %@զHIHD(R5TB p (1~,zYs^T޹s~5QjTըF5QjT#%"Nztto?$5H] 'H{, _s|9lȌPp@kPlhTT8JE3R2Q81U*YL*9_^O)Uo=d}zBRzNӶM*G<2 s/a]"ϑF' ̾WI&X(,`b7(~/ÖT#oYK\A1إked69_Z "zrut +Θ6X"*~$#IQo"eZz4[p }=E[%DZ?{B޵kZ ~dr)0ctC`hNOG/ pوA :U$q ֔]Btfd:,>'zp2 u1sdvX&^w(R ʁ:8Jz:_|? 76Y2'm]GǓop+#F&*1j'AJTT%+6Y )/r!|uMc aN*][5LKb ̎cX]?ˁ/<DDo[[kJ$ 8Jd|_3R0c!4$ *,t^ F8BXF+i_Һ^.~ |%D/PP,hFJz5z =m%~Y#\#4&ꥁA4׏\沦{ߺKQIXϏ~tӸkrs~67L>~݃XI#\QϺ(GHLoѰ!4K#% QB[3 '/3.}+К@.x`H=;#k9ogw㵷T $}Ol1I2(^=͒\ϳxQ H,U`$Ns{oy n#)nzf M۸r"|u?) 9[8 -4޻~ns!~rDkGK@'*DZux8!5˟D-=ss΋'6j G7qX<WB _ƙ f5N=ٓ)DSY=s/OFτ!O0+>U4r N|UXG]G5m| ?Gi]wLj;O0;xޣccVҊm/éeEҷ8tPVy&8&4<̹?[$;(xK2Qd1 D3bTgūX1 tP`IˤM:=:5kt5bq-46; (-0a< $Ȓ -Ypn"Nݪ `6 KV4@n Be$l鍼ò:tŨU²;[Qq0 EGZ 2v$u Ѡ8=wUؚa]kk&^V}wtSC4 ie'^Q tp:4܄}] ]( ibl)Z:liH`. (Q+lZ~֡!rsR#9EӴ J\fW Q&4/X-64ⅣSڏR7J[̙34L`UT\:)&kl*<Ѧ))ND8XP"X%s/Jarl_wCs~QzN W=†{WQߘ.s$}.P=8u*"̎N\#sLEtj:b`،:;c* f0zz!;!?pP3|rԱ4>M_V@ͺv{mh`i3M>apu4 &OxV8!)ik D1Ճ^wIj4DRNXq/ Cڋ E28t,ل!/hqNQa"$#yBMNۢuոVp]X,і6jƱ7 L94Dv.nXWxG ;hRuL+ S,)*-}_1ok5lÿsݠrlTT/,( IO &֢(VF*YS0bkే=˺<Z+̅ #z+7aZ@j7Mf8$~[Fvwb]Q<:gq _7Xk zṷ'"L6Y}޶?]<}z8*veafX-*iˮzh3vy~ff7_~E }HW)Ϥ*MTܯ×yZ=54΍lֶsըF5QjTըF5Qj?ֺiDIENDB`libffado-2.4.5/tests/0000755000175000001440000000000014206145613014024 5ustar jwoitheuserslibffado-2.4.5/tests/SConscript0000644000175000001440000000711614206145246016045 0ustar jwoitheusers# # Copyright (C) 2007-2008 Arnold Krille # Copyright (C) 2007-2008 Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Import( 'env' ) env = env.Clone() env.MergeFlags( "-I#/ -I#/src -L%ssrc -lffado" % env['build_base'] ) if not env.GetOption( "clean" ): env.MergeFlags( "-lpthread" ) env.MergeFlags( env['LIBIEC61883_FLAGS'].decode() ) env.MergeFlags( env['LIBRAW1394_FLAGS'].decode() ) if not env['SERIALIZE_USE_EXPAT']: if 'LIBXML30_FLAGS' in env : env.MergeFlags( env['LIBXML30_FLAGS'].decode() ) if not('LIBXML30_FLAGS' in env) : env.MergeFlags( env['LIBXML26_FLAGS'].decode() ) else: env.PrependUnique( LIBS=["expat"] ) static_env = env.Clone() # # deactivate as they don't seem ported to the new api: test-extplugcmd, # test-mixer, test-volume # apps = { "ffado-test" : "test-ffado.cpp", "test-fw410" : "test-fw410.cpp", #"test-extplugcmd" : "test-extplugcmd.cpp", #"test-mixer" : "test-mixer.cpp", "test-timestampedbuffer" : "test-timestampedbuffer.cpp", "test-ieee1394service" : "test-ieee1394service.cpp", "test-streamdump" : "test-streamdump.cpp", "test-bufferops" : "test-bufferops.cpp", "test-watchdog" : "test-watchdog.cpp", "test-messagequeue" : "test-messagequeue.cpp", "test-shm" : "test-shm.cpp", "test-ipcringbuffer" : "test-ipcringbuffer.cpp", "test-devicestringparser" : "test-devicestringparser.cpp", "dumpiso_mod" : "dumpiso_mod.cpp", "scan-devreg" : "scan-devreg.cpp", "test-cycle-time" : "test-cycle-time.c" } if env['ENABLE_BEBOB']: apps.update( { "test-focusrite" : "test-focusrite.cpp" } ) if env['ENABLE_GENERICAVC']: if 'ALSA_FLAGS' in env and env["ALSA_FLAGS"]: env.MergeFlags( env["ALSA_FLAGS"].decode() ) apps.update( { "test-scs" : "test-scs.cpp" } ) apps.update( { "test-volume" : "test-volume.cpp" } ) apps.update( { "test-enhanced-mixer" : "test-enhanced-mixer.cpp" } ) # MB: utility to unmute M-Audio Ozonic apps.update( { "unmute-ozonic" : "unmute-ozonic.cpp" } ) apps.update( { "test-avccmd" : "test-avccmd.cpp" } ) if env['ENABLE_FIREWORKS']: apps.update( { "test-echomixer" : "test-echomixer.cpp" } ) if env['ENABLE_DICE']: apps.update( { "test-dice-eap" : "test-dice-eap.cpp" } ) apps.update( { "set-default-router-config-dice-eap" : "set-default-router-config-dice-eap.cpp" } ) for app in apps.keys(): env.Program( target=app, source = env.Split( apps[app] ) ) env.Install( "$bindir", app ) env.SConscript( dirs=["streaming", "systemtests"], exports="env" ) # static versions if static_env['BUILD_STATIC_TOOLS']: static_env.Append(LIBS=File('#/src/libffado.a')) for app in apps.keys(): static_app = app + "-static" static_env.Program( target=static_app, source = static_env.Split( apps[app] ) ) libffado-2.4.5/tests/dbus_test.py0000755000175000001440000003023514206145246016402 0ustar jwoitheusers#!/usr/bin/python # # Copyright (C) 2005-2007 by Pieter Palmers # 2007-2008 by Arnold Krille # # 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 sys import os import time import dbus class ControlInterface: def __init__(self, servername, basepath): self.basepath=basepath self.servername=servername self.bus=dbus.SessionBus() def setContignuous(self, subpath, v, idx=None): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Continuous') if idx == None: dev_cont.setValue(v) else: dev_cont.setValueIdx(idx,v) except: print( "Failed to set Continuous %s on server %s" % (path, self.servername) ) def getContignuous(self, subpath, idx=None): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Continuous') if idx == None: return dev_cont.getValue() else: return dev_cont.getValueIdx(idx) except: print( "Failed to get Continuous %s on server %s" % (path, self.servername) ) return 0 def setDiscrete(self, subpath, v): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Discrete') dev_cont.setValue(v) except: print( "Failed to set Discrete %s on server %s" % (path, self.servername) ) def getDiscrete(self, subpath): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Discrete') return dev_cont.getValue() except: print( "Failed to get Discrete %s on server %s" % (path, self.servername) ) return 0 def setRegister(self, subpath, address, value): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Register') dev_cont.setValue(address, value) except: print( "Failed to set Register %s on server %s" % (path, self.servername) ) def getRegister(self, subpath, address): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Register') return dev_cont.getValue(address) except: print( "Failed to get Register %s on server %s" % (path, self.servername) ) return 0 def setText(self, subpath, v): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Text') dev_cont.setValue(v) except: print( "Failed to set Text %s on server %s" % (path, self.servername) ) def getText(self, subpath): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Text') return dev_cont.getValue() except: print( "Failed to get Text %s on server %s" % (path, self.servername) ) return 0 def setMatrixMixerValue(self, subpath, row, col, v): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.MatrixMixer') dev_cont.setValue(row, col, v) except: print( "Failed to set MatrixMixer %s on server %s" % (path, self.servername) ) def getMatrixMixerValue(self, subpath, row, col): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.MatrixMixer') return dev_cont.getValue(row, col) except: print( "Failed to get MatrixMixer %s on server %s" % (path, self.servername) ) return 0 def enumSelect(self, subpath, v): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Enum') dev_cont.select(v) except: print( "Failed to select %s on server %s" % (path, self.servername) ) def enumSelected(self, subpath): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Enum') return dev_cont.selected() except: print( "Failed to get selected enum %s on server %s" % (path, self.servername) ) return 0 def enumGetLabel(self, subpath, v): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Enum') return dev_cont.getEnumLabel(v) except: print( "Failed to get enum label %s on server %s" % (path, self.servername) ) return 0 def enumCount(self, subpath): try: path = self.basepath + subpath dev = self.bus.get_object(self.servername, path) dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Enum') return dev_cont.count() except: print( "Failed to get enum count %s on server %s" % (path, self.servername) ) return 0 class DeviceManagerInterface: def __init__(self, servername, basepath): self.basepath=basepath + '/DeviceManager' self.servername=servername self.bus=dbus.SessionBus() self.dev = self.bus.get_object(self.servername, self.basepath) self.iface = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.Container') # signal reception does not work yet since we need a mainloop for that # and qt3 doesn't provide one for python/dbus #try: #self.dev.connect_to_signal("Updated", self.updateSignal, \ #dbus_interface="org.ffado.Control.Element.Container", arg0=self) #except dbus.DBusException: #traceback.print_exc() #def updateSignal(self): #print( ("Received update signal") ) def getNbDevices(self): return self.iface.getNbElements() def getDeviceName(self, idx): return self.iface.getElementName(idx) class ConfigRomInterface: def __init__(self, servername, devicepath): self.basepath=devicepath + '/ConfigRom' self.servername=servername self.bus=dbus.SessionBus() self.dev = self.bus.get_object(self.servername, self.basepath) self.iface = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.ConfigRomX') def getGUID(self): return self.iface.getGUID() def getVendorName(self): return self.iface.getVendorName() def getModelName(self): return self.iface.getModelName() def getVendorId(self): return self.iface.getVendorId() def getModelId(self): return self.iface.getModelId() def getUnitVersion(self): return self.iface.getUnitVersion() class ClockSelectInterface: def __init__(self, servername, devicepath): self.basepath=devicepath + '/Generic/ClockSelect' self.servername=servername self.bus=dbus.SessionBus() self.dev = self.bus.get_object(self.servername, self.basepath) self.iface = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.AttributeEnum') def count(self): return self.iface.count() def select(self, idx): return self.iface.select(idx) def selected(self): return self.iface.selected() def getEnumLabel(self, idx): return self.iface.getEnumLabel(idx) def attributeCount(self): return self.iface.attributeCount() def getAttributeValue(self, idx): return self.iface.getAttributeValue(idx) def getAttributeName(self, idx): return self.iface.getAttributeName(idx) class TextInterface: def __init__(self, servername, basepath): self.basepath=basepath self.servername=servername self.bus=dbus.SessionBus() self.dev = self.bus.get_object( self.servername, self.basepath ) self.iface = dbus.Interface( self.dev, dbus_interface="org.ffado.Control.Element.Text" ) def text(self): return self.iface.getValue() def setText(self,text): self.iface.setValue(text) if __name__ == "__main__": print( """ ) =========================================================== FFADO DBUS TEST TOOL =========================================================== """ server='org.ffado.Control' basepath='/org/ffado/Control' repeat = 1 while repeat > 0: try: devmgr = DeviceManagerInterface(server, basepath) nbDevices = devmgr.getNbDevices() repeat -= 1 except dbus.DBusException, ex: print( "\n" ) print( "===========================================================" ) print( "ERROR: Could not communicate with the FFADO DBus service..." ) print( "===========================================================" ) print( "\n" ) sys.exit( -1 ) if nbDevices == 0: print( "No supported device found..." ) sys.exit( -1 ) idx = 0 path=devmgr.getDeviceName(idx) print( "Found device %d: %s" % (idx, path) ) cfgrom = ConfigRomInterface(server, basepath+'/DeviceManager/'+path) vendorId = cfgrom.getVendorId() modelId = cfgrom.getModelId() unitVersion = cfgrom.getUnitVersion() GUID = cfgrom.getGUID() vendorName = cfgrom.getVendorName() modelName = cfgrom.getModelName() print( " Found (%s, %X, %X) %s %s" % (str(GUID), vendorId, modelId, vendorName, modelName) ) # create a control objects hw = ControlInterface(server, basepath+'/DeviceManager/'+path) clockselect = ClockSelectInterface( server, basepath+"/DeviceManager/"+path ) nickname = TextInterface( server, basepath+"/DeviceManager/"+path+"/Generic/Nickname" ) import time register = range(60) # do whatever you have to do for i in range(60): for regid in range(60): newval = hw.getRegister("/Register", regid) oldval = register[regid] if newval != oldval: print( "%04d: from %8d | %08X to %8d | %08X" % (regid, oldval, oldval, newval, newval) ) register[regid] = newval time.sleep(0.2) print( "--- %d ---" % i ) #time.sleep(1) #regid=eval(sys.argv[1]) #val=eval(sys.argv[2]) #print( hw.getRegister("/Register", regid) ) #hw.setRegister("/Register", regid, val) libffado-2.4.5/tests/python/0000755000175000001440000000000014206145613015345 5ustar jwoitheuserslibffado-2.4.5/tests/python/test-eap-ctrl.py0000644000175000001440000000563414206145246020415 0ustar jwoitheusers#!/usr/bin/python # # Copyright (C) 2005-2009 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 . # import dbus GUID = "00130e0401400045" servername = "org.ffado.Control" path = "/org/ffado/Control/DeviceManager/%s/EAP/" % GUID bus = dbus.SessionBus() router = dbus.Interface(bus.get_object(servername, path+"Router"), dbus_interface='org.ffado.Control.Element.CrossbarRouter') mixer = dbus.Interface(bus.get_object(servername, path+"MatrixMixer"), dbus_interface='org.ffado.Control.Element.MatrixMixer') # mute the mixer #nbinputs = mixer.getRowCount() #nboutputs = mixer.getColCount() #print( nbinputs ) #print( nboutputs ) #for i in range(nbinputs): #for o in range(nboutputs): #mixer.setValue(i, o, 0) ## reconfigure the router #def reroute(src, dsts): #srcidx = router.getSourceIndex(src) #for dst in dsts: #print( dst ) #dstidx = router.getDestinationIndex(dst) #sourceidx = 0 ## disconnect #while sourceidx >= 0: #sourceidx = router.getSourceForDestination(dstidx) #router.setConnectionState(sourceidx, dstidx, 0) ## connect #if router.canConnect(srcidx, dstidx): #print( router.getConnectionState(srcidx, dstidx) ) #print( router.setConnectionState(srcidx, dstidx, 1) ) #reroute("InS1:02", ["InS0:00", "InS1:00", "InS1:02", "InS1:04", "InS1:06"]) #reroute("InS1:03", ["InS0:01", "InS1:01", "InS1:03", "InS1:05", "InS1:07"]) tst = router.getConnectionMap() nb_sources = router.getNbSources() nb_destinations = router.getNbDestinations() source_names = router.getSourceNames() destination_names = router.getDestinationNames() srcnames_formatted = ["%10s" % s for s in source_names] s = "" for i in range(10): s += " |" for src in range(nb_sources): name = srcnames_formatted[src] char = name[i] s += " %s" % char s += "\n" s += "-----------+" for src in range(nb_sources): s += "--" s += "\n" for dst in range(nb_destinations): s += "%10s |" % destination_names[dst] for src in range(nb_sources): idx = dst * nb_sources + src s += " %d" % tst[idx] s += "\n" print( s ) libffado-2.4.5/tests/python/test-echo-digital.py0000644000175000001440000000365313066160336021236 0ustar jwoitheusers#!/usr/bin/python ''' This test is for checking a function to switch digital interface on devices based on Echo Fireworks. Only two devices support this function, AudioFire8A (as of July 2009) and AudioFirePre8. If no argument given, this script output current status. If one argument given, this script try to set it. The value of argument is: 0: S/PDIF over coaxial digital interface 2: S/PDIF over optical digital interface 3: ADAT over optical digital interface The value '1' seems to be 'ADAT over coaxial digital interface'. But according to users manual, these devices don't support this. I think there is no specification to satisfy 'ADAT over coaxial digital interface'. ''' import sys import dbus # Echo AudioFirePre8 GUID = "0014860a5b6bdb9b" # GUID = "001486085b68e02a" # Echo AudioFire8A # GUID = "001486045B65D6E8" # Echo AudioFire8 servername = "org.ffado.Control" path = "/org/ffado/Control/DeviceManager/%s/" % GUID bus = dbus.SessionBus() # connect to ffado-dbus-server to ask hardware information try: hwinfo = dbus.Interface(bus.get_object(servername, path + 'HwInfo/OpticalInterface'), dbus_interface='org.ffado.Control.Element.Discrete') except: print( "'ffado-dbus-server' should be started in advance." ) sys.exit() # ask the device has optical digital interface try: if (not hwinfo.getValue()): raise Exception() except: print( "Your device just supports coaxial digital interface." ) sys.exit() interface = dbus.Interface(bus.get_object(servername, path + "DigitalInterface"), dbus_interface='org.ffado.Control.Element.Discrete') argvs = sys.argv argc = len(argvs) if (argc > 2): print( "too many arguments" ) sys.exit() elif (argc > 1): param = int(argvs[1]) if ((param == 1) or (param > 3)): print( "wrong argument. it should be:" ) print( "\t0(S/PDIF coaxial), 2(S/PDIF optical), or 3(ADAT optical)" ) else: print( interface.setValue(param) ) else: print( interface.getValue() ) libffado-2.4.5/tests/python/test-echo-routing.py0000644000175000001440000000375013066160336021306 0ustar jwoitheusers#!/usr/bin/python ''' This test is for checking playback-routing of devices based on Echo Fireworks. Only two devices support this function, AudioFire2 and AudioFire4. AudioFire2: Playback stream ch1/2, 3/4, 5/6 can be assigned to analog out ch1/2, headphone out ch1/2, and digital out ch1/2. AudioFire4: Playback stream ch1/2, 3/4, 5/6 can be assigned to analog out ch1/2, 3/4 and digital out ch1/2. Usage: To set routing, give two arguments below. Hardware port id as a first argument. id AudioFire2 AudioFire4 0 Analog out ch1/2 Analog out ch1/2 1 Headphone out ch1/2 Analog out ch4/4 2 Digital out ch1/2 Digital out ch1/2 Playback stream id as a second argument 0 Stream out ch1/2 1 Stream out ch3/4 2 Stream out ch5/6 (shown as digital out) Give no arguments, current state is printed. ''' import sys import dbus GUID = "0014860f5a616e83" # for AudioFire4 #GUID = "001486069D5BD6CA" # for AudioFire2 servername = "org.ffado.Control" path = "/org/ffado/Control/DeviceManager/%s/" % GUID bus = dbus.SessionBus() # connect to ffado-dbus-server to ask hardware information try: hwinfo = dbus.Interface(bus.get_object(servername, path + 'HwInfo/PlaybackRouting'), dbus_interface='org.ffado.Control.Element.Discrete') except: print( "'ffado-dbus-server' should be started in advance." ) sys.exit() # ask the device supports playback-routing try: if(not hwinfo.getValue()): raise Exception() except: print( "Your device doesn't support playback-routing." ) sys.exit() router = dbus.Interface(bus.get_object(servername, path + "PlaybackRouting"), dbus_interface='org.ffado.Control.Element.Discrete') argvs = sys.argv argc = len(argvs) # set status if (argc == 3): port = int(argvs[1]) strm = int(argvs[2]) if ((port > 2) or (strm > 2)): print( 'arguments should be less than 3.' ) sys.exit() s = router.setValueIdx(port, strm); print( s ) # get status else: print( "port\tstream" ) for i in range(3): print( i, "\t", router.getValueIdx(i); ) libffado-2.4.5/tests/scan-devreg.cpp0000644000175000001440000002365314206145246016741 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2008 by Jonathan Woithe * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include #include "libutil/ByteSwap.h" #include "debugmodule/debugmodule.h" #include "ffadodevice.h" // Needed for ffado_smartptr #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/Time.h" #include #include #include #include #include using namespace std; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 1000 #define SCAN_BASE_ADDR 0xfffff0000000ULL // If changing these be sure to update the option help text #define DEFAULT_SCAN_START_REG 0x0b00 #define DEFAULT_SCAN_LENGTH 0x0200 //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "scan-devreg 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "scan-devreg -- scan device registers for changes."; static char args_doc[] = "NODE_ID"; static struct argp_option options[] = { {"verbose", 'v', "LEVEL", 0, "Set verbose level" }, {"port", 'p', "PORT", 0, "Set port" }, {"node", 'n', "NODE", 0, "Set node" }, {"start", 's', "REG", 0, "Register to start scan at (default: 0x0b00)" }, {"length", 'l', "LENGTH", 0, "Number of bytes of register space to scan (default: 0x0200)" }, {"dump", 'd', NULL, 0, "Dump initial register values to stdout" }, { 0 } }; struct arguments { arguments() : nargs ( 0 ) , verbose( 0 ) , test( false ) , port( -1 ) , node ( -1 ) , scan_start ( DEFAULT_SCAN_START_REG ) , scan_length ( DEFAULT_SCAN_LENGTH ) , dump ( 0 ) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; signed int verbose; bool test; signed int port; signed int node; signed int scan_start; signed int scan_length; signed int dump; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; switch (key) { case 'v': errno = 0; arguments->verbose = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 't': arguments->test = true; break; case 's': errno = 0; arguments->scan_start = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } if (arguments->scan_start < 0) { fprintf(stderr, "Start of scan must be >= 0\n"); return -1; } break; case 'l': errno = 0; arguments->scan_length = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } if (arguments->scan_length < 0) { fprintf(stderr, "Scan length must be >= 0\n"); return -1; } break; case 'd': arguments->dump = 1; break; case 'p': errno = 0; arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'n': errno = 0; arguments->node = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { signed int loop = 0; char chr[100]; signed int node_id = -1; signed int port = -1; signed int n_ports = -1; signed int p1, p2, n1, n2; signed int initial_dump; // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { fprintf( stderr, "Could not parse command line\n" ); exit(-1); } errno = 0; setDebugLevel(arguments.verbose); // Do a really basic scan to find an audio device on the bus n_ports = Ieee1394Service::detectNbPorts(); if (arguments.port < 0) { p1 = 0; p2 = n_ports; } else { // Scan a specific port if given on the command line p1 = p2 = arguments.port; p2++; } printf("Scanning %d FireWire adapters (ports)\n", n_ports); for (signed int i=p1; isetVerboseLevel(getDebugLevel()); if ( !tmp1394->initialize(i) ) { delete tmp1394; continue; } if (arguments.node < 0) { n1 = 0; n2 = tmp1394->getNodeCount(); } else { // Scan a specific node if given on the command line n1 = n2 = arguments.node; n2++; } for (fb_nodeid_t node = n1; node < n2; node++) { ffado_smartptr configRom = ffado_smartptr( new ConfigRom(*tmp1394, node)); if (!configRom->initialize()) { continue; } // Assume anything with suitable vendor IDs is an audio device. // Stop at the first audio device found. Currently we detect // only MOTU devices but this can be expanded on an as-needs // basis. if (configRom->getNodeVendorId() == 0x1f2) { node_id = node; port = i; break; } } delete tmp1394; } if (node_id == -1) { printf("Could not find a target audio device on the FireWire bus\n"); return false; } printf("Targetting device at port %d, node %d\n", port, node_id); Ieee1394Service *m_1394Service = new Ieee1394Service(); if ( !m_1394Service ) { debugFatal( "Could not create Ieee1349Service object\n" ); return false; } m_1394Service->setVerboseLevel(getDebugLevel()); if ( !m_1394Service->initialize(port) ) { // This initialise call should never fail since it worked during the // scan. We live in a strange world though, so trap the error // anyway. debugFatal("Could not initialise ieee1394 on MOTU port\n"); delete m_1394Service; return false; } quadlet_t old_vals[arguments.scan_length/4+1], quadlet; char present[arguments.scan_length/(4*32)+1]; memset(old_vals, 0x00, sizeof(old_vals)); // Assume all registers are present until proven otherwise memset(present, 0xff, sizeof(present)); printf("Scanning from %012llX to %012llX (len: %d)\n", SCAN_BASE_ADDR + arguments.scan_start, SCAN_BASE_ADDR + arguments.scan_start + arguments.scan_length, arguments.scan_length); printf("Scanning initial register values, please wait\n"); chr[0] = 0; initial_dump = arguments.dump; while(chr[0]!='q') { for (signed int reg=arguments.scan_start; reg < arguments.scan_start + arguments.scan_length; reg+=4) { unsigned int reg_index = (reg - arguments.scan_start)/4; unsigned int pres_index = (reg - arguments.scan_start)/(4*32); unsigned int pres_bit = ((reg - arguments.scan_start)/4) & 0x1f; if (!(present[pres_index] & (1<read(0xffc0 | node_id, SCAN_BASE_ADDR+reg, 1, &quadlet) <= 0) { // Flag the register as not being present so we don't keep trying to read it present[pres_index] &= ~(1< to scan, \"q\" to exit\n"); fgets(chr, sizeof(chr), stdin); loop++; } delete m_1394Service; return 0; } libffado-2.4.5/tests/set-default-router-config-dice-eap.cpp0000644000175000001440000001635214206145246023204 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include #include #include "debugmodule/debugmodule.h" #include "devicemanager.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/Configuration.h" #include "libcontrol/MatrixMixer.h" #include "libcontrol/CrossbarRouter.h" #include "dice/dice_avdevice.h" #include "dice/dice_eap.h" using namespace Dice; #include #include #include #include #include #include int run; static void sighandler(int sig) { run = 0; } using namespace std; using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 1000 //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "set-default-router-config-dice-eap 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "set-default-router-config-dice-eap -- use set up default router configuration of dice eap code."; static char args_doc[] = "NODE_ID"; static struct argp_option options[] = { {"verbose", 'v', "LEVEL", 0, "Produce verbose output" }, {"port", 'p', "PORT", 0, "Set port" }, {"node", 'n', "NODE", 0, "Set node" }, {"samplerate",'s', "SAMPLERATE", 0, "Prescribe the sample rate"}, { 0 } }; struct arguments { arguments() : nargs ( 0 ) , verbose( DEBUG_LEVEL_NORMAL ) , test( false ) , port( -1 ) , node( -1 ) , samplerate( 48000 ) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; int verbose; bool test; int port; int node; int samplerate; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': arguments->verbose = strtol(arg, &tail, 0); break; case 't': arguments->test = true; break; case 's': arguments->samplerate = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'p': arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'n': arguments->node = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: if(arguments->nargs<0) { printf("not enough arguments\n"); return -1; } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { run=1; signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { printMessage("Could not parse command line\n" ); exit(-1); } errno = 0; DeviceManager *m_deviceManager = new DeviceManager(); if ( !m_deviceManager ) { printMessage("Could not allocate device manager\n" ); return -1; } if ( arguments.verbose ) { m_deviceManager->setVerboseLevel(arguments.verbose); } if ( !m_deviceManager->initialize() ) { printMessage("Could not initialize device manager\n" ); delete m_deviceManager; return -1; } char s[1024]; if(arguments.node > -1) { snprintf(s, 1024, "hw:%d,%d", arguments.port, arguments.node); if ( !m_deviceManager->addSpecString(s) ) { printMessage("Could not add spec string %s to device manager\n", s ); delete m_deviceManager; return -1; } } else { snprintf(s, 1024, "hw:%d", arguments.port); if ( !m_deviceManager->addSpecString(s) ) { printMessage("Could not add spec string %s to device manager\n", s ); delete m_deviceManager; return -1; } } if ( !m_deviceManager->discover(false) ) { printMessage("Could not discover devices\n" ); delete m_deviceManager; return -1; } if(m_deviceManager->getAvDeviceCount() == 0) { printMessage("No devices found\n"); delete m_deviceManager; return -1; } Dice::Device* avDevice = dynamic_cast(m_deviceManager->getAvDeviceByIndex(0)); if(avDevice == NULL) { printMessage("Device is not a DICE device\n" ); delete m_deviceManager; return -1; } // now play //avDevice->setVerboseLevel(DEBUG_LEVEL_VERY_VERBOSE); bool supports_eap = EAP::supportsEAP(*avDevice); if (!supports_eap) { printMessage("EAP not supported on this device\n"); delete m_deviceManager; return -1; } printMessage("device supports EAP\n"); EAP &eap = *(avDevice->getEAP()); // Fix the samplerate if necessary int samplerate = avDevice->getSamplingFrequency(); if (arguments.samplerate != samplerate) { if (avDevice->setSamplingFrequency(arguments.samplerate)) { samplerate = avDevice->getSamplingFrequency(); } else { printMessage("Unable to fix samplerate\n"); delete m_deviceManager; return -1; } } printMessage(" Initial configuration\n"); eap.showFullRouter(); eap.showFullPeakSpace(); // set default router configuration at present sample rate printMessage(" Setting default configuration at samplerate %d\n", samplerate); eap.setupDefaultRouterConfig(); printMessage(" Present configuration\n"); eap.showFullRouter(); eap.showFullPeakSpace(); // cleanup delete m_deviceManager; return 0; } libffado-2.4.5/tests/streaming/0000755000175000001440000000000014206145613016015 5ustar jwoitheuserslibffado-2.4.5/tests/streaming/SConscript0000644000175000001440000000244214206145246020033 0ustar jwoitheusers# # Copyright (C) 2007-2008 Arnold Krille # Copyright (C) 2007-2008 Pieter Palmers # # This file is part of FFADO # FFADO = Free FireWire (pro-)audio drivers for Linux # # FFADO is based upon FreeBoB. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Import( 'env' ) env = env.Clone() env.PrependUnique( CPPPATH=["#/src"] ) env.PrependUnique( LIBPATH=["#/src"] ) env.PrependUnique( LIBS=["ffado"] ) apps = { "ffado-test-streaming" : "teststreaming3.cpp", "ffado-test-streaming-ipc" : "teststreaming-ipc.cpp", "ffado-test-streaming-ipcclient" : "test-ipcclient.cpp", } for app in apps.keys(): env.Program( target=app, source = env.Split( apps[app] ) ) env.Install( "$bindir", app ) libffado-2.4.5/tests/streaming/test-ipcclient.cpp0000644000175000001440000002410114206145246021450 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "debugmodule/debugmodule.h" #include "libutil/IpcRingBuffer.h" #include "libutil/SystemTimeSource.h" #include #include #include #include #include #include using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 2 int run=1; int lastsig=-1; static void sighandler (int sig) { run = 0; } //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-ipcringbuffer 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-avccmd -- test program to test the ipc ringbuffer class."; static char args_doc[] = ""; static struct argp_option options[] = { {"verbose", 'v', "level", 0, "Produce verbose output" }, {"playback", 'o', "count", 0, "Number of playback channels" }, {"capture", 'i', "count", 0, "Number of capture channels" }, {"period", 'p', "frames", 0, "Period size in frames" }, {"nb_buffers", 'n', "count", 0, "Number of IPC buffers" }, { 0 } }; struct arguments { arguments() : nargs ( 0 ) , verbose( false ) , playback( 0 ) , capture( 0 ) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; long int verbose; long int playback; long int capture; long int period; long int nb_buffers; long int test_tone; float test_tone_freq; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': if (arg) { arguments->verbose = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'o': if (arg) { arguments->playback = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'playback' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'i': if (arg) { arguments->capture = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'capture' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'p': if (arg) { arguments->period = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'periods' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'n': if (arg) { arguments->nb_buffers = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'nb_buffers' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: // if(arguments->nargs <= 0) { // printMessage("not enough arguments\n"); // return -1; // } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); arguments.verbose = 6; arguments.period = 1024; arguments.nb_buffers = 3; arguments.playback = 0; arguments.capture = 0; arguments.test_tone_freq = 0.01; arguments.test_tone = 1; // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { fprintf( stderr, "Could not parse command line\n" ); exit(-1); } setDebugLevel(arguments.verbose); if(arguments.playback == 0 && arguments.capture == 0) { debugError("No playback nor capture channels requested\n"); return -1; } printMessage("Testing shared memory streaming IPC\n"); printMessage(" period %ld, nb_buffers %ld, playback %ld, capture %ld\n", arguments.period, arguments.nb_buffers, arguments.playback, arguments.capture ); // prepare the IPC buffers unsigned int capture_buffsize = arguments.capture * arguments.period * 4; unsigned int playback_buffsize = arguments.playback * arguments.period * 4; IpcRingBuffer* capturebuffer = NULL; IpcRingBuffer* playbackbuffer = NULL; if(arguments.playback) { playbackbuffer = new IpcRingBuffer("playbackbuffer", IpcRingBuffer::eBT_Slave, IpcRingBuffer::eD_Outward, IpcRingBuffer::eB_Blocking, arguments.nb_buffers, playback_buffsize); if(playbackbuffer == NULL) { debugError("Could not create playbackbuffer\n"); exit(-1); } if(!playbackbuffer->init()) { debugError("Could not init playbackbuffer\n"); delete playbackbuffer; exit(-1); } playbackbuffer->setVerboseLevel(arguments.verbose); } if(arguments.capture) { capturebuffer = new IpcRingBuffer("capturebuffer", IpcRingBuffer::eBT_Slave, IpcRingBuffer::eD_Inward, IpcRingBuffer::eB_Blocking, arguments.nb_buffers, capture_buffsize); if(capturebuffer == NULL) { debugError("Could not create capturebuffer\n"); delete playbackbuffer; exit(-1); } if(!capturebuffer->init()) { debugError("Could not init capturebuffer\n"); delete playbackbuffer; delete capturebuffer; exit(-1); } capturebuffer->setVerboseLevel(arguments.verbose); } // dummy buffers char capture_buff[capture_buffsize]; char playback_buff[playback_buffsize]; int cnt = 0; int pbkcnt = 0; float frame_counter = 0.0; float sine_advance = 0.0; float amplitude = 0.97; sine_advance = 2.0*M_PI*arguments.test_tone_freq; uint32_t sine_buff[arguments.period]; run=1; while(run) { // test tone generation if (arguments.test_tone) { // generate the test tone for (int i=0; i < arguments.period; i++) { float v = amplitude * sin(sine_advance * (frame_counter + (float)i)); v = (v * 2147483392.0); int32_t tmp = ((int) v); tmp = tmp >> 8; memcpy(&sine_buff[i], &tmp, 4); } for(int j=0; jWrite(playback_buff); if(res != IpcRingBuffer::eR_OK && res != IpcRingBuffer::eR_Again) { debugError("Could not write to segment\n"); goto out_err; } if(res == IpcRingBuffer::eR_Again) { printMessage(" Try playback again on %d...\n", cnt); } else { if(pbkcnt%100==0) { printMessage(" Period %d...\n", pbkcnt); } pbkcnt++; } } // read data if (capturebuffer) { res = capturebuffer->Read(capture_buff); if(res != IpcRingBuffer::eR_OK && res != IpcRingBuffer::eR_Again) { debugError("Could not receive from queue\n"); goto out_err; } if(res == IpcRingBuffer::eR_Again) { printMessage(" Try again on %d...\n", cnt); } else { if(cnt%10==0) { uint32_t *tmp = (uint32_t *)capture_buff; for(int i=0;i. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "src/debugmodule/debugmodule.h" #include "libutil/ByteSwap.h" #include "src/libutil/PosixThread.h" #include "src/libstreaming/util/IsoHandler.h" #include "src/libstreaming/util/StreamProcessorManager.h" #include "src/libstreaming/util/IsoHandlerManager.h" #include "src/libstreaming/amdtp/AmdtpStreamProcessor.h" #include "src/libstreaming/amdtp/AmdtpPort.h" using namespace Streaming; int run; static void sighandler (int sig) { run = 0; } int main(int argc, char *argv[]) { int retval=0; int i=0; run=1; signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); printf("FFADO streaming test application\n"); printf(" ISO handler tests\n"); // also create a processor manager to manage the actual stream // processors StreamProcessorManager *procMan = new StreamProcessorManager(512,3); if(!procMan) { printf("Could not create StreamProcessorManager\n"); return -1; } procMan->setVerboseLevel(DEBUG_LEVEL_VERBOSE); // now we can allocate the stream processors themselves // StreamProcessor *spt = new AmdtpTransmitStreamProcessor(3,2,44100,10); // if(!spt) { // printf("Could not create transmit AmdtpTransmitStreamProcessor\n"); // return -1; // } // spt->setVerboseLevel(DEBUG_LEVEL_VERBOSE); AmdtpReceiveStreamProcessor *spr = new AmdtpReceiveStreamProcessor(2,44100,7); // ReceiveStreamProcessor *spr = new ReceiveStreamProcessor(0,2,44100); if(!spr) { printf("Could not create receive AmdtpStreamProcessor\n"); return -1; } spr->setVerboseLevel(DEBUG_LEVEL_VERBOSE); spr->setChannel(0); AmdtpReceiveStreamProcessor *spr2 = new AmdtpReceiveStreamProcessor(2,44100,11); // ReceiveStreamProcessor *spr = new ReceiveStreamProcessor(0,2,44100); if(!spr2) { printf("Could not create receive AmdtpStreamProcessor\n"); return -1; } spr2->setVerboseLevel(DEBUG_LEVEL_VERBOSE); spr2->setChannel(1); // printf("----------------------\n"); // if (procMan->registerProcessor(spt)) { // printf("Could not register transmit stream processor with the Processor manager\n"); // return -1; // } // printf("----------------------\n"); // also register it with the processor manager, so that it is aware of // buffer sizes etc... if (procMan->registerProcessor(spr)) { printf("Could not register receive stream processor with the Processor manager\n"); return -1; } printf("----------------------\n"); if (procMan->registerProcessor(spr2)) { printf("Could not register receive stream processor with the Processor manager\n"); return -1; } printf("----------------------\n"); AmdtpAudioPort *p1=new AmdtpAudioPort( std::string("Test port 1"), AmdtpAudioPort::E_Capture, 1, 0, AmdtpPortInfo::E_MBLA, 0 ); if (!p1) { printf("Could not create port 1\n"); return -1; } p1->setBufferSize(512); printf("----------------------\n"); if (spr2->addPort(p1)) { printf("Could not register port with receive stream processor\n"); return -1; } Util::PosixThread *thread=new Util::PosixThread(procMan); procMan->prepare(); // start the runner thread->Start(); printf("----------------------\n"); if(procMan->start()) { printf("Could not start handlers\n"); return -1; } printf("----------------------\n"); int periods=0; int periods_print=0; while(run) { periods++; if(periods>periods_print) { printf("\n"); printf("============================================\n"); procMan->dumpInfo(); printf("--------------------------------------------\n"); /* hexDumpQuadlets((quadlet_t*)(p1->getBufferAddress()),10); printf("--------------------------------------------\n");*/ hexDumpQuadlets((quadlet_t*)(p1->getBufferAddress()),10); printf("============================================\n"); printf("\n"); periods_print+=100; } procMan->waitForPeriod(); procMan->transfer(); } thread->Stop(); procMan->stop(); // procMan->unregisterProcessor(spt); procMan->unregisterProcessor(spr); procMan->unregisterProcessor(spr2); delete thread; delete procMan; // delete spt; delete spr; delete spr2; printf("Bye...\n"); return EXIT_SUCCESS; } libffado-2.4.5/tests/streaming/teststreaming-ipc.cpp0000644000175000001440000005250714206145246022176 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** * Test application for the IPC audio server */ #include #include #include #include #include #include "libffado/ffado.h" #include "debugmodule/debugmodule.h" #include "libutil/IpcRingBuffer.h" #include "libutil/SystemTimeSource.h" #include #include int run; DECLARE_GLOBAL_DEBUG_MODULE; using namespace Util; // Program documentation. // Program documentation. static char doc[] = "FFADO -- a driver for FireWire Audio devices (IPC streaming test application)\n\n" ; // A description of the arguments we accept. static char args_doc[] = ""; struct arguments { long int verbose; long int test_tone; long int test_tone_freq; long int period; long int slave_mode; long int snoop_mode; long int nb_buffers; long int sample_rate; long int rtprio; long int audio_buffer_type; long int playback; long int capture; char* args[2]; }; // The options we understand. static struct argp_option options[] = { {"verbose", 'v', "level", 0, "Verbose level" }, {"rtprio", 'P', "prio", 0, "Realtime priority (0 = no RT scheduling)" }, {"test-tone", 't', "bool", 0, "Output test sine" }, {"test-tone-freq", 'f', "hz", 0, "Test sine frequency" }, {"samplerate", 'r', "hz", 0, "Sample rate" }, {"period", 'p', "frames", 0, "Period (buffer) size" }, {"nb_buffers", 'n', "nb", 0, "Nb buffers (periods)" }, {"slave_mode", 's', "bool", 0, "Run in slave mode" }, {"snoop_mode", 'S', "bool", 0, "Run in snoop mode" }, {"audio_buffer_type", 'b', "", 0, "Datatype of audio buffers (0=float, 1=int24)" }, {"playback", 'o', "", 0, "Number of playback channels" }, {"capture", 'i', "", 0, "Number of capture channels" }, { 0 } }; //------------------------------------------------------------- // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': if (arg) { arguments->verbose = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'P': if (arg) { arguments->rtprio = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'rtprio' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'p': if (arg) { arguments->period = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'period' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'n': if (arg) { arguments->nb_buffers = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'nb_buffers' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'r': if (arg) { arguments->sample_rate = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'samplerate' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 't': if (arg) { arguments->test_tone = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'test-tone' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'f': if (arg) { arguments->test_tone_freq = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'test-tone-freq' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'b': if (arg) { arguments->audio_buffer_type = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'audio-buffer-type' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'i': if (arg) { arguments->capture = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'capture' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'o': if (arg) { arguments->playback = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'playback' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 's': if (arg) { arguments->slave_mode = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'slave_mode' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'S': if (arg) { arguments->snoop_mode = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'snoop_mode' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case ARGP_KEY_ARG: break; case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } return 0; } // Our argp parser. static struct argp argp = { options, parse_opt, args_doc, doc }; int set_realtime_priority(unsigned int prio) { debugOutput(DEBUG_LEVEL_NORMAL, "Setting thread prio to %u\n", prio); if (prio > 0) { struct sched_param schp; /* * set the process to realtime privs */ memset(&schp, 0, sizeof(schp)); schp.sched_priority = prio; if (sched_setscheduler(0, SCHED_FIFO, &schp) != 0) { perror("sched_setscheduler"); return -1; } } else { struct sched_param schp; /* * set the process to realtime privs */ memset(&schp, 0, sizeof(schp)); schp.sched_priority = 0; if (sched_setscheduler(0, SCHED_OTHER, &schp) != 0) { perror("sched_setscheduler"); return -1; } } return 0; } static void sighandler (int sig) { run = 0; } int main(int argc, char *argv[]) { struct arguments arguments; // Default values. arguments.verbose = 6; arguments.period = 1024; arguments.slave_mode = 0; arguments.snoop_mode = 0; arguments.nb_buffers = 3; arguments.sample_rate = 44100; arguments.rtprio = 0; arguments.audio_buffer_type = 1; arguments.playback = 0; arguments.capture = 0; // Parse our arguments; every option seen by `parse_opt' will // be reflected in `arguments'. if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { debugError("Could not parse command line\n" ); return -1; } debugOutput(DEBUG_LEVEL_NORMAL, "verbose level = %ld\n", arguments.verbose); setDebugLevel(arguments.verbose); if(arguments.playback == 0 && arguments.capture == 0) { debugError("No playback nor capture channels requested\n"); return -1; } int nb_in_channels=0, nb_out_channels=0; int i=0; int start_flag = 0; int nb_periods=0; uint32_t **audiobuffers_in = NULL; uint32_t **audiobuffers_out = NULL; float *nullbuffer = NULL; run=1; debugOutput(DEBUG_LEVEL_NORMAL, "FFADO streaming test application (3)\n"); printMessage(" period %ld, nb_buffers %ld, playback %ld, capture %ld\n", arguments.period, arguments.nb_buffers, arguments.playback, arguments.capture ); signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); ffado_device_info_t device_info; memset(&device_info,0,sizeof(ffado_device_info_t)); ffado_options_t dev_options; memset(&dev_options,0,sizeof(ffado_options_t)); dev_options.sample_rate = arguments.sample_rate; dev_options.period_size = arguments.period; dev_options.nb_buffers = arguments.nb_buffers; dev_options.realtime = (arguments.rtprio != 0); dev_options.packetizer_priority = arguments.rtprio; dev_options.verbose = arguments.verbose; dev_options.slave_mode = arguments.slave_mode; dev_options.snoop_mode = arguments.snoop_mode; ffado_device_t *dev=ffado_streaming_init(device_info, dev_options); if (!dev) { debugError("Could not init Ffado Streaming layer\n"); exit(-1); } if (arguments.audio_buffer_type == 0) { ffado_streaming_set_audio_datatype(dev, ffado_audio_datatype_float); } else { ffado_streaming_set_audio_datatype(dev, ffado_audio_datatype_int24); } int nb_in_channels_all = ffado_streaming_get_nb_capture_streams(dev); int nb_out_channels_all = ffado_streaming_get_nb_playback_streams(dev); // only do audio for now for (i=0; i < nb_in_channels_all; i++) { switch (ffado_streaming_get_capture_stream_type(dev,i)) { case ffado_stream_type_audio: nb_in_channels++; break; case ffado_stream_type_midi: default: break; } } for (i=0; i < nb_out_channels_all; i++) { switch (ffado_streaming_get_playback_stream_type(dev,i)) { case ffado_stream_type_audio: nb_out_channels++; break; case ffado_stream_type_midi: default: break; } } printMessage("Device channel count: %d capture, %d playback\n", nb_in_channels, nb_out_channels); printMessage("Requested channel count: %ld capture, %ld playback\n", arguments.capture, arguments.playback); if(arguments.playback > nb_out_channels) { debugError("Too many playback channels requested (want: %ld, have:%d)\n", arguments.playback, nb_out_channels); return -1; } if(arguments.capture > nb_in_channels) { debugError("Too many capture channels requested (want: %ld, have:%d)\n", arguments.capture, nb_in_channels); return -1; } printMessage("Buffer size: %d capture, %d playback\n", nb_in_channels*dev_options.period_size * 4, nb_out_channels*dev_options.period_size * 4); // allocate the IPC structures IpcRingBuffer* capturebuffer = NULL; IpcRingBuffer* playbackbuffer = NULL; if(arguments.capture) { // 4 bytes per channel per sample capturebuffer = new IpcRingBuffer("capturebuffer", IpcRingBuffer::eBT_Master, IpcRingBuffer::eD_Outward, IpcRingBuffer::eB_NonBlocking, arguments.nb_buffers, dev_options.period_size * arguments.capture * 4); if(capturebuffer == NULL) { debugError("Could not create capture IPC buffer\n"); exit(-1); } capturebuffer->setVerboseLevel(arguments.verbose); if(!capturebuffer->init()) { debugError("Could not init capture buffer\n"); delete capturebuffer; exit(-1); } // indexes to the memory locations where the frames should be put audiobuffers_in = (uint32_t **)calloc(arguments.capture, sizeof(uint32_t *)); } if(arguments.playback) { // 4 bytes per channel per sample playbackbuffer = new IpcRingBuffer("playbackbuffer", IpcRingBuffer::eBT_Master, IpcRingBuffer::eD_Inward, IpcRingBuffer::eB_NonBlocking, arguments.nb_buffers, dev_options.period_size * arguments.playback * 4); if(playbackbuffer == NULL) { debugError("Could not create playback IPC buffer\n"); exit(-1); } playbackbuffer->setVerboseLevel(arguments.verbose); if(!playbackbuffer->init()) { debugError("Could not init playback buffer\n"); delete capturebuffer; delete playbackbuffer; exit(-1); } // indexes to the memory locations where the frames should be get audiobuffers_out = (uint32_t **)calloc(arguments.playback, sizeof(uint32_t *)); } // this serves in case we miss a cycle, or a channel is disabled nullbuffer = (float *)calloc(arguments.period, sizeof(float)); // give us RT prio set_realtime_priority(arguments.rtprio); // start the streaming layer if (ffado_streaming_prepare(dev)) { debugFatal("Could not prepare streaming system\n"); ffado_streaming_finish(dev); return -1; } start_flag = ffado_streaming_start(dev); // enter the loop debugOutput(DEBUG_LEVEL_NORMAL, "Entering receive loop (IN: %ld, OUT: %ld)\n", arguments.capture, arguments.playback); while(run && start_flag==0) { bool need_silent; enum IpcRingBuffer::eResult msg_res; ffado_wait_response response; response = ffado_streaming_wait(dev); if (response == ffado_wait_xrun) { debugOutput(DEBUG_LEVEL_NORMAL, "Xrun\n"); ffado_streaming_reset(dev); continue; } else if (response == ffado_wait_error) { debugError("fatal xrun\n"); break; } // get a block pointer from the IPC buffer to write if(arguments.capture) { uint32_t *audiobuffers_raw; msg_res = capturebuffer->requestBlockForWrite((void**) &audiobuffers_raw); // pointer voodoo if(msg_res == IpcRingBuffer::eR_OK) { // if we got a valid pointer, setup the stream pointers for (i=0; i < nb_in_channels; i++) { if(i < arguments.capture) { audiobuffers_in[i] = audiobuffers_raw + i*dev_options.period_size; switch (ffado_streaming_get_capture_stream_type(dev,i)) { case ffado_stream_type_audio: /* assign the audiobuffer to the stream */ ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(audiobuffers_in[i])); ffado_streaming_capture_stream_onoff(dev, i, 1); break; // this is done with read/write routines because the nb of bytes can differ. case ffado_stream_type_midi: ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(audiobuffers_in[i])); ffado_streaming_capture_stream_onoff(dev, i, 1); default: break; } } else { ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(nullbuffer)); ffado_streaming_capture_stream_onoff(dev, i, 0); } } need_silent = false; } else { need_silent = true; debugOutput(DEBUG_LEVEL_NORMAL, "CAP: missed period %d\n", nb_periods); } } else { need_silent=true; } if(need_silent) { // if not, use the null buffer for (i=0; i < nb_in_channels; i++) { switch (ffado_streaming_get_capture_stream_type(dev,i)) { case ffado_stream_type_audio: /* assign the audiobuffer to the stream */ ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(nullbuffer)); ffado_streaming_capture_stream_onoff(dev, i, 0); break; // this is done with read/write routines because the nb of bytes can differ. case ffado_stream_type_midi: ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(nullbuffer)); ffado_streaming_capture_stream_onoff(dev, i, 0); default: break; } } } // transfer ffado_streaming_transfer_capture_buffers(dev); if(capturebuffer && !need_silent && msg_res == IpcRingBuffer::eR_OK) { // if we had a good block, release it // FIXME: we should check for errors here capturebuffer->releaseBlockForWrite(); } if(arguments.playback) { uint32_t *audiobuffers_raw; // get a block pointer from the IPC buffer to read msg_res = playbackbuffer->requestBlockForRead((void**) &audiobuffers_raw); // pointer voodoo if(msg_res == IpcRingBuffer::eR_OK) { // if we got a valid pointer, setup the stream pointers for (i=0; i < nb_out_channels; i++) { if(i < arguments.playback) { audiobuffers_out[i] = audiobuffers_raw + i*dev_options.period_size; switch (ffado_streaming_get_playback_stream_type(dev,i)) { case ffado_stream_type_audio: /* assign the audiobuffer to the stream */ ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(audiobuffers_out[i])); ffado_streaming_playback_stream_onoff(dev, i, 1); break; // this is done with read/write routines because the nb of bytes can differ. case ffado_stream_type_midi: ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(audiobuffers_out[i])); ffado_streaming_playback_stream_onoff(dev, i, 1); default: break; } } else { ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(nullbuffer)); ffado_streaming_playback_stream_onoff(dev, i, 0); } } need_silent=false; } else { debugOutput(DEBUG_LEVEL_NORMAL, "PBK: missed period %d\n", nb_periods); need_silent=true; } } else { need_silent=true; } if(need_silent) { // if not, use the null buffer memset(nullbuffer, 0, arguments.period * sizeof(float)); // clean it first for (i=0; i < nb_out_channels; i++) { switch (ffado_streaming_get_playback_stream_type(dev,i)) { case ffado_stream_type_audio: /* assign the audiobuffer to the stream */ ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(nullbuffer)); ffado_streaming_playback_stream_onoff(dev, i, 0); break; // this is done with read/write routines because the nb of bytes can differ. case ffado_stream_type_midi: ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(nullbuffer)); ffado_streaming_playback_stream_onoff(dev, i, 0); default: break; } } } // transfer playback buffers ffado_streaming_transfer_playback_buffers(dev); if(playbackbuffer && !need_silent && msg_res == IpcRingBuffer::eR_OK) { // if we had a good block, release it // FIXME: we should check for errors here playbackbuffer->releaseBlockForRead(); } nb_periods++; } debugOutput(DEBUG_LEVEL_NORMAL, "Exiting receive loop\n"); ffado_streaming_stop(dev); ffado_streaming_finish(dev); delete capturebuffer; delete playbackbuffer; free(nullbuffer); free(audiobuffers_in); free(audiobuffers_out); return EXIT_SUCCESS; } libffado-2.4.5/tests/streaming/teststreaming3.cpp0000644000175000001440000004017114206145246021502 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** * Test application for the direct decode stream API * for floating point use */ #include #include #include #include #include #include #include "libffado/ffado.h" #include "debugmodule/debugmodule.h" #include #include int run; DECLARE_GLOBAL_DEBUG_MODULE; // Program documentation. // Program documentation. static char doc[] = "FFADO -- a driver for FireWire Audio devices (streaming test application)\n\n" ; // A description of the arguments we accept. static char args_doc[] = ""; struct arguments { long int verbose; long int test_tone; long int test_tone_freq; long int period; long int slave_mode; long int snoop_mode; long int nb_buffers; long int sample_rate; long int rtprio; long int audio_buffer_type; char* args[2]; }; // The options we understand. static struct argp_option options[] = { {"verbose", 'v', "level", 0, "Verbose level" }, {"rtprio", 'P', "prio", 0, "Realtime priority (0 = no RT scheduling)" }, {"test-tone", 't', "bool", 0, "Output test sine" }, {"test-tone-freq", 'f', "hz", 0, "Test sine frequency" }, {"samplerate", 'r', "hz", 0, "Sample rate" }, {"period", 'p', "frames", 0, "Period (buffer) size" }, {"nb_buffers", 'n', "nb", 0, "Nb buffers (periods)" }, {"slave_mode", 's', "bool", 0, "Run in slave mode" }, {"snoop_mode", 'S', "bool", 0, "Run in snoop mode" }, {"audio_buffer_type", 'b', "", 0, "Datatype of audio buffers (0=float, 1=int24)" }, { 0 } }; //------------------------------------------------------------- // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': if (arg) { arguments->verbose = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'P': if (arg) { arguments->rtprio = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'rtprio' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'p': if (arg) { arguments->period = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'period' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'n': if (arg) { arguments->nb_buffers = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'nb_buffers' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'r': if (arg) { arguments->sample_rate = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'samplerate' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 't': if (arg) { arguments->test_tone = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'test-tone' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'f': if (arg) { arguments->test_tone_freq = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'test-tone-freq' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'b': if (arg) { arguments->audio_buffer_type = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'audio-buffer-type' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 's': if (arg) { arguments->slave_mode = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'slave_mode' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'S': if (arg) { arguments->snoop_mode = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'snoop_mode' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case ARGP_KEY_ARG: break; case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } return 0; } // Our argp parser. static struct argp argp = { options, parse_opt, args_doc, doc }; int set_realtime_priority(unsigned int prio) { debugOutput(DEBUG_LEVEL_NORMAL, "Setting thread prio to %u\n", prio); if (prio > 0) { struct sched_param schp; /* * set the process to realtime privs */ memset(&schp, 0, sizeof(schp)); schp.sched_priority = prio; if (sched_setscheduler(0, SCHED_FIFO, &schp) != 0) { perror("sched_setscheduler"); return -1; } } else { struct sched_param schp; /* * set the process to realtime privs */ memset(&schp, 0, sizeof(schp)); schp.sched_priority = 0; if (sched_setscheduler(0, SCHED_OTHER, &schp) != 0) { perror("sched_setscheduler"); return -1; } } return 0; } static void sighandler (int sig) { run = 0; } int main(int argc, char *argv[]) { struct arguments arguments; // Default values. arguments.test_tone = 0; arguments.test_tone_freq = 1000; arguments.verbose = 6; arguments.period = 1024; arguments.slave_mode = 0; arguments.snoop_mode = 0; arguments.nb_buffers = 3; arguments.sample_rate = 44100; arguments.rtprio = 0; arguments.audio_buffer_type = 0; // Parse our arguments; every option seen by `parse_opt' will // be reflected in `arguments'. if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { debugError("Could not parse command line\n" ); return -1; } debugOutput(DEBUG_LEVEL_NORMAL, "verbose level = %d\n", (int)arguments.verbose); setDebugLevel(arguments.verbose); int nb_in_channels=0, nb_out_channels=0; int i=0; int start_flag = 0; float frame_counter = 0.0; float sine_advance = 0.0; float amplitude = 0.97; int nb_periods=0; int min_ch_count=0; float **audiobuffers_in; float **audiobuffers_out; float *nullbuffer; run=1; debugOutput(DEBUG_LEVEL_NORMAL, "FFADO streaming test application (3)\n"); signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); ffado_device_info_t device_info; memset(&device_info,0,sizeof(ffado_device_info_t)); ffado_options_t dev_options; memset(&dev_options,0,sizeof(ffado_options_t)); dev_options.sample_rate = arguments.sample_rate; dev_options.period_size = arguments.period; dev_options.nb_buffers = arguments.nb_buffers; dev_options.realtime = (arguments.rtprio != 0); dev_options.packetizer_priority = arguments.rtprio; dev_options.verbose = arguments.verbose; dev_options.slave_mode = arguments.slave_mode; dev_options.snoop_mode = arguments.snoop_mode; sine_advance = 2.0*M_PI*arguments.test_tone_freq/((float)dev_options.sample_rate); ffado_device_t *dev=ffado_streaming_init(device_info, dev_options); if (!dev) { debugError("Could not init Ffado Streaming layer\n"); exit(-1); } if (arguments.audio_buffer_type == 0) { ffado_streaming_set_audio_datatype(dev, ffado_audio_datatype_float); } else { ffado_streaming_set_audio_datatype(dev, ffado_audio_datatype_int24); } nb_in_channels = ffado_streaming_get_nb_capture_streams(dev); nb_out_channels = ffado_streaming_get_nb_playback_streams(dev); if (nb_in_channels < nb_out_channels) { min_ch_count = nb_in_channels; } else { min_ch_count = nb_out_channels; } /* allocate intermediate buffers */ audiobuffers_in = (float **)calloc(nb_in_channels, sizeof(float *)); for (i=0; i < nb_in_channels; i++) { audiobuffers_in[i] = (float *)calloc(arguments.period+1, sizeof(float)); switch (ffado_streaming_get_capture_stream_type(dev,i)) { case ffado_stream_type_audio: /* assign the audiobuffer to the stream */ ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(audiobuffers_in[i])); ffado_streaming_capture_stream_onoff(dev, i, 1); break; // this is done with read/write routines because the nb of bytes can differ. case ffado_stream_type_midi: // note that using a float * buffer for midievents is a HACK ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(audiobuffers_in[i])); ffado_streaming_capture_stream_onoff(dev, i, 1); default: break; } } audiobuffers_out = (float **)calloc(nb_out_channels, sizeof(float *)); for (i=0; i < nb_out_channels; i++) { audiobuffers_out[i] = (float *)calloc(arguments.period+1, sizeof(float)); switch (ffado_streaming_get_playback_stream_type(dev,i)) { case ffado_stream_type_audio: /* assign the audiobuffer to the stream */ ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(audiobuffers_out[i])); ffado_streaming_playback_stream_onoff(dev, i, 1); break; // this is done with read/write routines because the nb of bytes can differ. case ffado_stream_type_midi: ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(audiobuffers_out[i])); ffado_streaming_playback_stream_onoff(dev, i, 1); default: break; } } nullbuffer = (float *)calloc(arguments.period+1, sizeof(float)); // /* open the files to write to*/ // FILE* fid_in[nb_in_channels]; // char name[256]; // // for (i=0;i> 8; memcpy(&nullbuffer[i], &tmp, sizeof(float)); } } // copy the test tone to the audio buffers for (i=0; i < nb_out_channels; i++) { if (ffado_streaming_get_playback_stream_type(dev,i) == ffado_stream_type_audio) { memcpy((char *)(audiobuffers_out[i]), (char *)(nullbuffer), sizeof(float) * arguments.period); } } } else { uint32_t *midibuffer; int idx; for (i=0; i < min_ch_count; i++) { switch (ffado_streaming_get_capture_stream_type(dev,i)) { case ffado_stream_type_audio: // if both channels are audio channels, copy the buffers if (ffado_streaming_get_playback_stream_type(dev,i) == ffado_stream_type_audio) { memcpy((char *)(audiobuffers_out[i]), (char *)(audiobuffers_in[i]), sizeof(float) * arguments.period); } break; // this is done with read/write routines because the nb of bytes can differ. case ffado_stream_type_midi: midibuffer=(uint32_t *)audiobuffers_in[i]; for(idx=0; idx < arguments.period; idx++) { uint32_t midievent = *(midibuffer + idx); if(midievent & 0xFF000000) { debugOutput(DEBUG_LEVEL_NORMAL, " Received midi event %08X at idx %d of period %d on port %d\n", midievent, idx, nb_periods, i); } } default: break; } } for (i=0; i < nb_out_channels; i++) { if (ffado_streaming_get_playback_stream_type(dev,i) == ffado_stream_type_midi) { uint32_t *midievent = (uint32_t *)audiobuffers_out[i]; *midievent = 0x010000FF; break; } } } ffado_streaming_transfer_playback_buffers(dev); nb_periods++; frame_counter += arguments.period; } debugOutput(DEBUG_LEVEL_NORMAL, "Exiting receive loop\n"); ffado_streaming_stop(dev); ffado_streaming_finish(dev); // for (i=0;i. # Import( 'env' ) env = env.Clone() env.PrependUnique( CPPPATH=["#/src"] ) env.PrependUnique( LIBPATH=[env['build_base']+"src"] ) env.PrependUnique( LIBS=["ffado"] ) if not env.GetOption( "clean" ): env.MergeFlags( env['LIBRAW1394_FLAGS'].decode() ) env.MergeFlags( "-lrt -lpthread" ) static_env = env.Clone() # # deactivate as they don't seem ported to the new api: test-extplugcmd, # test-mixer, test-volume # apps = { "ffado-test-isorecv" : ["test-isorecv-1.cpp", "realtimetools.cpp"], "ffado-test-isoxmit" : ["test-isoxmit-1.cpp", "realtimetools.cpp"], "test-sysload" : ["test-sysload.cpp", "realtimetools.cpp"], "gen-loadpulses" : ["gen-loadpulses.cpp", "realtimetools.cpp"], "test-clock_nanosleep" : ["test-clock_nanosleep.cpp", "realtimetools.cpp"], } for app in apps.keys(): env.Program( target=app, source = env.Split( apps[app] ) ) env.Install( "$bindir", app ) libffado-2.4.5/tests/systemtests/gen-loadpulses.cpp0000644000175000001440000001354614206145246022074 0ustar jwoitheusers/* * Parts Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * based on howdyget.c (unknown source, maybe Maas Digital LLC) */ #include #include #include #include #include #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include #include "debugmodule/debugmodule.h" #include "realtimetools.h" #include uint32_t count = 0; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_EXTRA_ARGS 2 // Program documentation. // Program documentation. static char doc[] = "FFADO -- System Load pulse generator\n\n"; // A description of the arguments we accept. static char args_doc[] = ""; struct arguments { long int verbose; long int rtprio; long int period; long int duration; long int countdown; char* args[MAX_EXTRA_ARGS]; }; // The options we understand. static struct argp_option options[] = { {"verbose", 'v', "level", 0, "Verbose level" }, {"rtprio", 'P', "prio", 0, "real time priority of the iterator process/thread (0 = no RT)" }, {"period", 'p', "usecs", 0, "period (in usecs)" }, {"duration", 'd', "usecs", 0, "pulse duration (in usecs)" }, {"countdown", 'u', "count", 0, "number of pulses to run" }, { 0 } }; // Parse a single option. #define PARSE_ARG_LONG(XXletterXX, XXvarXX, XXdescXX) \ case XXletterXX: \ if (arg) { \ XXvarXX = strtol( arg, &tail, 0 ); \ if ( errno ) { \ fprintf( stderr, "Could not parse '%s' argument\n", XXdescXX ); \ return ARGP_ERR_UNKNOWN; \ } \ } \ break; static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { PARSE_ARG_LONG('v', arguments->verbose, "verbose"); PARSE_ARG_LONG('P', arguments->rtprio, "rtprio"); PARSE_ARG_LONG('p', arguments->period, "period"); PARSE_ARG_LONG('d', arguments->duration, "duration"); PARSE_ARG_LONG('u', arguments->countdown, "countdown"); case ARGP_KEY_ARG: break; case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } return 0; } // Our argp parser. static struct argp argp = { options, parse_opt, args_doc, doc }; // the global arguments struct struct arguments arguments; // signal handler int run; static void sighandler (int sig) { run = 0; } // the load function float global; void load_function() { int cnt = 10; while(cnt--) { int global_int = (int)global; global = global / 7.0; global_int++; global += (float)global_int; } } int main(int argc, char **argv) { // register signal handler run = 1; signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); // Default values. arguments.verbose = DEBUG_LEVEL_VERBOSE; arguments.rtprio = 0; arguments.countdown = 1000; arguments.period = 1000; arguments.duration = 100; // Parse our arguments; every option seen by `parse_opt' will // be reflected in `arguments'. if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { debugError("Could not parse command line\n" ); return -1; } debugOutput(DEBUG_LEVEL_INFO, "System Load pulse generator\n"); debugOutput(DEBUG_LEVEL_INFO, " Arguments:\n"); debugOutput(DEBUG_LEVEL_INFO, " RT priority : %ld\n", arguments.rtprio); debugOutput(DEBUG_LEVEL_INFO, " Countdown : %ld\n", arguments.countdown); debugOutput(DEBUG_LEVEL_INFO, " Period : %ld usec\n", arguments.period); debugOutput(DEBUG_LEVEL_INFO, " Pulse duration : %ld usec\n", arguments.duration); debugOutput(DEBUG_LEVEL_INFO, "Setting RT priority (%ld)...\n", arguments.rtprio); set_realtime_priority(arguments.rtprio); debugOutput(DEBUG_LEVEL_INFO, "Starting iterate loop...\n"); flushDebugOutput(); int countdown = arguments.countdown; uint64_t sleep_time = rt_gettime_usecs(); while(countdown-- && run) { // figure out when to stop calling the load function uint64_t run_until = sleep_time + arguments.duration; while(rt_gettime_usecs() < run_until) load_function(); // now wait for the period to end sleep_time += arguments.period; rt_sleep_absolute_usecs(sleep_time); // check if we are late uint64_t toc = rt_gettime_usecs(); int64_t usecs_late = toc - sleep_time; if(usecs_late > 1000) { debugWarning("late wakeup: %" PRId64 " usecs\n", usecs_late); } // try and detect lockup () if(usecs_late > 100000) { debugWarning("very late wakeup: %" PRId64 " usecs\n", usecs_late); // force exit, since this is a loop out of control run=0; } } if(run) { debugOutput(DEBUG_LEVEL_INFO, "Clean exit...\n"); } else { debugOutput(DEBUG_LEVEL_INFO, "Forced exit...\n"); } return 0; } libffado-2.4.5/tests/systemtests/realtimetools.cpp0000644000175000001440000000500014206145246022017 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "realtimetools.h" #include "debugmodule/debugmodule.h" #include #include #include #include // needed for clock_nanosleep #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include DECLARE_GLOBAL_DEBUG_MODULE; int set_realtime_priority(unsigned int prio) { debugOutput(DEBUG_LEVEL_NORMAL, "Setting thread prio to %u\n", prio); if (prio > 0) { struct sched_param schp; /* * set the process to realtime privs */ memset(&schp, 0, sizeof(schp)); schp.sched_priority = prio; if (sched_setscheduler(0, SCHED_FIFO, &schp) != 0) { perror("sched_setscheduler"); return -1; } } else { struct sched_param schp; /* * set the process to realtime privs */ memset(&schp, 0, sizeof(schp)); schp.sched_priority = 0; if (sched_setscheduler(0, SCHED_OTHER, &schp) != 0) { perror("sched_setscheduler"); return -1; } } return 0; } void rt_sleep_relative_usecs(uint64_t usecs) { //usleep(usecs); struct timespec ts; ts.tv_sec = usecs / (1000000LL); ts.tv_nsec = (usecs % (1000000LL)) * 1000LL; clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL); } void rt_sleep_absolute_usecs(uint64_t wake_at_usec) { struct timespec ts; ts.tv_sec = wake_at_usec / (1000000LL); ts.tv_nsec = (wake_at_usec % (1000000LL)) * 1000LL; clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL); } uint64_t rt_gettime_usecs() { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return (uint64_t)(ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL); } libffado-2.4.5/tests/systemtests/realtimetools.h0000644000175000001440000000210314206145246021465 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __REALTIMETOOLS_H__ #define __REALTIMETOOLS_H__ #include int set_realtime_priority(unsigned int prio); void rt_sleep_relative_usecs(uint64_t usecs); void rt_sleep_absolute_usecs(uint64_t wake_at); uint64_t rt_gettime_usecs(); #endif libffado-2.4.5/tests/systemtests/test-clock_nanosleep.cpp0000644000175000001440000001012514206145246023254 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "debugmodule/debugmodule.h" #include "libutil/Time.h" // needed for clock_nanosleep #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include DECLARE_GLOBAL_DEBUG_MODULE; #define RUNTIME_USECS 5000000 #define USE_ABSOLUTE_NANOSLEEP 1 #define SLEEP_PERIOD_USECS 200000LL #define NB_THREADS 2 uint64_t getCurrentTimeAsUsecs() { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return (uint64_t)(ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL); } void SleepUsecRelative(uint64_t usecs) { //usleep(usecs); struct timespec ts; ts.tv_sec = usecs / (1000000LL); ts.tv_nsec = (usecs % (1000000LL)) * 1000LL; clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL); } void SleepUsecAbsolute(uint64_t wake_at_usec) { #if USE_ABSOLUTE_NANOSLEEP struct timespec ts; ts.tv_sec = wake_at_usec / (1000000LL); ts.tv_nsec = (wake_at_usec % (1000000LL)) * 1000LL; debugOutput(DEBUG_LEVEL_VERBOSE, "clock_nanosleep until %" PRId64 " sec, %" PRId64 " nanosec\n", (int64_t)ts.tv_sec, (int64_t)ts.tv_nsec); int err = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL); debugOutput(DEBUG_LEVEL_VERBOSE, "back with err=%d\n", err); #else uint64_t to_sleep = wake_at_usec - getCurrentTimeAsUsecs(); SleepUsecRelative(to_sleep); #endif } struct thread_args { bool fRunning; int fCancellation; }; void* thread_function(void* arg) { struct thread_args* obj = (struct thread_args*)arg; int err; if ((err = pthread_setcanceltype(obj->fCancellation, NULL)) != 0) { debugError("pthread_setcanceltype err = %s\n", strerror(err)); } debugOutput( DEBUG_LEVEL_VERBOSE, "start %p\n", obj); uint64_t now = getCurrentTimeAsUsecs(); uint64_t wake_at = now += SLEEP_PERIOD_USECS; // If Init succeed start the thread loop while (obj->fRunning) { debugOutput( DEBUG_LEVEL_VERBOSE, "run %p\n", obj); SleepUsecAbsolute(wake_at); wake_at += SLEEP_PERIOD_USECS; debugOutput(DEBUG_LEVEL_VERBOSE, "cuckoo from %p at %012" PRI_FFADO_MICROSECS_T "\n", obj, getCurrentTimeAsUsecs()); pthread_testcancel(); } debugOutput( DEBUG_LEVEL_VERBOSE, "exit %p\n", obj); return 0; } int main() { setDebugLevel(DEBUG_LEVEL_VERBOSE); struct thread_args args[NB_THREADS]; pthread_t threads[NB_THREADS]; int res=0; for (int i=0; i. * */ /* * based on howdyget.c (unknown source, maybe Maas Digital LLC) */ #include #include #include #include #include #include "debugmodule/debugmodule.h" #include "realtimetools.h" #include uint32_t count = 0; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_EXTRA_ARGS 2 // Program documentation. // Program documentation. static char doc[] = "FFADO -- ISO receive test\n\n"; // A description of the arguments we accept. static char args_doc[] = ""; struct arguments { long int verbose; long int port; long int channel; long int packetsize; long int buffersize; long int interval; long int countdown; long int startoncycle; long int printinterval; long int rtprio; char* args[MAX_EXTRA_ARGS]; }; // The options we understand. static struct argp_option options[] = { {"verbose", 'v', "level", 0, "Verbose level" }, {"port", 'p', "port", 0, "FireWire port to use" }, {"channel", 'c', "channel", 0, "iso channel to use" }, {"packetsize", 's', "packetsize", 0, "packet size to use" }, {"buffersize", 'b', "buffersize", 0, "number of packets to buffer" }, {"interval", 'i', "interval", 0, "interrupt interval in packets" }, {"startoncycle", 't', "cycle", 0, "start on a specific cycle" }, {"countdown", 'u', "count", 0, "number of iterations to run" }, {"printinterval", 'r', "packets", 0, "number of packets between successive status prints" }, {"rtprio", 'P', "prio", 0, "real time priority of the iterator process/thread (0 = no RT)" }, { 0 } }; // Parse a single option. #define PARSE_ARG_LONG(XXletterXX, XXvarXX, XXdescXX) \ case XXletterXX: \ if (arg) { \ XXvarXX = strtol( arg, &tail, 0 ); \ if ( errno ) { \ fprintf( stderr, "Could not parse '%s' argument\n", XXdescXX ); \ return ARGP_ERR_UNKNOWN; \ } \ } \ break; static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { PARSE_ARG_LONG('v', arguments->verbose, "verbose"); PARSE_ARG_LONG('p', arguments->port, "port"); PARSE_ARG_LONG('c', arguments->channel, "channel"); PARSE_ARG_LONG('s', arguments->packetsize, "packetsize"); PARSE_ARG_LONG('b', arguments->buffersize, "buffersize"); PARSE_ARG_LONG('i', arguments->interval, "interval"); PARSE_ARG_LONG('u', arguments->countdown, "countdown"); PARSE_ARG_LONG('r', arguments->printinterval, "printinterval"); PARSE_ARG_LONG('t', arguments->startoncycle, "startoncycle"); PARSE_ARG_LONG('P', arguments->rtprio, "rtprio"); case ARGP_KEY_ARG: break; case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } return 0; } // Our argp parser. static struct argp argp = { options, parse_opt, args_doc, doc }; // the global arguments struct struct arguments arguments; int last_cycle = -1; static enum raw1394_iso_disposition myISOGetter(raw1394handle_t handle, unsigned char *data, unsigned int len, unsigned char channel, unsigned char tag, unsigned char sy, unsigned int cycle, unsigned int dropped1) { unsigned int skipped = (dropped1 & 0xFFFF0000) >> 16; unsigned int dropped = dropped1 & 0xFFFF; //debugOutput(DEBUG_LEVEL_INFO, "In handler\n"); //sprintf((char *)data, "Hello World %8u\n", count); if (last_cycle >= 0) { if (cycle != ((unsigned int)last_cycle + 1) % 8000) { debugOutput(DEBUG_LEVEL_INFO, "nonmonotonic cycles: this: %04d, last: %04d, dropped: 0x%08X\n", cycle, last_cycle, dropped1); } } last_cycle = cycle; count++; if(dropped) debugOutput(DEBUG_LEVEL_INFO, "Dropped packets! (%d)\n", dropped); if(skipped) debugOutput(DEBUG_LEVEL_INFO, "Skipped packets! (%d)\n", skipped); if (count % arguments.printinterval == 0) debugOutput(DEBUG_LEVEL_INFO, "cycle=%d count=%d\n", cycle, count); return RAW1394_ISO_OK; } int main(int argc, char **argv) { raw1394handle_t handle; // Default values. arguments.verbose = DEBUG_LEVEL_VERBOSE; arguments.port = 0; arguments.channel = 0; arguments.packetsize = 1024; arguments.buffersize = 100; arguments.interval = -1; arguments.startoncycle = -1; arguments.countdown = 10000; arguments.printinterval = 100; arguments.rtprio = 0; // Parse our arguments; every option seen by `parse_opt' will // be reflected in `arguments'. if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { debugError("Could not parse command line\n" ); return -1; } debugOutput(DEBUG_LEVEL_INFO, "Get 1394 handle...\n"); handle = raw1394_new_handle(); if(!handle) { perror("raw1394_new_handle"); return -1; } debugOutput(DEBUG_LEVEL_INFO, "Select 1394 port %d...\n", (int) arguments.port); int ret = -1; do { if (raw1394_get_port_info(handle, NULL, 0) < 0) { perror("raw1394_get_port_info"); return 1; } ret = raw1394_set_port(handle, arguments.port); } while (ret != 0 && errno == ESTALE); if(ret != 0 && errno) { perror("raw1394_set_port"); return 1; } debugOutput(DEBUG_LEVEL_INFO, "Init ISO receive...\n"); if(raw1394_iso_recv_init(handle, myISOGetter, arguments.buffersize, arguments.packetsize, arguments.channel, RAW1394_DMA_DEFAULT, arguments.interval)) { perror("raw1394_iso_recv_init"); return 1; } debugOutput(DEBUG_LEVEL_INFO, "Start ISO receive...\n"); if(raw1394_iso_recv_start(handle, arguments.startoncycle, 0xF, 0)) { perror("raw1394_iso_recv_start"); return 1; } debugOutput(DEBUG_LEVEL_INFO, "Setting RT priority (%d)...\n", (int)arguments.rtprio); set_realtime_priority(arguments.rtprio); debugOutput(DEBUG_LEVEL_INFO, "Starting iterate loop...\n"); int countdown = arguments.countdown; while(countdown--) { if(raw1394_loop_iterate(handle)) { perror("raw1394_loop_iterate"); return 1; } } return 0; } libffado-2.4.5/tests/systemtests/test-isoxmit-1.cpp0000644000175000001440000002121714206145246021753 0ustar jwoitheusers/* * Parts Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * ISO xmit stall test * This test stalls on certain controllers from a packet size = 225 on * * Controllers affected: * O2 Micro, Inc. FireWire (IEEE 1394) (rev 02) * Texas Instruments XIO2200(A) IEEE-1394a-2000 Controller (PHY/Link) (rev 01) * not affected: * NEC Corporation uPD72874 IEEE1394 OHCI 1.1 3-port PHY-Link Ctrlr (rev 01) * * gcc -g -O2 -Wall -D_REENTRANT -std=c99 -D_GNU_SOURCE -o testxmitstall testxmitstall.c -lm -lpthread -lraw1394 * */ /* * based on howdysend.c (unknown source, maybe Maas Digital LLC) */ #include #include #include #include #include #include #include #include "debugmodule/debugmodule.h" #include "realtimetools.h" #define SPEED RAW1394_ISO_SPEED_400 uint32_t count = 0; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_EXTRA_ARGS 2 // Program documentation. // Program documentation. static char doc[] = "FFADO -- ISO transmit stall test\n\n"; // A description of the arguments we accept. static char args_doc[] = ""; struct arguments { long int verbose; long int port; long int channel; long int packetsize; long int buffersize; long int interval; long int prebuffers; long int startoncycle; long int countdown; long int printinterval; long int rtprio; char* args[MAX_EXTRA_ARGS]; }; // The options we understand. static struct argp_option options[] = { {"verbose", 'v', "level", 0, "Verbose level" }, {"port", 'p', "port", 0, "FireWire port to use" }, {"channel", 'c', "channel", 0, "iso channel to use" }, {"packetsize", 's', "packetsize", 0, "packet size to use" }, {"buffersize", 'b', "buffersize", 0, "number of packets to buffer" }, {"interval", 'i', "interval", 0, "interrupt interval in packets" }, {"prebuffers", 'x', "packets", 0, "number of packets to prebuffer" }, {"startoncycle", 't', "cycle", 0, "start on a specific cycle" }, {"countdown", 'u', "count", 0, "number of iterations to run" }, {"printinterval", 'r', "packets", 0, "number of packets between successive status prints" }, {"rtprio", 'P', "prio", 0, "real time priority of the iterator process/thread (0 = no RT)" }, { 0 } }; // Parse a single option. #define PARSE_ARG_LONG(XXletterXX, XXvarXX, XXdescXX) \ case XXletterXX: \ if (arg) { \ XXvarXX = strtol( arg, &tail, 0 ); \ if ( errno ) { \ fprintf( stderr, "Could not parse '%s' argument\n", XXdescXX ); \ return ARGP_ERR_UNKNOWN; \ } \ } \ break; static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { PARSE_ARG_LONG('v', arguments->verbose, "verbose"); PARSE_ARG_LONG('p', arguments->port, "port"); PARSE_ARG_LONG('c', arguments->channel, "channel"); PARSE_ARG_LONG('s', arguments->packetsize, "packetsize"); PARSE_ARG_LONG('b', arguments->buffersize, "buffersize"); PARSE_ARG_LONG('i', arguments->interval, "interval"); PARSE_ARG_LONG('x', arguments->prebuffers, "prebuffers"); PARSE_ARG_LONG('t', arguments->startoncycle, "startoncycle"); PARSE_ARG_LONG('u', arguments->countdown, "countdown"); PARSE_ARG_LONG('r', arguments->printinterval, "printinterval"); PARSE_ARG_LONG('P', arguments->rtprio, "rtprio"); case ARGP_KEY_ARG: break; case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } return 0; } // Our argp parser. static struct argp argp = { options, parse_opt, args_doc, doc }; // the global arguments struct struct arguments arguments; int32_t last_cycle = -1; static enum raw1394_iso_disposition myISOSender(raw1394handle_t handle, unsigned char *data, uint32_t *len, unsigned char *tag, unsigned char *sy, int32_t cycle, uint32_t dropped1) { unsigned int skipped = (dropped1 & 0xFFFF0000) >> 16; unsigned int dropped = dropped1 & 0xFFFF; //debugOutput(DEBUG_LEVEL_INFO, "In handler\n"); sprintf((char *)data, "Hello World %8u\n", count); if (last_cycle >= 0) { if (cycle != (last_cycle + 1) % 8000) { debugOutput(DEBUG_LEVEL_INFO, "nonmonotonic cycles: this: %04d, last: %04d, dropped: 0x%08X\n", cycle, last_cycle, dropped1); } } last_cycle = cycle; count++; *len = arguments.packetsize; *tag = 1; *sy = 0; if(dropped) debugOutput(DEBUG_LEVEL_INFO, "Dropped packets! (%d)\n", dropped); if(skipped) debugOutput(DEBUG_LEVEL_INFO, "Skipped packets! (%d)\n", skipped); if (count % arguments.printinterval == 0) debugOutput(DEBUG_LEVEL_INFO, "cycle=%d count=%d\n", cycle, count); return RAW1394_ISO_OK; } void prepareXmit(raw1394handle_t handle) { debugOutput(DEBUG_LEVEL_INFO, "prepareXmit(handle)\n"); if(raw1394_iso_xmit_init(handle, myISOSender, arguments.buffersize, arguments.packetsize, arguments.channel, SPEED, arguments.interval)) { perror("raw1394_iso_xmit_init"); exit(1); } if(raw1394_iso_xmit_start(handle, arguments.startoncycle, arguments.prebuffers)) { perror("raw1394_iso_xmit_start"); exit(1); } } int32_t myResetHandler(raw1394handle_t handle, uint32_t generation) { debugOutput(DEBUG_LEVEL_INFO, "Reset happened\n"); raw1394_iso_shutdown(handle); raw1394_update_generation(handle, generation); prepareXmit(handle); return 0; } int32_t main(int32_t argc, char **argv) { raw1394handle_t handle; // Default values. arguments.verbose = DEBUG_LEVEL_VERBOSE; arguments.port = 0; arguments.channel = 0; arguments.packetsize = 1024; arguments.buffersize = 100; arguments.interval = -1; arguments.prebuffers = 0; arguments.startoncycle = -1; arguments.countdown = 10000; arguments.printinterval = 100; arguments.rtprio = 0; // Parse our arguments; every option seen by `parse_opt' will // be reflected in `arguments'. if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { debugError("Could not parse command line\n" ); return -1; } debugOutput(DEBUG_LEVEL_NORMAL, "verbose level = %d\n", (int) arguments.verbose); setDebugLevel(arguments.verbose); debugOutput(DEBUG_LEVEL_INFO, "Get 1394 handle...\n"); handle = raw1394_new_handle(); if(!handle) { perror("raw1394_new_handle"); return -1; } debugOutput(DEBUG_LEVEL_INFO, "Select 1394 port %d...\n", (int)arguments.port); int ret = -1; do { if (raw1394_get_port_info(handle, NULL, 0) < 0) { perror("raw1394_get_port_info"); return 1; } ret = raw1394_set_port(handle, arguments.port ); } while (ret != 0 && errno == ESTALE); if(ret != 0 && errno) { perror("raw1394_set_port"); return 1; } if(raw1394_busreset_notify(handle, RAW1394_NOTIFY_ON)) { perror("raw1394_busreset_notify"); exit(1); } raw1394_set_bus_reset_handler(handle, myResetHandler); debugOutput(DEBUG_LEVEL_INFO, "Setting RT priority (%d)...\n", (int)arguments.rtprio); set_realtime_priority(arguments.rtprio); debugOutput(DEBUG_LEVEL_INFO, "Prepare/start ISO transmit...\n"); prepareXmit(handle); int countdown = arguments.countdown; debugOutput(DEBUG_LEVEL_INFO, "Starting iterate loop...\n"); while(countdown--) { if(raw1394_loop_iterate(handle)) { perror("raw1394_loop_iterate"); return 1; } } return 0; } libffado-2.4.5/tests/systemtests/test-sysload.cpp0000644000175000001440000001371014206145246021576 0ustar jwoitheusers/* * Parts Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * based on howdyget.c (unknown source, maybe Maas Digital LLC) */ #include #include #include #include #include #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include #include "debugmodule/debugmodule.h" #include "realtimetools.h" #include uint32_t count = 0; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_EXTRA_ARGS 2 // Program documentation. // Program documentation. static char doc[] = "FFADO -- simple RT system loader\n\n"; // A description of the arguments we accept. static char args_doc[] = ""; struct arguments { long int verbose; long int rtprio; long int period; long int countdown; long int cpu_pct; char* args[MAX_EXTRA_ARGS]; }; // The options we understand. static struct argp_option options[] = { {"verbose", 'v', "level", 0, "Verbose level" }, {"rtprio", 'P', "prio", 0, "real time priority of the iterator process/thread (0 = no RT)" }, {"period", 'p', "usecs", 0, "period duration (in usecs)" }, {"countdown", 'u', "count", 0, "number of times to run the load loop" }, {"cpu_pct", 'c', "count", 0, "target CPU use (in percent)" }, { 0 } }; // Parse a single option. #define PARSE_ARG_LONG(XXletterXX, XXvarXX, XXdescXX) \ case XXletterXX: \ if (arg) { \ XXvarXX = strtol( arg, &tail, 0 ); \ if ( errno ) { \ fprintf( stderr, "Could not parse '%s' argument\n", XXdescXX ); \ return ARGP_ERR_UNKNOWN; \ } \ } \ break; static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { PARSE_ARG_LONG('v', arguments->verbose, "verbose"); PARSE_ARG_LONG('P', arguments->rtprio, "rtprio"); PARSE_ARG_LONG('p', arguments->period, "period"); PARSE_ARG_LONG('u', arguments->countdown, "countdown"); PARSE_ARG_LONG('c', arguments->cpu_pct, "cpu_pct"); case ARGP_KEY_ARG: break; case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } return 0; } // Our argp parser. static struct argp argp = { options, parse_opt, args_doc, doc }; // the global arguments struct struct arguments arguments; // signal handler int run; static void sighandler (int sig) { run = 0; } // the load function float global; void load_function() { int cnt = 10; while(cnt--) { int global_int = (int)global; global = global / 7.0; global_int++; global += (float)global_int; } } int main(int argc, char **argv) { // register signal handler run = 1; signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); // Default values. arguments.verbose = DEBUG_LEVEL_VERBOSE; arguments.rtprio = 0; arguments.countdown = 1000; arguments.period = 1000; arguments.cpu_pct = 50; // Parse our arguments; every option seen by `parse_opt' will // be reflected in `arguments'. if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { debugError("Could not parse command line\n" ); return -1; } debugOutput(DEBUG_LEVEL_INFO, "Simple RT system loader\n"); debugOutput(DEBUG_LEVEL_INFO, " Arguments:\n"); debugOutput(DEBUG_LEVEL_INFO, " RT priority : %ld\n", arguments.rtprio); debugOutput(DEBUG_LEVEL_INFO, " Countdown : %ld\n", arguments.countdown); debugOutput(DEBUG_LEVEL_INFO, " Period : %ld usec\n", arguments.period); debugOutput(DEBUG_LEVEL_INFO, " CPU load : %ld%%\n", arguments.cpu_pct); debugOutput(DEBUG_LEVEL_INFO, "Setting RT priority (%ld)...\n", arguments.rtprio); set_realtime_priority(arguments.rtprio); debugOutput(DEBUG_LEVEL_INFO, "Starting iterate loop...\n"); flushDebugOutput(); int countdown = arguments.countdown; uint64_t sleep_time = rt_gettime_usecs(); while(countdown-- && run) { // figure out when to stop calling the load function uint64_t run_until = sleep_time + arguments.period * arguments.cpu_pct / 100; // uint64_t tic = rt_gettime_usecs(); while(rt_gettime_usecs() < run_until) load_function(); uint64_t toc = rt_gettime_usecs(); // now wait for the period to end sleep_time += arguments.period; rt_sleep_absolute_usecs(sleep_time); // check if we are late toc = rt_gettime_usecs(); int64_t usecs_late = toc - sleep_time; if(usecs_late > 1000) { debugWarning("late wakeup: %" PRId64 " usecs\n", usecs_late); } // try and detect lockup () if(usecs_late > 100000) { debugWarning("very late wakeup: %" PRId64 " usecs\n", usecs_late); // force exit, since this is a loop out of control run=0; } } if(run) { debugOutput(DEBUG_LEVEL_INFO, "Clean exit...\n"); } else { debugOutput(DEBUG_LEVEL_INFO, "Forced exit...\n"); } return 0; } libffado-2.4.5/tests/systemtests/.gitignore0000644000175000001440000000012712132617070020420 0ustar jwoitheusersffado-test-isorecv ffado-test-isoxmit gen-loadpulses test-clock_nanosleep test-sysload libffado-2.4.5/tests/test-avccmd.cpp0000644000175000001440000001565414206145246016757 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include #include #include "debugmodule/debugmodule.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/cmd_serialize.h" #include "libavc/general/avc_generic.h" #include #include #include using namespace std; using namespace AVC; using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 1000 class TestCmd: public AVCCommand { public: TestCmd(Ieee1394Service& ieee1394service, opcode_t opcode ); virtual ~TestCmd(); virtual bool serialize( Util::Cmd::IOSSerialize& se ); virtual bool deserialize( Util::Cmd::IISDeserialize& de ); virtual const char* getCmdName() const { return "TestCmd"; } byte_t args[MAX_ARGS]; int nargs; }; //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-avccmd 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-avccmd -- test program to send a custom avc command to a specific node."; static char args_doc[] = "NODE_ID [avc cmd byte sequence]"; static struct argp_option options[] = { {"verbose", 'v', 0, 0, "Produce verbose output" }, {"port", 'p', "PORT", 0, "Set port" }, {"node", 'n', "NODE", 0, "Set node" }, { 0 } }; struct arguments { arguments() : nargs ( 0 ) , verbose( false ) , test( false ) , port( 0 ) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; bool verbose; bool test; int port; int node; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': arguments->verbose = true; break; case 't': arguments->test = true; break; case 'p': arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'n': arguments->node = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: if(arguments->nargs<4) { printf("not enough arguments\n"); return -1; } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { fprintf( stderr, "Could not parse command line\n" ); exit(-1); } errno = 0; char* tail; Ieee1394Service *m_1394Service = new Ieee1394Service(); if ( !m_1394Service ) { debugFatal( "Could not create Ieee1349Service object\n" ); return false; } if ( !m_1394Service->initialize( arguments.port ) ) { debugFatal( "Could not initialize Ieee1349Service object\n" ); delete m_1394Service; m_1394Service = 0; return false; } char cmdtype = strtol(arguments.args[0], &tail, 0); if (errno) { perror("argument parsing failed:"); return -1; } char unit_subunit = strtol(arguments.args[1], &tail, 0); if (errno) { perror("argument parsing failed:"); return -1; } opcode_t opcode = strtol(arguments.args[2], &tail, 0); if (errno) { perror("argument parsing failed:"); return -1; } TestCmd cmd( *m_1394Service, opcode ); switch (cmdtype) { case AVC::AVCCommand::eCT_Control: cmd.setCommandType( AVC::AVCCommand::eCT_Control ); break; case AVC::AVCCommand::eCT_Status: cmd.setCommandType( AVC::AVCCommand::eCT_Status ); break; case AVC::AVCCommand::eCT_SpecificInquiry: cmd.setCommandType( AVC::AVCCommand::eCT_SpecificInquiry ); break; case AVC::AVCCommand::eCT_Notify: cmd.setCommandType( AVC::AVCCommand::eCT_Notify ); break; case AVC::AVCCommand::eCT_GeneralInquiry: cmd.setCommandType( AVC::AVCCommand::eCT_GeneralInquiry ); break; default: printf("Invalid command type: 0x%02X.\n", cmdtype); exit(-1); } cmd.setNodeId( arguments.node ); cmd.setSubunitType( byteToSubunitType(unit_subunit >> 3) ); cmd.setSubunitId( (unit_subunit & 0x7) ); cmd.setVerbose( DEBUG_LEVEL_VERY_VERBOSE ); int i=0; for (;i. * */ #include "debugmodule/debugmodule.h" DECLARE_GLOBAL_DEBUG_MODULE; #include "libutil/ByteSwap.h" #include "libstreaming/amdtp/AmdtpBufferOps.h" #include "libutil/SystemTimeSource.h" #include "libutil/Time.h" #include // 32M of test data #define NB_QUADLETS (1024 * 1024 * 32) #define NB_TESTS 10 bool testByteSwap(int nb_quadlets, int nb_tests) { quadlet_t *buffer_1; quadlet_t *buffer_ref; int i=0; ffado_microsecs_t start; ffado_microsecs_t elapsed; setDebugLevel(DEBUG_LEVEL_NORMAL); buffer_1 = new quadlet_t[nb_quadlets]; buffer_ref = new quadlet_t[nb_quadlets]; printMessage( "Generating test data...\n"); for (i=0; i> 8 ) | 0x40000000; buffer_ref[i] = tmp; } printMessage( "Performing AMDTP labeling...\n"); int test=0; for (test=0; test. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "debugmodule/debugmodule.h" #include "src/DeviceStringParser.h" DECLARE_GLOBAL_DEBUG_MODULE; // Program documentation. static char doc[] = "FFADO -- Watchdog test\n\n"; // A description of the arguments we accept. static char args_doc[] = ""; struct arguments { short verbose; }; // The options we understand. static struct argp_option options[] = { {"verbose", 'v', "n", 0, "Verbose level" }, { 0 } }; //------------------------------------------------------------- // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': if (arg) { arguments->verbose = strtoll( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } else { if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; default: return ARGP_ERR_UNKNOWN; } return 0; } // Our argp parser. static struct argp argp = { options, parse_opt, args_doc, doc }; int main(int argc, char *argv[]) { struct arguments arguments; // Default values. arguments.verbose = DEBUG_LEVEL_VERY_VERBOSE; // Parse our arguments; every option seen by `parse_opt' will // be reflected in `arguments'. if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { fprintf( stderr, "Could not parse command line\n" ); exit(1); } setDebugLevel(arguments.verbose); DeviceStringParser p = DeviceStringParser(); p.setVerboseLevel(arguments.verbose); p.parseString("hw:0;hw:1"); p.parseString("hw:0;hw:1;"); p.parseString("hw:0,0;hw:1,1;"); p.parseString("hw:1,1;hw:1,0"); p.parseString("guid:0x123456789"); p.show(); return EXIT_SUCCESS; } libffado-2.4.5/tests/test-dice-eap.cpp0000644000175000001440000002050614206145246017161 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include #include #include "debugmodule/debugmodule.h" #include "devicemanager.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/Configuration.h" #include "libcontrol/MatrixMixer.h" #include "libcontrol/CrossbarRouter.h" #include "dice/dice_avdevice.h" #include "dice/dice_eap.h" using namespace Dice; #include #include #include #include #include #include int run; static void sighandler(int sig) { run = 0; } using namespace std; using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 1000 //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-dice-eap 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-dice-eap -- test program to examine the DICE EAP code."; static char args_doc[] = "NODE_ID"; static struct argp_option options[] = { {"verbose", 'v', "LEVEL", 0, "Produce verbose output" }, {"port", 'p', "PORT", 0, "Set port" }, {"node", 'n', "NODE", 0, "Set node" }, {"application",'a', NULL, 0, "Show the application space"}, {"counts", 'c', "COUNTS", 0, "Number of runs to do. -1 means to run forever."}, { 0 } }; struct arguments { arguments() : nargs ( 0 ) , verbose( DEBUG_LEVEL_NORMAL ) , test( false ) , port( -1 ) , node( -1 ) , application( false ) , counts( -1 ) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; int verbose; bool test; int port; int node; bool application; int counts; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': arguments->verbose = strtol(arg, &tail, 0); break; case 't': arguments->test = true; break; case 'a': arguments->application = true; break; case 'p': arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'n': arguments->node = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'c': arguments->counts = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: if(arguments->nargs<0) { printf("not enough arguments\n"); return -1; } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { run=1; signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { printMessage("Could not parse command line\n" ); exit(-1); } errno = 0; DeviceManager *m_deviceManager = new DeviceManager(); if ( !m_deviceManager ) { printMessage("Could not allocate device manager\n" ); return -1; } if ( arguments.verbose ) { m_deviceManager->setVerboseLevel(arguments.verbose); } if ( !m_deviceManager->initialize() ) { printMessage("Could not initialize device manager\n" ); delete m_deviceManager; return -1; } char s[1024]; if(arguments.node > -1) { snprintf(s, 1024, "hw:%d,%d", arguments.port, arguments.node); if ( !m_deviceManager->addSpecString(s) ) { printMessage("Could not add spec string %s to device manager\n", s ); delete m_deviceManager; return -1; } } else { snprintf(s, 1024, "hw:%d", arguments.port); if ( !m_deviceManager->addSpecString(s) ) { printMessage("Could not add spec string %s to device manager\n", s ); delete m_deviceManager; return -1; } } if ( !m_deviceManager->discover(false) ) { printMessage("Could not discover devices\n" ); delete m_deviceManager; return -1; } if(m_deviceManager->getAvDeviceCount() == 0) { printMessage("No devices found\n"); delete m_deviceManager; return -1; } Dice::Device* avDevice = dynamic_cast(m_deviceManager->getAvDeviceByIndex(0)); if(avDevice == NULL) { printMessage("Device is not a DICE device\n" ); delete m_deviceManager; return -1; } // now play //avDevice->setVerboseLevel(DEBUG_LEVEL_VERY_VERBOSE); bool supports_eap = EAP::supportsEAP(*avDevice); if (!supports_eap) { printMessage("EAP not supported on this device\n"); delete m_deviceManager; return -1; } printMessage("device supports EAP\n"); EAP &eap = *(avDevice->getEAP()); if (arguments.application) { eap.showApplication(); eap.showFullRouter(); eap.showFullPeakSpace(); } else eap.show(); eap.lockControl(); Control::Element *e = eap.getElementByName("MatrixMixer"); if(e == NULL) { printMessage("Could not get MatrixMixer control element\n"); } else { Control::MatrixMixer *m = dynamic_cast(e); if(m == NULL) { printMessage("Element is not a MatrixMixer control element\n"); }/* else { for(int row=0; row < 16; row++) { for(int col=0; col < 18; col++) { m->setValue(row, col, 0); } } }*/ } // after unlocking, these should not be used anymore e = NULL; eap.unlockControl(); while(run && arguments.counts != 0) { eap.lockControl(); Control::Element *e = eap.getElementByName("Router"); if(e == NULL) { printMessage("Could not get CrossbarRouter control element\n"); } else { Control::CrossbarRouter *c = dynamic_cast(e); if(c == NULL) { printMessage("Element is not a CrossbarRouter control element\n"); } else { std::map peaks = c->getPeakValues(); for (std::map::iterator it=peaks.begin(); it!=peaks.end(); ++it) { printMessage("%s: %g\n", it->first.c_str(), it->second); } } } // after unlocking, these should not be used anymore e = NULL; eap.unlockControl(); sleep(1); arguments.counts--; } // cleanup delete m_deviceManager; return 0; } libffado-2.4.5/tests/test-echo.cpp0000644000175000001440000001737414206145246016441 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include "libutil/Time.h" #include #include #include using namespace std; //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-echo 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-echo -- test program for the ECHO AUDIOFIRE devices"; static char args_doc[] = "NODE_ID"; static struct argp_option options[] = { {"verbose", 'v', 0, 0, "Produce verbose output" }, {"port", 'p', "PORT", 0, "Set port" }, { 0 } }; struct arguments { arguments() : verbose( false ) , test( false ) , port( 0 ) { args[0] = 0; } char* args[1]; bool verbose; bool test; int port; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; switch (key) { case 'v': arguments->verbose = true; break; case 't': arguments->test = true; break; case 'p': errno = 0; arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case ARGP_KEY_ARG: if (state->arg_num >= 1) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; break; case ARGP_KEY_END: if (state->arg_num < 1) { // Not enough arguments. argp_usage (state); } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { // arg parsing argp_parse (&argp, argc, argv, 0, 0, &arguments); errno = 0; char* tail; int iNodeId = strtol(arguments.args[0], &tail, 0); if (errno) { perror("argument parsing failed:"); return -1; } raw1394handle_t pHandle = raw1394_new_handle_on_port( arguments.port ); if ( !pHandle ) { if ( !errno ) { cerr << "libraw1394 not compatible" << endl; } else { perror( "Could not get 1394 handle" ); cerr << "Is ieee1394 and raw1394 driver loaded?" << endl; } return -1; } quadlet_t cmd[6]; unsigned int response_len; // cerr << "Opening descriptor" << endl; // 0h 05m 21.760442s - GetDescriptorOpen: cmd[0]= < 0x00 0x60 0x08 0x80 0x01 0xFF> // 0h 05m 21.760687s - GetDescriptorOpen: resp[0]= < 0x09 0x60 0x08 0x80 0x01 0xFF > ACCEPTED // cmd[0] = 0x00600880; // cmd[1] = 0x01FF0000; // avc1394_transaction_block2(pHandle, 0xffc0 | iNodeId, cmd, 2, &response_len, 10); // SleepRelativeUsec(100000); // // cerr << "Reading descriptor" << endl; // // 0h 05m 21.760700s - GetDescriptorRead: cmd[0]= < 0x00 0x60 0x09 0x80 0xFF 0xFF 0x00 0x00 0x00 0x00> // // 0h 05m 21.761123s - GetDescriptorRead: resp[0]= < 0x09 0x60 0x09 0x80 0x11 0xFF 0x01 0xF6 0x00 0x00 0x03 0x9E // cmd[0] = 0x00600980; // cmd[1] = 0xFFFF0000; // cmd[2] = 0x00000000; // cmd[2] = 0x00000000; // // avc1394_transaction_block2(pHandle, 0xffc0 | iNodeId, cmd, 3, &response_len, 10); // SleepRelativeUsec(100000); // // cerr << "Closing descriptor" << endl; // cmd[0] = 0x00600880; // cmd[1] = 0x00FF0000; // avc1394_transaction_block2(pHandle, 0xffc0 | iNodeId, cmd, 2, &response_len, 10); // SleepRelativeUsec(100000); cerr << "getting signal source" << endl; // 0h 05m 21.762917s - at line 2283, fMaxAudioOutputChannels=2, fMaxAudioInputChannels=0 // 0h 05m 21.762919s - GetSignalSource: cmd[0]= < 0x01 0xFF 0x1A 0xFF 0xFF 0xFE 0xFF 0x00> // 0h 05m 21.763149s - GetSignalSource: resp[0]= < 0x0c 0xFF 0x1A 0x60 0x60 0x00 0xFF 0x00 > IMPLEMENTED // 0h 05m 21.763167s - Isoch out 0 gets its signal from sub/unit 0x60 Source Plug 0 // 0h 05m 21.763170s - GetSignalSource: cmd[0]= < 0x01 0xFF 0x1A 0xFF 0xFF 0xFE 0xFF 0x80> // 0h 05m 21.763376s - GetSignalSource: resp[0]= < 0x0c 0xFF 0x1A 0x00 0x60 0x01 0xFF 0x80 > IMPLEMENTED // 0h 05m 21.763394s - Isoch out 128 gets its signal from sub/unit 0x60 Source Plug 1 // 0h 05m 21.763397s - GetSignalSource: cmd[0]= < 0x01 0xFF 0x1A 0xFF 0xFF 0xFE 0xFF 0x81> // 0h 05m 21.763637s - GetSignalSource: resp[0]= < 0x0c 0xFF 0x1A 0x00 0x60 0x02 0xFF 0x81 > IMPLEMENTED // 0h 05m 21.763654s - Isoch out 129 gets its signal from sub/unit 0x60 Source Plug 2 // 0h 05m 21.764895s - Starting to look at subunit 1. fNumberOfSubUnits = 2 // 0h 05m 21.764897s - Subunit 1 GetSignalSource: cmd[0]= < 0x01 0xFF 0x1A 0xFF 0xFF 0xFE 0x60 0x00> // 0h 05m 21.765129s - GetSignalSource: resp[0]= < 0x0c 0xFF 0x1A 0x20 0xFF 0x00 0x60 0x00 > IMPLEMENTED // 0h 05m 21.765140s - Subunit type12, addr:0x60 dest 0 gets its signal from sub/unit 0xff Source Plug 0 // 0h 05m 21.765142s - subunit 96 dest plug 0 is routed // 0h 05m 21.765143s - Subunit 1 GetSignalSource: cmd[0]= < 0x01 0xFF 0x1A 0xFF 0xFF 0xFE 0x60 0x01> // 0h 05m 21.765364s - GetSignalSource: resp[0]= < 0x0c 0xFF 0x1A 0x00 0xFF 0x80 0x60 0x01 > IMPLEMENTED // 0h 05m 21.765382s - Subunit type12, addr:0x60 dest 1 gets its signal from sub/unit 0xff Source Plug 128 // 0h 05m 21.765385s - Plug being changed from 0x80 to 1 for internal bookeeping. // 0h 05m 21.765389s - Subunit 1 GetSignalSource: cmd[0]= < 0x01 0xFF 0x1A 0xFF 0xFF 0xFE 0x60 0x02> // 0h 05m 21.765632s - GetSignalSource: resp[0]= < 0x0c 0xFF 0x1A 0x00 0xFF 0x81 0x60 0x02 > IMPLEMENTED // 0h 05m 21.765651s - Subunit type12, addr:0x60 dest 2 gets its signal from sub/unit 0xff Source Plug 129 // 0h 05m 21.765653s - Plug being changed from 0x81 to 2 for internal bookeeping. // 0h 05m 21.765657s - Subunit 1 GetSignalSource: cmd[0]= < 0x01 0xFF 0x1A 0xFF 0xFF 0xFE 0x60 0x03> // 0h 05m 21.765874s - GetSignalSource: resp[0]= < 0x0c 0xFF 0x1A 0x00 0x60 0x03 0x60 0x03 > IMPLEMENTED // 0h 05m 21.765892s - Subunit type12, addr:0x60 dest 3 gets its signal from sub/unit 0x60 Source Plug 3 cmd[0] = 0x01FF1AFF; cmd[1] = 0xFFFE6000; avc1394_transaction_block2(pHandle, 0xffc0 | iNodeId, cmd, 2, &response_len, 10); // SleepRelativeUsec(100000); cmd[0] = 0x01FF1AFF; cmd[1] = 0xFFFEFF00; avc1394_transaction_block2(pHandle, 0xffc0 | iNodeId, cmd, 2, &response_len, 10); SleepRelativeUsec(100000); raw1394_destroy_handle( pHandle ); return 0; } libffado-2.4.5/tests/test-echomixer.cpp0000644000175000001440000004327014206145246017500 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include #include #include "debugmodule/debugmodule.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/cmd_serialize.h" #include "libavc/general/avc_generic.h" #include "fireworks/efc/efc_avc_cmd.h" #include "fireworks/efc/efc_cmds_mixer.h" #include "fireworks/efc/efc_cmds_monitor.h" #include "fireworks/efc/efc_cmds_hardware.h" using namespace FireWorks; #include #include #include #include using namespace std; using namespace AVC; using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 1000 //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-echomixer 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-avccmd -- test program to examine the echo audio mixer."; static char args_doc[] = "NODE_ID"; static struct argp_option options[] = { {"verbose", 'v', 0, 0, "Produce verbose output" }, {"port", 'p', "PORT", 0, "Set port" }, {"node", 'n', "NODE", 0, "Set node" }, { 0 } }; struct arguments { arguments() : nargs ( 0 ) , verbose( false ) , test( false ) , port( 0 ) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; bool verbose; bool test; int port; int node; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': arguments->verbose = true; break; case 't': arguments->test = true; break; case 'p': arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'n': arguments->node = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: if(arguments->nargs<0) { printf("not enough arguments\n"); return -1; } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; bool doEfcOverAVC(Ieee1394Service& m_1394Service, int node, EfcCmd &c) { EfcOverAVCCmd cmd( m_1394Service ); cmd.setCommandType( AVC::AVCCommand::eCT_Control ); cmd.setNodeId( node ); cmd.setSubunitType( AVC::eST_Unit ); cmd.setSubunitId( 0xff ); cmd.setVerbose( DEBUG_LEVEL_NORMAL ); cmd.m_cmd = &c; if ( !cmd.fire()) { debugError( "EfcOverAVCCmd command failed\n" ); c.showEfcCmd(); return false; } if ( c.m_header.retval != EfcCmd::eERV_Ok && c.m_header.retval != EfcCmd::eERV_FlashBusy) { debugError( "EFC command failed\n" ); c.showEfcCmd(); return false; } return true; } /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { fprintf( stderr, "Could not parse command line\n" ); exit(-1); } errno = 0; Ieee1394Service *m_1394Service = new Ieee1394Service(); if ( !m_1394Service ) { debugFatal( "Could not create Ieee1349Service object\n" ); return false; } if ( !m_1394Service->initialize( arguments.port ) ) { debugFatal( "Could not initialize Ieee1349Service object\n" ); delete m_1394Service; m_1394Service = 0; return false; } EfcHardwareInfoCmd m_HwInfo; if (!doEfcOverAVC(*m_1394Service, arguments.node, m_HwInfo)) { debugFatal("Could not obtain FireWorks device info\n"); return false; } m_HwInfo.showEfcCmd(); unsigned int ch=0; uint32_t pbk_vol[m_HwInfo.m_nb_1394_playback_channels][5]; uint32_t rec_vol[m_HwInfo.m_nb_1394_record_channels][5]; uint32_t out_vol[m_HwInfo.m_nb_phys_audio_out][5]; uint32_t in_vol[m_HwInfo.m_nb_phys_audio_in][5]; memset(pbk_vol, 0, sizeof(uint32_t) * 5 * m_HwInfo.m_nb_1394_playback_channels); memset(rec_vol, 0, sizeof(uint32_t) * 5 * m_HwInfo.m_nb_1394_record_channels); memset(out_vol, 0, sizeof(uint32_t) * 5 * m_HwInfo.m_nb_phys_audio_out); memset(in_vol, 0, sizeof(uint32_t) * 5 * m_HwInfo.m_nb_phys_audio_in); enum eMixerTarget t=eMT_PlaybackMix; enum eMixerCommand c = eMC_Gain; enum eCmdType type = eCT_Get; EfcGenericMixerCmd cmd(t,c); cmd.setType(type); #define DO_PLAYBACK_MIX // #define DO_RECORD_MIX #define DO_PHYS_OUT_MIX #define DO_PHYS_IN_MIX #ifdef DO_PLAYBACK_MIX cmd.setTarget(eMT_PlaybackMix); for (ch=0;ch. * */ #include "libavc/audiosubunit/avc_function_block.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include #include #include using namespace AVC; bool doApp( Ieee1394Service& ieee1394service, int node_id, int fb_id ) { AVC::FunctionBlockCmd fbCmd( ieee1394service, FunctionBlockCmd::eFBT_Processing, fb_id, FunctionBlockCmd::eCA_Current); fbCmd.setNodeId( node_id ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Status ); fbCmd.setVerboseLevel( DEBUG_LEVEL_VERY_VERBOSE ); // Ok, this enhanced mixer setting here is just a hack, we need // a sane way to set processing features (read pointer management) delete fbCmd.m_pFBProcessing->m_pMixer; fbCmd.m_pFBProcessing->m_pMixer = 0; AVC::FunctionBlockProcessingEnhancedMixer em; fbCmd.m_pFBProcessing->m_pEnhancedMixer = em.clone(); fbCmd.m_pFBProcessing->m_fbInputPlugNumber = 0x00; fbCmd.m_pFBProcessing->m_inputAudioChannelNumber = 0xff; fbCmd.m_pFBProcessing->m_outputAudioChannelNumber = 0xff; fbCmd.m_pFBProcessing->m_pEnhancedMixer->m_statusSelector = 1; if ( !fbCmd.fire() ) { printf( "cmd failed\n" ); return false; } // Util::Cmd::CoutSerializer se; // fbCmd.serialize( se ); return true; } /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { if (argc < 2) { printf("usage: NODE_ID FB_ID\n"); exit(0); } errno = 0; char* tail; int node_id = strtol( argv[1], &tail, 0 ); int fb_id = strtol( argv[2], &tail, 0 ); if (errno) { perror("argument parsing failed:"); return -1; } Ieee1394Service ieee1394service; if ( !ieee1394service.initialize( 0 ) ) { fprintf( stderr, "could not set port on ieee1394service\n" ); return -1; } doApp( ieee1394service, node_id, fb_id ); return 0; } libffado-2.4.5/tests/test-extplugcmd.cpp0000644000175000001440000001455214206145246017672 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "libavc/avc_extended_plug_info.h" #include "libavc/avc_plug_info.h" #include "libavc/avc_serialize.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include using namespace std; //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-extplugcmd 0.2"; const char *argp_program_bug_address = ""; static char doc[] = "test-extplugcmd -- tests some extended plug info commands on a BeBoB device"; static char args_doc[] = "NODE_ID"; static struct argp_option options[] = { {"verbose", 'v', 0, 0, "Produce verbose output" }, {"port", 'p', "PORT", 0, "Set port" }, { 0 } }; struct arguments { arguments() : verbose( false ) , test( false ) , port( 0 ) { args[0] = 0; } char* args[1]; bool verbose; bool test; int port; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': arguments->verbose = true; break; case 't': arguments->test = true; break; case 'p': arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case ARGP_KEY_ARG: if (state->arg_num >= 1) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; break; case ARGP_KEY_END: if (state->arg_num < 1) { // Not enough arguments. argp_usage (state); } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; //////////////////////////////////////// // Application //////////////////////////////////////// bool doPlugType( Ieee1394Service& ieee1394service, int node_id ) { ExtendedPlugInfoCmd extPlugInfoCmd( ieee1394service ); UnitPlugAddress unitPlugAddress( UnitPlugAddress::ePT_PCR, 0 ); extPlugInfoCmd.setPlugAddress( PlugAddress( PlugAddress::ePD_Input, PlugAddress::ePAM_Unit, unitPlugAddress ) ); extPlugInfoCmd.setNodeId( node_id ); extPlugInfoCmd.setCommandType( AVCCommand::eCT_Status ); extPlugInfoCmd.setVerbose( arguments.verbose ); ExtendedPlugInfoInfoType extendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_PlugType ); extendedPlugInfoInfoType.initialize(); extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType ); if ( extPlugInfoCmd.fire() ) { CoutSerializer se; extPlugInfoCmd.serialize( se ); } ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType(); if ( infoType && infoType->m_plugType ) { plug_type_t plugType = infoType->m_plugType->m_plugType; printf( "iso input plug %d is of type %d (%s)\n", 0, plugType, extendedPlugInfoPlugTypeToString( plugType ) ); } else { fprintf( stderr, "Not plug name specific data found\n" ); return false; } return true; } bool doPlugName( Ieee1394Service& ieee1394service, int node_id ) { ExtendedPlugInfoCmd extPlugInfoCmd( ieee1394service ); UnitPlugAddress unitPlugAddress( UnitPlugAddress::ePT_PCR, 0 ); extPlugInfoCmd.setPlugAddress( PlugAddress( PlugAddress::ePD_Input, PlugAddress::ePAM_Unit, unitPlugAddress ) ); extPlugInfoCmd.setNodeId( node_id ); extPlugInfoCmd.setCommandType( AVCCommand::eCT_Status ); extPlugInfoCmd.setVerbose( arguments.verbose ); ExtendedPlugInfoInfoType extendedPlugInfoInfoType( ExtendedPlugInfoInfoType::eIT_PlugName ); extendedPlugInfoInfoType.initialize(); extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType ); if ( extPlugInfoCmd.fire() ) { CoutSerializer se; extPlugInfoCmd.serialize( se ); } ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType(); if ( infoType && infoType->m_plugName ) { printf( "iso input plug %d has name '%s'\n", 0, infoType->m_plugName->m_name.c_str() ); } else { fprintf( stderr, "Not plug name specific data found\n" ); return false; } return true; } bool doApp(Ieee1394Service& ieee1394service, int node_id ) { bool success; success = doPlugType( ieee1394service, node_id ); success &= doPlugName( ieee1394service, node_id ); return success; } /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { // arg parsing argp_parse (&argp, argc, argv, 0, 0, &arguments); errno = 0; char* tail; int node_id = strtol(arguments.args[0], &tail, 0); if (errno) { perror("argument parsing failed:"); return -1; } Ieee1394Service ieee1394service; if ( !ieee1394service.initialize( arguments.port ) ) { fprintf( stderr, "could not set port on ieee1394service\n" ); return -1; } doApp( ieee1394service, node_id ); return 0; } libffado-2.4.5/tests/test-ffado.cpp0000644000175000001440000004334414206145246016576 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * This version uses the CPP API */ #include "config.h" #include "version.h" #include "libffado/ffado.h" #include "debugmodule/debugmodule.h" #include "fbtypes.h" #include "devicemanager.h" #include "ffadodevice.h" #include #include #include #include #include #include #include #include #include #include "libieee1394/cycletimer.h" using namespace std; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 5 // global's const char *argp_program_version = PACKAGE_STRING; const char *argp_program_bug_address = PACKAGE_BUGREPORT; // Program documentation. static char doc[] = "FFADO -- a driver for FireWire Audio devices (test application)\n\n" "OPERATION: Discover\n" " SetSamplerate samplerate\n" " SetClockSource [id]\n" " BusReset\n" " ListDevices\n" " SetSplitTimeout timeout_usec\n" " GetSplitTimeout\n" ; // A description of the arguments we accept. static char args_doc[] = "OPERATION"; struct arguments { unsigned int nargs; short silent; long int verbose; long int port; long int use_cache; long int node_id; long int node_id_set; const char* args[MAX_ARGS]; }; // The options we understand. static struct argp_option options[] = { {"quiet", 'q', 0, 0, "Don't produce any output" }, {"silent", 's', 0, OPTION_ALIAS }, {"verbose", 'v', "level", 0, "Produce verbose output" }, #if ENABLE_DISCOVERY_CACHE {"cache", 'c', "enable", 0, "Use AVC model cache" }, #endif {"node", 'n', "id", 0, "Node to use" }, {"port", 'p', "nr", 0, "IEEE1394 Port to use" }, { 0 } }; //------------------------------------------------------------- // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'q': case 's': arguments->silent = 1; break; case 'v': if (arg) { arguments->verbose = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'c': if (arg) { arguments->use_cache = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'cache' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'p': if (arg) { arguments->port = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'port' argument\n" ); return ARGP_ERR_UNKNOWN; } } else { if ( errno ) { fprintf( stderr, "Could not parse 'port' argumen\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'n': if (arg) { arguments->node_id = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'node' argument\n" ); return ARGP_ERR_UNKNOWN; } arguments->node_id_set=1; } else { if ( errno ) { fprintf( stderr, "Could not parse 'node' argumen\n" ); return ARGP_ERR_UNKNOWN; } } break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage( state ); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: if (state->arg_num < 1) { // Not enough arguments. argp_usage( state ); } break; default: return ARGP_ERR_UNKNOWN; } return 0; } // Our argp parser. static struct argp argp = { options, parse_opt, args_doc, doc }; int exitfunction( int retval ) { debugOutput( DEBUG_LEVEL_NORMAL, "Debug output flushed...\n" ); flushDebugOutput(); return retval; } void printDeviceList(unsigned int port) { Ieee1394Service service; // switch off all messages since they mess up the list service.setVerboseLevel(0); if ( !service.initialize( port ) ) { printf("Could not initialize IEEE 1394 service on port %d\n", port); exit(-1); } printf("=== 1394 PORT %d ===\n", port); printf(" Node id GUID VendorId ModelId Vendor - Model\n"); for (int i = 0; i < service.getNodeCount(); i++) { ConfigRom crom(service, i); if (crom.initialize()) printf(" %2d 0x%s 0x%08X 0x%08X %s - %s\n", i, crom.getGuidString().c_str(), crom.getNodeVendorId(), crom.getModelId(), crom.getVendorName().c_str(), crom.getModelName().c_str()); } } void busreset(unsigned int port) { Ieee1394Service service; // switch off all messages since they mess up the list service.setVerboseLevel(0); if ( !service.initialize( port ) ) { printf("Could not initialize IEEE 1394 service on port %d\n", port); exit(-1); } printf("Doing busreset on port %d\n", port); service.doBusReset(); } int main( int argc, char **argv ) { struct arguments arguments; printf("-----------------------------------------------\n"); printf("FFADO test and diagnostic utility\n"); printf("Part of the FFADO project -- www.ffado.org\n"); printf("Version: %s\n", PACKAGE_VERSION); printf("(C) 2008-2021, Daniel Wagner, Pieter Palmers and others\n"); printf("This program comes with ABSOLUTELY NO WARRANTY.\n"); printf("-----------------------------------------------\n\n"); // check the library version const char *libversion = ffado_get_version(); const char *progversion = PACKAGE_STRING; if(strcmp(libversion, progversion) != 0) { printf("Library version mismatch. (required: %s, present: %s)\n", progversion, libversion); printf("Please run this application against the exact corresponding library\n"); printf("it was compiled for. The most common cause for this is having more\n"); printf("than one version of libffado installed.\n\n"); return exitfunction(-1); } // Default values. arguments.nargs = 0; arguments.silent = 0; arguments.verbose = 0; arguments.use_cache = 1; arguments.port = 0; arguments.node_id = 0; arguments.node_id_set = 0; // if we don't specify a node, discover all arguments.args[0] = ""; arguments.args[1] = ""; // Parse our arguments; every option seen by `parse_opt' will // be reflected in `arguments'. if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { fprintf( stderr, "Could not parse command line\n" ); return exitfunction(-1); } setDebugLevel(arguments.verbose); if ( strcmp( arguments.args[0], "Discover" ) == 0 ) { DeviceManager *m_deviceManager = new DeviceManager(); if ( !m_deviceManager ) { fprintf( stderr, "Could not allocate device manager\n" ); return exitfunction(-1); } if ( arguments.verbose ) { m_deviceManager->setVerboseLevel(arguments.verbose); } if ( !m_deviceManager->initialize() ) { fprintf( stderr, "Could not initialize device manager\n" ); delete m_deviceManager; return exitfunction(-1); } if ( arguments.verbose ) { m_deviceManager->setVerboseLevel(arguments.verbose); } if ( !m_deviceManager->discover(arguments.use_cache) ) { fprintf( stderr, "Could not discover devices\n" ); delete m_deviceManager; return exitfunction(-1); } delete m_deviceManager; return exitfunction(0); } else if ( strcmp( arguments.args[0], "BusReset" ) == 0 ) { busreset(arguments.port); } else if ( strcmp( arguments.args[0], "ListDevices" ) == 0 ) { unsigned int nb_ports = Ieee1394Service::detectNbPorts(); for (unsigned int i=0;isetVerboseLevel(arguments.verbose); } if ( !m_deviceManager->initialize() ) { fprintf( stderr, "Could not initialize device manager\n" ); delete m_deviceManager; return exitfunction(-1); } if ( arguments.verbose ) { m_deviceManager->setVerboseLevel(arguments.verbose); } if ( !m_deviceManager->discover(arguments.use_cache) ) { fprintf( stderr, "Could not discover devices\n" ); delete m_deviceManager; return exitfunction(-1); } if(arguments.node_id_set) { FFADODevice* avDevice = m_deviceManager->getAvDevice( arguments.node_id ); if ( avDevice ) { avDevice->setVerboseLevel(arguments.verbose); if ( ! avDevice->setSamplingFrequency( samplerate ) ) { fprintf( stderr, "Could not set samplerate\n" ); } } } else { int i=0; int devices_on_bus = m_deviceManager->getNbDevices(); printf(" port = %d, devices_on_bus = %d\n", (int)arguments.port, devices_on_bus); for(i=0;igetDeviceNodeId(i); printf(" set samplerate for device = %d, node = %d\n", i, node_id); FFADODevice* avDevice = m_deviceManager->getAvDevice( node_id ); if ( avDevice ) { avDevice->setVerboseLevel(arguments.verbose); if ( !avDevice->setSamplingFrequency( samplerate ) ) { fprintf( stderr, "Could not set samplerate\n" ); } } } } delete m_deviceManager; return exitfunction(0); } else if ( strcmp( arguments.args[0], "SetClockSource" ) == 0 ) { char* tail; unsigned int targetid = (unsigned int)strtol( arguments.args[1], &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse clock source argument\n" ); targetid=0xFFFF; } DeviceManager *m_deviceManager = new DeviceManager(); if ( !m_deviceManager ) { fprintf( stderr, "Could not allocate device manager\n" ); return exitfunction(-1); } if ( arguments.verbose ) { m_deviceManager->setVerboseLevel(arguments.verbose); } if ( !m_deviceManager->initialize() ) { fprintf( stderr, "Could not initialize device manager\n" ); delete m_deviceManager; return exitfunction(-1); } if ( arguments.verbose ) { m_deviceManager->setVerboseLevel(arguments.verbose); } if ( !m_deviceManager->discover(arguments.use_cache) ) { fprintf( stderr, "Could not discover devices\n" ); delete m_deviceManager; return exitfunction(-1); } if(arguments.node_id_set) { FFADODevice* avDevice = m_deviceManager->getAvDevice( arguments.node_id ); if ( avDevice ) { FFADODevice::ClockSource s; avDevice->setVerboseLevel(arguments.verbose); FFADODevice::ClockSourceVector sources=avDevice->getSupportedClockSources(); for ( FFADODevice::ClockSourceVector::const_iterator it = sources.begin(); it != sources.end(); ++it ) { FFADODevice::ClockSource c=*it; printf( " Type: %s, Id: %d, Valid: %d, Active: %d, Description: %s\n", FFADODevice::ClockSourceTypeToString(c.type), c.id, c.valid, c.active, c.description.c_str()); if (c.id==targetid) { s=*it; } } if (s.type != FFADODevice::eCT_Invalid) { printf(" set clock source to %d\n", s.id); if ( ! avDevice->setActiveClockSource( s ) ) { fprintf( stderr, "Could not set clock source\n" ); } } else { printf(" no clock source with id %d found\n", targetid); } } } else { fprintf( stderr, "please specify a node\n" ); } delete m_deviceManager; return exitfunction(0); } else if ( strcmp( arguments.args[0], "SetSplitTimeout" ) == 0 ) { char* tail; int usecs = strtol( arguments.args[1], &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse timeout argument\n" ); return exitfunction(-1); } Ieee1394Service service; // switch off all messages since they mess up the list service.setVerboseLevel(arguments.verbose); if ( !service.initialize( arguments.port ) ) { printf("Could not initialize IEEE 1394 service on port %d\n", (int)arguments.port); return exitfunction(-1); } nodeid_t nodeid; if(arguments.node_id_set) { nodeid = arguments.node_id; } else { nodeid = service.getLocalNodeId(); } if (!service.setSplitTimeoutUsecs(nodeid, usecs)) { printf("Failed to set SPLIT_TIMEOUT to %u for node %X on port %d\n", usecs, nodeid, (int)arguments.port); return exitfunction(-1); } return exitfunction(0); } else if ( strcmp( arguments.args[0], "GetSplitTimeout" ) == 0 ) { Ieee1394Service service; // switch off all messages since they mess up the list service.setVerboseLevel(arguments.verbose); if ( !service.initialize( arguments.port ) ) { printf("Could not initialize IEEE 1394 service on port %d\n", (int)arguments.port); return exitfunction(-1); } nodeid_t nodeid; if(arguments.node_id_set) { nodeid = arguments.node_id; } else { nodeid = service.getLocalNodeId(); } int usecs = service.getSplitTimeoutUsecs(nodeid); if (usecs < 0) { printf("Failed to get SPLIT_TIMEOUT for node %X on port %d\n", nodeid, (int)arguments.port); return exitfunction(-1); } printf("SPLIT_TIMEOUT for node %X on port %d is %u\n", nodeid, (int)arguments.port, usecs); return exitfunction(0); } else if ( strcmp( arguments.args[0], "SytCalcTest" ) == 0 ) { if (arguments.nargs < 4) { fprintf( stderr,"Not enough arguments\n"); return -1; } uint64_t syt_timestamp = strtol(arguments.args[1], NULL, 0); if (errno) { fprintf( stderr,"syt_timestamp parsing failed: %s\n", strerror(errno)); return errno; } uint32_t rcv_cycle = strtol(arguments.args[2], NULL, 0); if (errno) { fprintf( stderr,"rcv_cycle parsing failed: %s\n", strerror(errno)); return errno; } uint64_t ctr_now = strtoll(arguments.args[3], NULL, 0); if (errno) { fprintf( stderr,"ctr_now parsing failed: %s\n", strerror(errno)); return errno; } uint64_t result_rcv = sytRecvToFullTicks(syt_timestamp, rcv_cycle, ctr_now); uint64_t result_xmt = sytXmitToFullTicks(syt_timestamp, rcv_cycle, ctr_now); printf("RCV: 0x%010" PRIX64 " %010" PRIu64 " XMT: 0x%010" PRIX64 " %010" PRIu64 " CTR: %010" PRIu64 "\n", result_rcv, result_rcv, result_xmt, result_xmt, CYCLE_TIMER_TO_TICKS(ctr_now)); } else { fprintf( stderr, "please specify a command\n" ); } } libffado-2.4.5/tests/test-focusrite.cpp0000644000175000001440000001312314206145246017512 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include #include #include "debugmodule/debugmodule.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/cmd_serialize.h" #include "libavc/general/avc_generic.h" #include "libutil/Time.h" #include "libutil/ByteSwap.h" #include "bebob/focusrite/focusrite_cmd.h" #include "bebob/focusrite/focusrite_generic.h" using namespace BeBoB::Focusrite; #include #include #include using namespace std; using namespace AVC; using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 1000 //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-focusrite 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-avccmd -- test program to examine the focusrite vendor dependent commands."; static char args_doc[] = "NODE_ID"; static struct argp_option options[] = { {"verbose", 'v', 0, 0, "Produce verbose output" }, {"port", 'p', "PORT", 0, "Set port" }, {"node", 'n', "NODE", 0, "Set node" }, { 0 } }; struct arguments { arguments() : nargs ( 0 ) , verbose( false ) , test( false ) , port( 0 ) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; bool verbose; bool test; int port; int node; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': arguments->verbose = true; break; case 't': arguments->test = true; break; case 'p': arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'n': arguments->node = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { fprintf( stderr, "Could not parse command line\n" ); exit(-1); } errno = 0; Ieee1394Service *m_1394Service = new Ieee1394Service(); if ( !m_1394Service ) { debugFatal( "Could not create Ieee1349Service object\n" ); return false; } if ( !m_1394Service->initialize( arguments.port ) ) { debugFatal( "Could not initialize Ieee1349Service object\n" ); delete m_1394Service; m_1394Service = 0; return false; } FocusriteVendorDependentCmd cmd( *m_1394Service ); cmd.setVerbose( DEBUG_LEVEL_NORMAL ); #define TOTAL_IDS_TO_SCAN 110 uint32_t old_vals[TOTAL_IDS_TO_SCAN+1]; m_1394Service->setVerboseLevel(DEBUG_LEVEL_INFO); while(1) { for (int id=88; idread_quadlet(nodeId, addr, &value))) { SleepRelativeUsec(10000); } if (!retval) { debugError( " read from %16" PRIX64 " failed (id: %d)\n", addr, id); } else { value = CondSwapFromBus32(value); if (old_vals[id] != value) { printf("%04d changed from %08X to %08X\n", id, old_vals[id], value); old_vals[id] = value; } } } SleepRelativeUsec(1000000); } delete m_1394Service; return 0; } libffado-2.4.5/tests/test-fw410.cpp0000644000175000001440000001467014206145246016360 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include using namespace std; //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-fw410 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-fw410 -- test program to get the fw410 streaming"; static char args_doc[] = "NODE_ID"; static struct argp_option options[] = { {"verbose", 'v', 0, 0, "Produce verbose output" }, {"port", 'p', "PORT", 0, "Set port" }, { 0 } }; struct arguments { arguments() : verbose( false ) , test( false ) , port( 0 ) { args[0] = 0; } char* args[1]; bool verbose; bool test; int port; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; switch (key) { case 'v': arguments->verbose = true; break; case 't': arguments->test = true; break; case 'p': errno = 0; arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case ARGP_KEY_ARG: if (state->arg_num >= 1) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; break; case ARGP_KEY_END: if (state->arg_num < 1) { // Not enough arguments. argp_usage (state); } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { // arg parsing argp_parse (&argp, argc, argv, 0, 0, &arguments); errno = 0; char* tail; int iNodeId = strtol(arguments.args[0], &tail, 0); if (errno) { perror("argument parsing failed:"); return -1; } raw1394handle_t pHandle = raw1394_new_handle_on_port( arguments.port ); if ( !pHandle ) { if ( !errno ) { cerr << "libraw1394 not compatible" << endl; } else { perror( "Could not get 1394 handle" ); cerr << "Is ieee1394 and raw1394 driver loaded?" << endl; } return -1; } struct Connection { int m_output; int m_oplug; int m_input; int m_iplug; int m_iBandwith; int m_iIsoChannel; }; int iLocalId = raw1394_get_local_id( pHandle ); int iRemoteId = iNodeId | 0xffc0; Connection cons[] = { // output, oplug, input, iplug, bandwith, iso channel { iRemoteId, 0, iLocalId, -1, 0x148, -1 }, // oPCR[0] { iRemoteId, 1, iLocalId, -1, 0x148, -1 }, // oPCR[1] // { iRemoteId, 2, iLocalId, -1, 0, -1 }, // oPCR[2]: cmp not supported { iLocalId, -1, iRemoteId, 0, 0x148, -1 }, // iPCR[0] { iLocalId, -1, iRemoteId, 1, 0x148, -1 }, // iPCR[1] // { iLocalId, -1, iRemoteId, 2, 0, -1 }, // iPCR[2]: cmp not supported }; printf( "local node id %d\n", iLocalId & ~0xffc0); printf( "remote node id %d\n", iRemoteId & ~0xffc0); for ( unsigned int i = 0; i < sizeof( cons ) / sizeof( cons[0] ); ++i ) { Connection* pCons = &cons[i]; // the bandwith calculation fails, so its better to use // some default values. pCons->m_iBandwith = iec61883_cmp_calc_bandwidth ( pHandle, pCons->m_output, pCons->m_oplug, IEC61883_DATARATE_400 ); sleep(1); pCons->m_iIsoChannel = iec61883_cmp_connect( pHandle, pCons->m_output, &pCons->m_oplug, pCons->m_input, &pCons->m_iplug, &pCons->m_iBandwith ); printf( "%2d -> %2d %cPCR[%2d]: bw = %4d, ch = %2d\n", pCons->m_output & ~0xffc0, pCons->m_input & ~0xffc0, pCons->m_oplug == -1? 'i' : 'o', pCons->m_oplug == -1? pCons->m_iplug: pCons->m_oplug, pCons->m_iBandwith, pCons->m_iIsoChannel ); sleep(1); } sleep( 3 ); for ( unsigned int i = 0; i < sizeof( cons ) / sizeof( cons[0] ); ++i ) { Connection* pCons = &cons[i]; if ( pCons->m_iIsoChannel != -1 ) { iec61883_cmp_disconnect( pHandle, pCons->m_output, pCons->m_oplug, pCons->m_input, pCons->m_iplug, pCons->m_iIsoChannel, pCons->m_iBandwith ); } } raw1394_destroy_handle( pHandle ); return 0; } libffado-2.4.5/tests/test-ieee1394service.cpp0000644000175000001440000003357614206145246020336 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include "src/debugmodule/debugmodule.h" #include "libutil/ByteSwap.h" #include "src/libieee1394/cycletimer.h" #include "src/libieee1394/configrom.h" #include "src/libieee1394/ieee1394service.h" #include "src/libutil/Thread.h" #include "src/libutil/Functors.h" #include "src/libutil/PosixThread.h" #include #include "libutil/Time.h" #define NB_THREADS 1 #define THREAD_RT true #define THREAD_PRIO 51 #define THREAD_SLEEP_US 50000 #define DISP_CYCLE_SLEEP_SECS 2 using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define DIFF_CONSIDERED_LARGE (TICKS_PER_CYCLE/2) int PORT_TO_USE = 0; int VERBOSE_LEVEL = 4; int max_diff=-99999; int min_diff= 99999; int run=1; static void sighandler (int sig) { run = 0; } class MyFunctor : public Functor { public: MyFunctor() {} virtual ~MyFunctor() {} void operator() () { printf("hello from the functor (%p)\n", this); }; virtual bool matchCallee(void *) {return false;}; }; class CtrThread : public Util::RunnableInterface { public: CtrThread(Ieee1394Service *s) : m_service(s) {}; virtual ~CtrThread() {}; virtual bool Init() { debugOutput(DEBUG_LEVEL_NORMAL, "(%p) Init\n", this); ctr = 0; ctr_dll = 0; ctr_prev = 0; ctr_dll_prev = 0; nb_checks = 0; summed_diff = 0; avg_diff = 0; m_reset_avg = 1; m_handle = raw1394_new_handle_on_port( PORT_TO_USE ); 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; } return true; } virtual bool Execute(); Ieee1394Service *m_service; raw1394handle_t m_handle; uint64_t ctr; uint64_t ctr_dll; uint64_t ctr_prev; uint64_t ctr_dll_prev; uint64_t nb_checks; int64_t summed_diff; double avg_diff; int m_reset_avg; }; bool CtrThread::Execute() { debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "(%p) Execute\n", this); SleepRelativeUsec(THREAD_SLEEP_US); uint32_t cycle_timer; uint64_t local_time; uint32_t cycle_timer2; uint64_t local_time2; uint64_t ticks1, ticks2; int err; do { // read the CTR 'raw' from a handle // and read it from the 1394 service, which uses a DLL err = raw1394_read_cycle_timer(m_handle, &cycle_timer2, &local_time2); err = raw1394_read_cycle_timer(m_handle, &cycle_timer, &local_time); ticks1 = CYCLE_TIMER_TO_TICKS(cycle_timer); ticks2 = CYCLE_TIMER_TO_TICKS(cycle_timer2); } while (diffTicks(ticks1, ticks2) < 0); ctr_prev = ctr; ctr_dll_prev = ctr_dll; ctr = CYCLE_TIMER_TO_TICKS( cycle_timer ); ctr_dll = m_service->getCycleTimerTicks(local_time); if(err) { debugError("(%p) CTR read error\n", this); } debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "(%p) Cycle timer: %011" PRIu64 " (%03us %04ucy %04uticks)\n", this, ctr, (unsigned int)TICKS_TO_SECS( ctr ), (unsigned int)TICKS_TO_CYCLES( ctr ), (unsigned int)TICKS_TO_OFFSET( ctr ) ); debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "(%p) from DLL: %011" PRIu64 " (%03us %04ucy %04uticks)\n", this, ctr_dll, (unsigned int)TICKS_TO_SECS( ctr_dll ), (unsigned int)TICKS_TO_CYCLES( ctr_dll ), (unsigned int)TICKS_TO_OFFSET( ctr_dll ) ); int64_t diff = diffTicks(ctr, ctr_dll); uint64_t abs_diff; // for jitter plots // debugOutput(DEBUG_LEVEL_NORMAL, "9876543210: %lld\n", diff); if(m_reset_avg) { m_reset_avg = 0; summed_diff = 0; nb_checks = 0; } // not 100% thread safe, but will do if (diff > max_diff) max_diff = diff; if (diff < min_diff) min_diff = diff; summed_diff += diff; nb_checks++; avg_diff = ((double)summed_diff)/((double)nb_checks); if (diff < 0) { abs_diff = -diff; } else { abs_diff = diff; } debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "(%p) diff: %s%011" PRIu64 " (%03us %04ucy %04uticks)\n", this, ((int64_t)abs_diff==diff?" ":"-"), abs_diff, (unsigned int)TICKS_TO_SECS( abs_diff ), (unsigned int)TICKS_TO_CYCLES( abs_diff ), (unsigned int)TICKS_TO_OFFSET( abs_diff ) ); if (abs_diff > DIFF_CONSIDERED_LARGE) { debugWarning("(%p) Alert, large diff: %" PRId64 "\n", this, diff); debugOutput ( DEBUG_LEVEL_NORMAL, "(%p) Cycle timer: %011" PRIu64 " (%03us %04ucy %04uticks)\n", this, ctr, (unsigned int)TICKS_TO_SECS( ctr ), (unsigned int)TICKS_TO_CYCLES( ctr ), (unsigned int)TICKS_TO_OFFSET( ctr ) ); debugOutput ( DEBUG_LEVEL_NORMAL, "(%p) from DLL: %011" PRIu64 " (%03us %04ucy %04uticks)\n", this, ctr_dll, (unsigned int)TICKS_TO_SECS( ctr_dll ), (unsigned int)TICKS_TO_CYCLES( ctr_dll ), (unsigned int)TICKS_TO_OFFSET( ctr_dll ) ); } diff = diffTicks(ctr, ctr_prev); if (diff < 0) { debugWarning("(%p) Alert, non-monotonic ctr (direct): %" PRId64 " - %" PRId64 " = %" PRId64 "\n", this, ctr, ctr_prev, diff); debugOutput ( DEBUG_LEVEL_NORMAL, "(%p) Cycle timer now : %011" PRIu64 " (%03us %04ucy %04uticks)\n", this, ctr, (unsigned int)TICKS_TO_SECS( ctr ), (unsigned int)TICKS_TO_CYCLES( ctr ), (unsigned int)TICKS_TO_OFFSET( ctr ) ); debugOutput ( DEBUG_LEVEL_NORMAL, "(%p) Cycle timer prev: %011" PRIu64 " (%03us %04ucy %04uticks)\n", this, ctr_prev, (unsigned int)TICKS_TO_SECS( ctr_prev ), (unsigned int)TICKS_TO_CYCLES( ctr_prev ), (unsigned int)TICKS_TO_OFFSET( ctr_prev ) ); } diff = diffTicks(ctr_dll, ctr_dll_prev); if (diff < 0) { debugWarning("(%p) Alert, non-monotonic ctr (dll): %" PRId64 " - %" PRId64 " = %" PRId64 "\n", this, ctr_dll, ctr_dll_prev, diff); debugOutput ( DEBUG_LEVEL_NORMAL, "(%p) Cycle timer now : %011" PRIu64 " (%03us %04ucy %04uticks)\n", this, ctr_dll, (unsigned int)TICKS_TO_SECS( ctr_dll ), (unsigned int)TICKS_TO_CYCLES( ctr_dll ), (unsigned int)TICKS_TO_OFFSET( ctr_dll ) ); debugOutput ( DEBUG_LEVEL_NORMAL, "(%p) Cycle timer prev: %011" PRIu64 " (%03us %04ucy %04uticks)\n", this, ctr_dll_prev, (unsigned int)TICKS_TO_SECS( ctr_dll_prev ), (unsigned int)TICKS_TO_CYCLES( ctr_dll_prev ), (unsigned int)TICKS_TO_OFFSET( ctr_dll_prev ) ); } // check some calculations uint32_t tmp_orig = m_service->getCycleTimer(); uint32_t tmp_ticks = CYCLE_TIMER_TO_TICKS(tmp_orig); uint32_t tmp_ctr = TICKS_TO_CYCLE_TIMER(tmp_ticks); if (tmp_orig != tmp_ctr) { debugError("CTR => TICKS => CTR failed\n"); debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "(%p) orig CTR : %08X (%03us %04ucy %04uticks)\n", this, (uint32_t)tmp_orig, (unsigned int)CYCLE_TIMER_GET_SECS( tmp_orig ), (unsigned int)CYCLE_TIMER_GET_CYCLES( tmp_orig ), (unsigned int)CYCLE_TIMER_GET_OFFSET( tmp_orig ) ); debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "(%p) TICKS: %011" PRIu32 " (%03us %04ucy %04uticks)\n", this, tmp_ticks, (unsigned int)TICKS_TO_SECS( tmp_ticks ), (unsigned int)TICKS_TO_CYCLES( tmp_ticks ), (unsigned int)TICKS_TO_OFFSET( tmp_ticks ) ); debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "(%p) new CTR : %08X (%03us %04ucy %04uticks)\n", this, (uint32_t)tmp_ctr, (unsigned int)CYCLE_TIMER_GET_SECS( tmp_ctr ), (unsigned int)CYCLE_TIMER_GET_CYCLES( tmp_ctr ), (unsigned int)CYCLE_TIMER_GET_OFFSET( tmp_ctr ) ); } debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "(%p) wait...\n", this); return true; } int main(int argc, char *argv[]) { int i=0; signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); static struct option long_opts[] = { { "port", 1, 0, 'p' }, { "verbose", 1, 0, 'v' }, { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; int optindex = 0; while(1) { int c = getopt_long( argc, argv, "p:h", long_opts, &optindex ); if(c==-1) break; switch(c) { case 'p': PORT_TO_USE = atoi( optarg ); break; case 'v': VERBOSE_LEVEL = atoi( optarg ); break; case 'h': printf( "USAGE:\n\ Currently two options are understood:\n\ --port or -p selects the FireWire-port to use, default is 0\n\ --verbose or -v selects the verbose level, default is 4\n\ --help or -h shows this help and exits.\n\ " ); return 0; break; } } printf("FFADO Ieee1394Service test application\n"); printf(" Using port %d\n", PORT_TO_USE); printf(" Verbose level %d\n", VERBOSE_LEVEL); setDebugLevel(VERBOSE_LEVEL); Ieee1394Service *m_service=NULL; m_service = new Ieee1394Service(); m_service->setVerboseLevel(VERBOSE_LEVEL); if(!m_service->initialize(PORT_TO_USE)) { printf("Could not initialize 1394 service\n"); delete m_service; exit(-1); } m_service->setThreadParameters(true, 1); MyFunctor *test_busreset=new MyFunctor(); printf(" adding (%p) as busreset handler\n", test_busreset); m_service->addBusResetHandler(test_busreset); nodeaddr_t addr = m_service->findFreeARMBlock(0x0000FFFFE0000000ULL, 4, 4 ); Ieee1394Service::ARMHandler *test_arm = new Ieee1394Service::ARMHandler(*m_service, addr, 4, RAW1394_ARM_READ | RAW1394_ARM_WRITE | RAW1394_ARM_LOCK, RAW1394_ARM_READ | RAW1394_ARM_WRITE | RAW1394_ARM_LOCK, 0); printf(" adding (%p) as arm handler\n", test_arm); if (!m_service->registerARMHandler(test_arm)) { printf(" failed\n"); } addr = m_service->findFreeARMBlock(0x0000FFFFE0000000ULL, 4, 4 ); Ieee1394Service::ARMHandler *test_arm2 = new Ieee1394Service::ARMHandler(*m_service, addr, 4, RAW1394_ARM_READ | RAW1394_ARM_WRITE | RAW1394_ARM_LOCK, RAW1394_ARM_READ | RAW1394_ARM_WRITE | RAW1394_ARM_LOCK, 0); printf(" adding (%p) as arm handler\n", test_arm2); if (!m_service->registerARMHandler(test_arm2)) { printf(" failed\n"); } CtrThread *thread_runners[NB_THREADS]; Thread* threads[NB_THREADS]; for (i=0; i < NB_THREADS; i++) { thread_runners[i] = new CtrThread(m_service); if (thread_runners[i] == NULL) { debugError("could not create thread runner %d\n", i); exit(-1); } threads[i] = new PosixThread(thread_runners[i], THREAD_RT, THREAD_PRIO, PTHREAD_CANCEL_DEFERRED); if (threads[i] == NULL) { debugError("could not create thread %d\n", i); exit(-1); } } for (i=0; i < NB_THREADS; i++) { threads[i]->Start(); } int cnt=0; while(run) { cnt++; debugOutput(DEBUG_LEVEL_NORMAL, "%08d: (max: %6d, min: %6d)\n", cnt, max_diff, min_diff); m_service->show(); max_diff = -999999; min_diff = 999999; for (i=0; i < NB_THREADS; i++) { debugOutput(DEBUG_LEVEL_NORMAL, "%2d: avg: %6f\n", i, thread_runners[i]->avg_diff); thread_runners[i]->m_reset_avg = 1; } sleep(DISP_CYCLE_SLEEP_SECS); } for (i=0; i < NB_THREADS; i++) { threads[i]->Stop(); } for (i=0; i < NB_THREADS; i++) { delete threads[i]; delete thread_runners[i]; } delete m_service; delete test_busreset; delete test_arm; delete test_arm2; printf("Bye...\n"); return EXIT_SUCCESS; } libffado-2.4.5/tests/test-ipcringbuffer.cpp0000644000175000001440000001412614206145246020340 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "debugmodule/debugmodule.h" #include "libutil/IpcRingBuffer.h" #include "libutil/SystemTimeSource.h" #include #include #include #include #include using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 2 int run=1; int lastsig=-1; static void sighandler (int sig) { run = 0; } //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-ipcringbuffer 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-avccmd -- test program to test the ipc ringbuffer class."; static char args_doc[] = "DIRECTION"; static struct argp_option options[] = { {"verbose", 'v', "level", 0, "Produce verbose output" }, { 0 } }; struct arguments { arguments() : nargs ( 0 ) , verbose( false ) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; long int verbose; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': if (arg) { arguments->verbose = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: if(arguments->nargs <= 0) { printMessage("not enough arguments\n"); return -1; } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { fprintf( stderr, "Could not parse command line\n" ); exit(-1); } setDebugLevel(arguments.verbose); errno = 0; char* tail; long int direction = strtol( arguments.args[0], &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse direction argument\n" ); exit(-1); } printMessage("Testing shared memory direction %ld\n", direction); #define TEST_SAMPLERATE 44100 #define BUFF_SIZE 64 #define NB_BUFFERS 4 IpcRingBuffer* b = NULL; if(direction == 0) { b = new IpcRingBuffer("testbuff", IpcRingBuffer::eBT_Master, IpcRingBuffer::eD_Outward, IpcRingBuffer::eB_Blocking, NB_BUFFERS, BUFF_SIZE); if(b == NULL) { debugError("Could not create master\n"); exit(-1); } } else { b = new IpcRingBuffer("testbuff", IpcRingBuffer::eBT_Master, IpcRingBuffer::eD_Inward, IpcRingBuffer::eB_Blocking, NB_BUFFERS, BUFF_SIZE); if(b == NULL) { debugError("Could not create master\n"); exit(-1); } } b->setVerboseLevel(arguments.verbose); char buff[BUFF_SIZE]; int cnt = 0; long int time_to_sleep = 1000*1000*BUFF_SIZE/TEST_SAMPLERATE; if(!b->init()) { debugError("Could not init buffer\n"); goto out_err; } run=1; while(run) { if(direction == 0) { snprintf(buff, BUFF_SIZE, "test %d", cnt); if(cnt%1000==0) { printMessage("writing '%s'...\n", buff); } IpcRingBuffer::eResult res = b->Write(buff); if(res != IpcRingBuffer::eR_OK && res != IpcRingBuffer::eR_Again) { debugError("Could not write to segment\n"); goto out_err; } if(res == IpcRingBuffer::eR_Again) { printMessage(" Try again on %d...\n", cnt); } else { cnt++; } usleep(time_to_sleep); } else { if(cnt%1000==0) { printMessage("reading...\n"); } IpcRingBuffer::eResult res = b->Read(buff); if(res != IpcRingBuffer::eR_OK && res != IpcRingBuffer::eR_Again) { debugError("Could not receive from queue\n"); goto out_err; } if(cnt%1000==0) { buff[BUFF_SIZE-1]=0; printMessage(" read: '%s'\n", buff); } if(res == IpcRingBuffer::eR_Again) { printMessage(" Try again on %d...\n", cnt); } else { cnt++; } } } delete b; return 0; out_err: delete b; return -1; } libffado-2.4.5/tests/test-messagequeue.cpp0000644000175000001440000001661014206145246020204 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "debugmodule/debugmodule.h" #include "libutil/PosixMessageQueue.h" #include "libutil/Functors.h" #include #include #include #include #include #include #include using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 2 int run=1; int lastsig=-1; static void sighandler (int sig) { run = 0; } sem_t peep_sem; //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-messagequeue 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-avccmd -- test program to test the message queues."; static char args_doc[] = "DIRECTION"; static struct argp_option options[] = { {"verbose", 'v', "level", 0, "Produce verbose output" }, { 0 } }; struct arguments { arguments() : nargs ( 0 ) , verbose( false ) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; long int verbose; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': if (arg) { arguments->verbose = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: if(arguments->nargs <= 0) { printMessage("not enough arguments\n"); return -1; } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; class TestMessage : public Util::PosixMessageQueue::Message { public: TestMessage() : Message() , m_prio( 0 ) , m_length( 64 ) , m_cnt( 0 ) {}; virtual ~TestMessage() {}; virtual unsigned int getPriority() {return m_prio;}; virtual unsigned int getLength() { return m_length; }; virtual bool serialize(char *buff) { snprintf(buff, m_length, "cnt: %d", m_cnt++); return true; } virtual bool deserialize(const char *buff, unsigned int length, unsigned prio) { char tmp[length+1]; snprintf(tmp, length, "%s", buff); tmp[length]=0; printMessage("got message: '%s', prio %u\n", tmp, prio); m_prio = prio; return true; }; private: unsigned m_prio; unsigned int m_length; int m_cnt; }; void peep() { printMessage("peep...\n"); sem_post(&peep_sem); } /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { int rv=0; signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { fprintf( stderr, "Could not parse command line\n" ); exit(-1); } setDebugLevel(arguments.verbose); errno = 0; char* tail; long int direction = strtol( arguments.args[0], &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse direction argument\n" ); exit(-1); } if(sem_init(&peep_sem, 0, 0)) { debugError("Could not init wait sem\n"); exit(-1); } printMessage("Testing message queue direction %ld\n", direction); Util::Functor* peepfunc = NULL; PosixMessageQueue p = PosixMessageQueue("testqueue1"); p.setVerboseLevel(arguments.verbose); if(direction == 0) { if(!p.Create(PosixMessageQueue::eD_WriteOnly)) { debugError("Could not create queue\n"); exit(-1); } } else { if(!p.Open(PosixMessageQueue::eD_ReadOnly, PosixMessageQueue::eB_NonBlocking)) { debugError("Could not open queue\n"); exit(-1); } peepfunc = new Util::CallbackFunctor0< void (*)() > ( &peep, false ); if ( !peepfunc ) { debugError( "Could not create peep handler\n" ); exit(-1); } if(!p.setNotificationHandler(peepfunc)) { debugError("Could not set Notification Handler\n"); exit(-1); } // if(!p.enableNotification()) { // debugError("Could not enable Notification\n"); // exit(-1); // } } #define TIME_TO_SLEEP 1000*100 TestMessage m = TestMessage(); //m.setVerboseLevel(arguments.verbose); run=1; while(run) { if(direction == 0) { printMessage("sending...\n"); if(p.Send(m) != PosixMessageQueue::eR_OK) { debugError("Could not send to queue\n"); goto out_err; } usleep(TIME_TO_SLEEP); } else { printMessage("receiving...\n"); printMessage(" enable notification...\n"); // first enable notification if(!p.enableNotification()) { debugError("Could not enable Notification\n"); goto out_err; } // then read all there is to read printMessage(" reading...\n"); enum PosixMessageQueue::eResult ret; while((ret = p.Receive(m)) == PosixMessageQueue::eR_OK) { // nothing } if (ret != PosixMessageQueue::eR_OK && ret != PosixMessageQueue::eR_Again) { debugError("Could not receive from queue\n"); goto out_err; } // wait for a notification printMessage(" waiting...\n"); if(sem_wait(&peep_sem)) { printMessage(" error: %s\n", strerror(errno)); goto out_err; } } } cleanup: if(peepfunc) { if(!p.disableNotification()) { debugError("Could not disable Notification\n"); } if(!p.unsetNotificationHandler()) { debugError("Could not unset Notification Handler\n"); exit(-1); } delete peepfunc; } sem_destroy(&peep_sem); return rv; out_err: rv=-1; goto cleanup; } libffado-2.4.5/tests/test-mixer.cpp0000644000175000001440000001741714206145246016645 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "libavc/avc_function_block.h" #include "libavc/avc_serialize.h" #include "debugmodule/debugmodule.h" #include "libieee1394/ieee1394service.h" #include #include DECLARE_GLOBAL_DEBUG_MODULE; const bool bVerbose = true; bool doApp( Ieee1394Service& ieee1394service, int node_id, int fb_id ) { FunctionBlockCmd fbCmd( ieee1394service, FunctionBlockCmd::eFBT_Processing, fb_id, FunctionBlockCmd::eCA_Current ); fbCmd.setNodeId( node_id ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Status ); fbCmd.m_pFBProcessing->m_pEnhancedMixer = new FunctionBlockProcessingEnhancedMixer; debugOutput(DEBUG_LEVEL_NORMAL, "Requesting mixer programmable state...\n"); fbCmd.m_pFBProcessing->m_fbInputPlugNumber = 0x00; fbCmd.m_pFBProcessing->m_inputAudioChannelNumber = 0xff; fbCmd.m_pFBProcessing->m_outputAudioChannelNumber = 0xff; fbCmd.m_pFBProcessing->m_pEnhancedMixer->m_statusSelector = FunctionBlockProcessingEnhancedMixer::eSS_ProgramableState; fbCmd.setVerbose( bVerbose ); if (bVerbose) { ieee1394service.setVerboseLevel( DEBUG_LEVEL_VERY_VERBOSE ); } if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); } if ( bVerbose ) { CoutSerializer se; fbCmd.serialize( se ); } debugOutput(DEBUG_LEVEL_NORMAL, "Requesting mixer level state...\n"); fbCmd.m_pFBProcessing->m_fbInputPlugNumber = 0x00; fbCmd.m_pFBProcessing->m_inputAudioChannelNumber = 0x00; fbCmd.m_pFBProcessing->m_outputAudioChannelNumber = 0x00; fbCmd.m_pFBProcessing->m_pEnhancedMixer->m_statusSelector = FunctionBlockProcessingEnhancedMixer::eSS_Level; if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); } if ( bVerbose ) { CoutSerializer se; fbCmd.serialize( se ); } return true; } bool selectorGet( Ieee1394Service& ieee1394service, int node_id, int fb_id ) { FunctionBlockCmd fbCmd( ieee1394service, FunctionBlockCmd::eFBT_Selector, fb_id, FunctionBlockCmd::eCA_Current ); fbCmd.setNodeId( node_id ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Status ); fbCmd.m_pFBSelector->m_inputFbPlugNumber=0; debugOutput(DEBUG_LEVEL_NORMAL, "Requesting selector state...\n"); fbCmd.setVerbose( bVerbose ); if (bVerbose) { ieee1394service.setVerboseLevel( DEBUG_LEVEL_VERY_VERBOSE ); } if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); } if ( bVerbose ) { CoutSerializer se; fbCmd.serialize( se ); } return true; } bool selectorSet( Ieee1394Service& ieee1394service, int node_id, int fb_id , int val ) { FunctionBlockCmd fbCmd( ieee1394service, FunctionBlockCmd::eFBT_Selector, fb_id, FunctionBlockCmd::eCA_Current ); fbCmd.setNodeId( node_id ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Control ); fbCmd.m_pFBSelector->m_inputFbPlugNumber=val; debugOutput(DEBUG_LEVEL_NORMAL, "Setting selector state to %d...\n", val); fbCmd.setVerbose( bVerbose ); if (bVerbose) { ieee1394service.setVerboseLevel( DEBUG_LEVEL_VERY_VERBOSE ); } if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); } if ( bVerbose ) { CoutSerializer se; fbCmd.serialize( se ); } return true; } bool volumeGet( Ieee1394Service& ieee1394service, int node_id, int fb_id, int channel ) { FunctionBlockCmd fbCmd( ieee1394service, FunctionBlockCmd::eFBT_Feature, fb_id, FunctionBlockCmd::eCA_Current ); fbCmd.setNodeId( node_id ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Status ); fbCmd.m_pFBFeature->m_audioChannelNumber=channel; fbCmd.m_pFBFeature->m_controlSelector=FunctionBlockFeature::eCSE_Feature_Volume; fbCmd.m_pFBFeature->m_pVolume->m_volume=0; debugOutput(DEBUG_LEVEL_NORMAL, "Requesting volume feature block state...\n"); fbCmd.setVerbose( bVerbose ); if (bVerbose) { ieee1394service.setVerboseLevel( DEBUG_LEVEL_VERY_VERBOSE ); } if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); } if ( bVerbose ) { CoutSerializer se; fbCmd.serialize( se ); } return true; } bool volumeSet( Ieee1394Service& ieee1394service, int node_id, int fb_id, int channel, int value ) { FunctionBlockCmd fbCmd( ieee1394service, FunctionBlockCmd::eFBT_Feature, fb_id, FunctionBlockCmd::eCA_Current ); fbCmd.setNodeId( node_id ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Control ); fbCmd.m_pFBFeature->m_audioChannelNumber=channel; fbCmd.m_pFBFeature->m_controlSelector=FunctionBlockFeature::eCSE_Feature_Volume; fbCmd.m_pFBFeature->m_pVolume->m_volume=value; debugOutput(DEBUG_LEVEL_NORMAL, "Setting volume feature block channel %d state to %d...\n", channel, value); fbCmd.setVerbose( bVerbose ); if (bVerbose) { ieee1394service.setVerboseLevel( DEBUG_LEVEL_VERY_VERBOSE ); } if ( !fbCmd.fire() ) { debugError( "cmd failed\n" ); } if ( bVerbose ) { CoutSerializer se; fbCmd.serialize( se ); } return true; } /////////////////////////// // main ////////////////////////// int main( int argc, char **argv ) { if (argc < 4) { debugError("usage: PORT NODE_ID CMD FB_ID [VAL1] [VAL2]\n"); exit(0); } errno = 0; char* tail; int port = strtol( argv[1], &tail, 0 ); int node_id = strtol( argv[2], &tail, 0 ); int cmd = strtol( argv[3], &tail, 0 ); int fb_id = strtol( argv[4], &tail, 0 ); int value1=-1; int value2=-1; if (argc>=6) value1 = strtol(argv[5], &tail, 0 ); if (argc>=7) value2 = strtol(argv[6], &tail, 0 ); if (errno) { debugError("argument parsing failed: %s\n", strerror(errno)); return -1; } Ieee1394Service ieee1394service; if ( !ieee1394service.initialize( port ) ) { debugError( "could not set port on ieee1394service\n" ); return -1; } switch(cmd) { case 0: doApp( ieee1394service, node_id, fb_id ); break; case 1: selectorGet( ieee1394service, node_id, fb_id ); break; case 2: selectorSet( ieee1394service, node_id, fb_id , value1 ); break; case 3: volumeGet( ieee1394service, node_id, fb_id, value1); break; case 4: volumeSet( ieee1394service, node_id, fb_id, value1, value2); break; } return 0; } libffado-2.4.5/tests/test-pan.cpp0000644000175000001440000001102414206145246016263 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "libavc/audiosubunit/avc_function_block.h" #include "libutil/serialize.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include const bool bVerbose = false; using namespace AVC; using namespace Util; using namespace Util::Cmd; short int getPan( Ieee1394Service& ieee1394service, int node_id, int ffb_id, FunctionBlockCmd::EControlAttribute control_attrib ) { FunctionBlockCmd fbCmd( ieee1394service, FunctionBlockCmd::eFBT_Feature, ffb_id, control_attrib ); fbCmd.setNodeId( node_id ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Status ); fbCmd.m_pFBFeature->m_audioChannelNumber = 2; fbCmd.m_pFBFeature->m_controlSelector=FunctionBlockFeature::eCSE_Feature_LRBalance; AVC::FunctionBlockFeatureLRBalance lr; fbCmd.m_pFBFeature->m_pLRBalance = lr.clone(); fbCmd.m_pFBFeature->m_pLRBalance->m_lrBalance = 0; fbCmd.setVerbose( bVerbose ); if (bVerbose) { ieee1394service.setVerboseLevel( DEBUG_LEVEL_VERBOSE ); } if ( !fbCmd.fire() ) { printf( "cmd failed\n" ); } if ( bVerbose ) { CoutSerializer se; fbCmd.serialize( se ); } return fbCmd.m_pFBFeature->m_pLRBalance->m_lrBalance; } bool setPan( Ieee1394Service& ieee1394service, int node_id, int ffb_id, int pan ) { FunctionBlockCmd fbCmd( ieee1394service, FunctionBlockCmd::eFBT_Feature, ffb_id, FunctionBlockCmd::eCA_Current ); fbCmd.setNodeId( node_id ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Control ); fbCmd.m_pFBFeature->m_audioChannelNumber = 2; fbCmd.m_pFBFeature->m_controlSelector=FunctionBlockFeature::eCSE_Feature_LRBalance; AVC::FunctionBlockFeatureLRBalance lr; fbCmd.m_pFBFeature->m_pLRBalance = lr.clone(); fbCmd.m_pFBFeature->m_pLRBalance->m_lrBalance = pan; fbCmd.setVerbose( bVerbose ); if (bVerbose) { ieee1394service.setVerboseLevel( DEBUG_LEVEL_VERBOSE ); } bool bStatus = fbCmd.fire(); if ( !bStatus ) { printf( "cmd failed\n" ); } if ( bVerbose ) { CoutSerializer se; fbCmd.serialize( se ); } return bStatus; } bool doApp( Ieee1394Service& ieee1394service, int node_id, int fb_id, int pan ) { short int maxPan = getPan( ieee1394service, node_id, fb_id, FunctionBlockCmd::eCA_Maximum ); short int minPan = getPan( ieee1394service, node_id, fb_id, FunctionBlockCmd::eCA_Minimum ); short int curPan = getPan( ieee1394service, node_id, fb_id, FunctionBlockCmd::eCA_Current ); printf( "max pan value = %d\n", maxPan ); printf( "min pan value = %d\n", minPan ); printf( "old pan value = %d\n", curPan ); //setPan( ieee1394service, node_id, fb_id, pan ); curPan = getPan( ieee1394service, node_id, fb_id, FunctionBlockCmd::eCA_Current ); printf( "new pan value = %d\n", curPan ); return true; } /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { if (argc < 4) { printf("usage: NODE_ID FB_ID PAN\n"); exit(0); } errno = 0; char* tail; int node_id = strtol( argv[1], &tail, 0 ); int fb_id = strtol( argv[2], &tail, 0 ); int pan = strtol( argv[3], &tail, 0 ); if (errno) { perror("argument parsing failed:"); return -1; } Ieee1394Service ieee1394service; if ( !ieee1394service.initialize( 0 ) ) { fprintf( stderr, "could not set port on ieee1394service\n" ); return -1; } doApp( ieee1394service, node_id, fb_id, pan ); return 0; } libffado-2.4.5/tests/test-scs.cpp0000644000175000001440000004640514206145246016310 0ustar jwoitheusers/* * Copyright (C) 2005-2009 by Pieter Palmers * Copyright (C) 2005-2008 by Daniel Wagner * * sysex-buffering: Copyright (C) 2012 by Rob Bothof * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include #include #include "debugmodule/debugmodule.h" #include "devicemanager.h" #include "libieee1394/configrom.h" #include "libieee1394/ieee1394service.h" #include "libutil/Configuration.h" #include "libutil/SystemTimeSource.h" #include "genericavc/stanton/scs.h" using namespace GenericAVC; using namespace GenericAVC::Stanton; #include #include #include #include #include #include #include #define ALSA_SEQ_BUFF_SIZE 32 #define MIDI_TRANSMIT_BUFFER_SIZE 512 using namespace std; using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 1000 int run; snd_seq_t *m_seq_handle = NULL; static void sighandler(int sig) { run = 0; // send an event to wake the iterator loop if(m_seq_handle) { snd_seq_nonblock(m_seq_handle, 1); } } //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-scs 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-scs -- test program to test the Stanton SCS code."; static char args_doc[] = "NODE_ID"; static struct argp_option options[] = { {"verbose", 'v', "LEVEL", 0, "Produce verbose output" }, {"port", 'p', "PORT", 0, "Set port" }, {"node", 'n', "NODE", 0, "Set node" }, {"sysexbuf", 's', "1", 0, "Enable Sysex Buffering of 1byte messages" }, { 0 } }; struct arguments { arguments() : nargs ( 0 ) , verbose( DEBUG_LEVEL_NORMAL ) , test( false ) , port( -1 ) , node( -1 ) , sysexbuf ( 0 ) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; int verbose; bool test; int port; int node; int sysexbuf; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': arguments->verbose = strtol(arg, &tail, 0); break; case 't': arguments->test = true; break; case 'p': arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 'n': arguments->node = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case 's': arguments->sysexbuf = strtol(arg, &tail, 0); break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: if(arguments->nargs<0) { printf("not enough arguments\n"); return -1; } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; class HSS1394AlsaSeqMidiBridge { class HSS1394UserDataHandler; public: HSS1394AlsaSeqMidiBridge(snd_seq_t *seq_handle, GenericAVC::Stanton::ScsDevice &device) : m_seq_handle (seq_handle) , m_device( device ) , m_name( "UNSPECIFIED" ) , m_out_port_nr( -1 ) , m_out_parser( NULL ) , m_input_handler( NULL ) {}; virtual ~HSS1394AlsaSeqMidiBridge() { if(m_input_handler) { // remove the handler if(!m_device.m_hss1394handler->removeMessageHandler(GenericAVC::Stanton::ScsDevice::eMT_UserData, m_input_handler)) { debugError("Could not register input message handler\n"); } delete m_input_handler; m_input_handler = NULL; } if(m_out_port_nr >= 0) { snd_seq_delete_simple_port(m_seq_handle, m_out_port_nr); m_out_port_nr = -1; } if(m_out_parser) { snd_midi_event_free(m_out_parser); m_out_parser = NULL; } }; bool init() { // need local copy as the nickname can change m_name = m_device.getNickname(); //m_name="SCS-plug"; // create the output port m_out_port_nr = snd_seq_create_simple_port(m_seq_handle, m_name.c_str(), SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC); if(m_out_port_nr < 0) { debugError("Could not create ALSA Sequencer port\n"); return false; } // create the output message encoder if(snd_midi_event_new( ALSA_SEQ_BUFF_SIZE, &m_out_parser) < 0) { debugError("could not init output event encoder"); return false; } // disable running-status snd_midi_event_no_status(m_out_parser, 1); // create a handler HSS1394UserDataHandler *m_input_handler = new HSS1394UserDataHandler(*this); if(m_input_handler == NULL) { debugError("Error creating handler."); return false; } if(!m_input_handler->init(m_name)) { debugError("Could not init input message handler\n"); return false; } if(!m_device.m_hss1394handler->addMessageHandler(GenericAVC::Stanton::ScsDevice::eMT_UserData, m_input_handler)) { debugError("Could not register input message handler\n"); return false; } return true; }; bool sendAlsaSeqEvent(snd_seq_event_t *ev) { int bytes_to_send = 0; // decode it to the work buffer if((bytes_to_send = snd_midi_event_decode ( m_out_parser, m_work_buffer, MIDI_TRANSMIT_BUFFER_SIZE, ev)) < 0) { // failed to decode debugError(" Error decoding event for port %d (errcode=%d)", ev->dest.port, bytes_to_send); return false; } else { if (bytes_to_send == 1 && arguments.sysexbuf==1) { switch(m_work_buffer[0]) { case 0xF0: sysex_buffer_count=1; m_sysex_buffer[sysex_buffer_count-1]=m_work_buffer[0]; debugOutput(DEBUG_LEVEL_VERBOSE, "Sysexstart-Buffering\n"); break; case 0xf7: sysex_buffer_count++; m_sysex_buffer[sysex_buffer_count-1]=m_work_buffer[0]; debugOutput(DEBUG_LEVEL_VERBOSE,"SysexEnd-SendBuffer...length: %d\n",sysex_buffer_count); if(!m_device.writeHSS1394Message(GenericAVC::Stanton::ScsDevice::eMT_UserData, m_sysex_buffer, sysex_buffer_count+1)) { debugError("Failed to send message\n"); return false; } sysex_buffer_count=0; break; case 0xF9: case 0xFA: case 0xFB: case 0xFC: case 0xFD: case 0xFE: case 0xFF: debugOutput(DEBUG_LEVEL_VERBOSE,"Clock or other 1byte message received, send without buffering\n"); if(!m_device.writeHSS1394Message(GenericAVC::Stanton::ScsDevice::eMT_UserData, m_work_buffer,bytes_to_send )) { debugError("Failed to send message\n"); return false; } break; default: if (sysex_buffer_count > 0) { //yes we are buffering sysex_buffer_count++; m_sysex_buffer[sysex_buffer_count-1]=m_work_buffer[0]; } } } else if(!m_device.writeHSS1394Message(GenericAVC::Stanton::ScsDevice::eMT_UserData, m_work_buffer,bytes_to_send )) { debugError("Failed to send message\n"); return false; } } return true; }; int getAlsaSeqOutputPortNumber() { return m_out_port_nr; } int getAlsaSeqInputPortNumber() { if (m_input_handler) { return m_input_handler->getAlsaSeqPortNumber(); } else { debugError("input handler not initialized yet"); return -1; } } void setVerboseLevel(int i) { setDebugLevel(i); }; private: snd_seq_t *m_seq_handle; GenericAVC::Stanton::ScsDevice &m_device; std::string m_name; int m_out_port_nr; snd_midi_event_t *m_out_parser; HSS1394UserDataHandler *m_input_handler; unsigned char m_work_buffer[MIDI_TRANSMIT_BUFFER_SIZE]; unsigned char m_sysex_buffer[MIDI_TRANSMIT_BUFFER_SIZE]; int sysex_buffer_count; DECLARE_DEBUG_MODULE; private: // the class that handles the async messages from the HSS1394 node class HSS1394UserDataHandler : public GenericAVC::Stanton::ScsDevice::HSS1394Handler::MessageFunctor { public: HSS1394UserDataHandler(HSS1394AlsaSeqMidiBridge &parent) : m_parent( parent ) , m_ready(false) , m_seq_port_nr( -1 ) , m_parser( NULL ) , m_debugModule(parent.m_debugModule) {}; virtual ~HSS1394UserDataHandler() { if(m_seq_port_nr >= 0) { snd_seq_delete_simple_port(m_parent.m_seq_handle, m_seq_port_nr); m_seq_port_nr = -1; } if(m_parser) { snd_midi_event_free(m_parser); m_parser = NULL; } m_ready = false; } bool init(std::string name) { m_seq_port_nr = snd_seq_create_simple_port(m_parent.m_seq_handle, name.c_str(), SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC); if(m_seq_port_nr < 0) { debugError("Could not create ALSA Sequencer port\n"); return false; } if(snd_midi_event_new( ALSA_SEQ_BUFF_SIZE, &m_parser) < 0) { debugError("could not init parser"); return false; } m_ready = true; return m_ready; } int getAlsaSeqPortNumber() { return m_seq_port_nr; } virtual void operator() (byte_t *buff, size_t len) { if (m_ready) { debugOutput(DEBUG_LEVEL_VERBOSE, "got message len %zd\n", len); for (size_t s=0; s < len; s++) { byte_t *byte = (buff+s); snd_seq_event_t ev; if ((snd_midi_event_encode_byte(m_parser, (*byte) & 0xFF, &ev)) > 0) { // a midi message is complete, send it out to ALSA snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); snd_seq_ev_set_source(&ev, m_seq_port_nr); snd_seq_event_output_direct(m_parent.m_seq_handle, &ev); } } } else { debugError("Not ready\n"); } }; private: HSS1394AlsaSeqMidiBridge &m_parent; bool m_ready; int m_seq_port_nr; snd_midi_event_t *m_parser; DECLARE_DEBUG_MODULE_REFERENCE; }; }; IMPL_DEBUG_MODULE( HSS1394AlsaSeqMidiBridge, HSS1394AlsaSeqMidiBridge, DEBUG_LEVEL_NORMAL ); /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { printMessage("Could not parse command line\n" ); exit(-1); } errno = 0; run=1; signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); DeviceManager *m_deviceManager = new DeviceManager(); if ( !m_deviceManager ) { printMessage("Could not allocate device manager\n" ); return -1; } if ( arguments.verbose ) { setDebugLevel(arguments.verbose); m_deviceManager->setVerboseLevel(arguments.verbose); } if ( !m_deviceManager->initialize() ) { printMessage("Could not initialize device manager\n" ); delete m_deviceManager; return -1; } char s[1024]; if(arguments.port > -1 && arguments.node > -1) { snprintf(s, 1024, "hw:%d,%d", arguments.port, arguments.node); if ( !m_deviceManager->addSpecString(s) ) { printMessage("Could not add spec string %s to device manager\n", s ); delete m_deviceManager; return -1; } } else if (arguments.port > -1) { snprintf(s, 1024, "hw:%d", arguments.port); if ( !m_deviceManager->addSpecString(s) ) { printMessage("Could not add spec string %s to device manager\n", s ); delete m_deviceManager; return -1; } } if ( !m_deviceManager->discover(false) ) { printMessage("Could not discover devices\n" ); delete m_deviceManager; return -1; } // loop over all discovered devices and extract the SCS devices int nb_devices = m_deviceManager->getAvDeviceCount(); if(nb_devices == 0) { printMessage("No devices found\n"); delete m_deviceManager; return -1; } typedef std::vector ScsDeviceVector; typedef std::vector::iterator ScsDeviceVectorIterator; ScsDeviceVector scsDevices; for(int i=0; igetAvDeviceByIndex(i); GenericAVC::Stanton::ScsDevice* scsDevice = dynamic_cast(device); if(scsDevice == NULL) { printMessage("Device %d (GUID: %s) is not a Stanton SCS device\n", i, device->getConfigRom().getGuidString().c_str() ); } else { printMessage("Device %d (GUID: %s) is a Stanton SCS device\n", i, device->getConfigRom().getGuidString().c_str() ); scsDevices.push_back(scsDevice); } } if(scsDevices.size() == 0) { printMessage("No SCS devices found\n"); delete m_deviceManager; return -1; } // open the alsa sequencer if (snd_seq_open(&m_seq_handle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) { debugError("Error opening ALSA sequencer."); delete m_deviceManager; return -1; } snd_seq_set_client_name(m_seq_handle, "STANTON-SCS1"); // this maps the alsa sequencer ports to the corresponding bridges typedef std::map BridgeMap; typedef std::map::iterator BridgeMapIterator; BridgeMap seqport2bridgemap; for ( ScsDeviceVectorIterator it = scsDevices.begin(); it != scsDevices.end(); ++it ) { HSS1394AlsaSeqMidiBridge *bridge = new HSS1394AlsaSeqMidiBridge(m_seq_handle, **it); if(bridge == NULL) { debugError("Could not allocate HSS1394 <=> ALSA bridge\n"); delete m_deviceManager; return -1; } bridge->setVerboseLevel(arguments.verbose); if(!bridge->init()) { debugError("Could not init HSS1394 <=> ALSA bridge\n"); delete m_deviceManager; return -1; } int portNumber = bridge->getAlsaSeqOutputPortNumber(); #ifdef DEBUG if(portNumber < -1) { debugError("BUG: port should be >= 0 after init\n"); } #endif BridgeMapIterator it2 = seqport2bridgemap.find(portNumber); if(it2 == seqport2bridgemap.end()) { seqport2bridgemap[portNumber] = bridge; } else { debugError("BUG: port already present in bridge map, duplicate port.\n"); delete bridge; } } // enter a wait loop printMessage(" >>> Entering wait loop, use CTRL-C to exit... \n" ); while(run) { snd_seq_event_t *ev; int err = 0; // get next event, if one is present, blocks until an event is received err = snd_seq_event_input(m_seq_handle, &ev); if(err > 0 && ev) { debugOutput(DEBUG_LEVEL_VERBOSE, "Got event...\n"); if (ev->source.client == SND_SEQ_CLIENT_SYSTEM) continue; // figure out what bridge this is intended for BridgeMapIterator it = seqport2bridgemap.find(ev->dest.port); if(it != seqport2bridgemap.end()) { HSS1394AlsaSeqMidiBridge *bridge = (*it).second; if(bridge) { if(!bridge->sendAlsaSeqEvent(ev)) { debugError("Failed to send event to HSS1394 node\n"); } } else { debugError("Bogus bridge in seqport2bridgemap\n"); } } else { debugWarning("Received message for unknown port\n"); } } else { switch(err) { case -EAGAIN: debugOutput(DEBUG_LEVEL_VERBOSE, "no events in ALSA-SEQ FIFO\n"); break; case -ENOSPC: debugWarning("ALSA-SEQ FIFO overrun, events dropped!\n"); break; default: debugError("Failed to receive ALSA-SEQ event (%d)\n", err); } } } printMessage(" <<< Exit wait loop... \n" ); // cleanup for ( BridgeMapIterator it = seqport2bridgemap.begin(); it != seqport2bridgemap.end(); ++it ) { HSS1394AlsaSeqMidiBridge *bridge = (*it).second; delete bridge; } seqport2bridgemap.clear(); snd_seq_close(m_seq_handle); delete m_deviceManager; printMessage("Bye... \n" ); return 0; } libffado-2.4.5/tests/test-shm.cpp0000644000175000001440000001217114206145246016300 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "debugmodule/debugmodule.h" #include "libutil/PosixSharedMemory.h" #include #include #include #include #include using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; #define MAX_ARGS 2 int run=1; int lastsig=-1; static void sighandler (int sig) { run = 0; } //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-shm 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-avccmd -- test program to test the shared memory class."; static char args_doc[] = "DIRECTION"; static struct argp_option options[] = { {"verbose", 'v', "level", 0, "Produce verbose output" }, { 0 } }; struct arguments { arguments() : nargs ( 0 ) , verbose( false ) { args[0] = 0; } char* args[MAX_ARGS]; int nargs; long int verbose; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': if (arg) { arguments->verbose = strtol( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case ARGP_KEY_ARG: if (state->arg_num >= MAX_ARGS) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; arguments->nargs++; break; case ARGP_KEY_END: if(arguments->nargs <= 0) { printMessage("not enough arguments\n"); return -1; } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); // arg parsing if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { fprintf( stderr, "Could not parse command line\n" ); exit(-1); } setDebugLevel(arguments.verbose); errno = 0; char* tail; long int direction = strtol( arguments.args[0], &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse direction argument\n" ); exit(-1); } printMessage("Testing shared memory direction %ld\n", direction); PosixSharedMemory s = PosixSharedMemory("testseg", 1024); s.setVerboseLevel(arguments.verbose); if(direction == 0) { if(!s.Create(PosixSharedMemory::eD_ReadWrite)) { debugError("Could not create segment\n"); exit(-1); } } else { if(!s.Open(PosixSharedMemory::eD_ReadOnly)) { debugError("Could not open segment\n"); exit(-1); } } if(!s.LockInMemory(true)) { debugError("Could not memlock segment\n"); } int offset=0; int len = 64; char buff[len]; int cnt = 0; run=1; long int time_to_sleep = 1000*1000; while(run) { if(direction == 0) { snprintf(buff, len, "test %d", cnt++); printMessage("writing '%s'...\n", buff); if(s.Write(offset, buff, len) != PosixSharedMemory::eR_OK) { debugError("Could not write to segment\n"); goto out_err; } usleep(time_to_sleep); } else { printMessage("reading...\n"); if(s.Read(offset, buff, len) != PosixSharedMemory::eR_OK) { debugError("Could not receive from queue\n"); goto out_err; } buff[len-1]=0; printMessage(" read: '%s'\n", buff); usleep(time_to_sleep * 110/100); } } if(!s.LockInMemory(false)) { debugError("Could not mem-unlock segment\n"); } return 0; out_err: if(!s.LockInMemory(false)) { debugError("Could not mem-unlock segment\n"); } return -1; } libffado-2.4.5/tests/test-streamdump.cpp0000644000175000001440000001506614206145246017700 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include using namespace std; //////////////////////////////////////////////// // arg parsing //////////////////////////////////////////////// const char *argp_program_version = "test-streamdump 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "test-streamdump -- test program to get a BeBoB device to stream and to save the streams"; static char args_doc[] = "NODE_ID"; static struct argp_option options[] = { {"verbose", 'v', 0, 0, "Produce verbose output" }, {"port", 'p', "PORT", 0, "Set port" }, { 0 } }; struct arguments { arguments() : verbose( false ) , test( false ) , port( 0 ) { args[0] = 0; } char* args[1]; bool verbose; bool test; int port; } arguments; // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': arguments->verbose = true; break; case 't': arguments->test = true; break; case 'p': arguments->port = strtol(arg, &tail, 0); if (errno) { perror("argument parsing failed:"); return errno; } break; case ARGP_KEY_ARG: if (state->arg_num >= 1) { // Too many arguments. argp_usage (state); } arguments->args[state->arg_num] = arg; break; case ARGP_KEY_END: if (state->arg_num < 1) { // Not enough arguments. argp_usage (state); } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { // arg parsing argp_parse (&argp, argc, argv, 0, 0, &arguments); errno = 0; char* tail; int iNodeId = strtol(arguments.args[0], &tail, 0); if (errno) { perror("argument parsing failed:"); return -1; } raw1394handle_t pHandle = raw1394_new_handle_on_port( arguments.port ); if ( !pHandle ) { if ( !errno ) { cerr << "libraw1394 not compatible" << endl; } else { perror( "Could not get 1394 handle" ); cerr << "Is ieee1394 and raw1394 driver loaded?" << endl; } return -1; } struct Connection { int m_output; int m_oplug; int m_input; int m_iplug; int m_iBandwith; int m_iIsoChannel; }; int iLocalId = raw1394_get_local_id( pHandle ); int iRemoteId = iNodeId | 0xffc0; Connection cons[] = { // output, oplug, input, iplug, bandwith, iso channel { iRemoteId, 0, iLocalId, -1, 0x148, -1 }, // oPCR[0] // { iRemoteId, 1, iLocalId, -1, 0x148, -1 }, // oPCR[1] // { iRemoteId, 2, iLocalId, -1, 0, -1 }, // oPCR[2]: cmp not supported // { iLocalId, -1, iRemoteId, 0, 0x148, -1 }, // iPCR[0] // { iLocalId, -1, iRemoteId, 1, 0x148, -1 }, // iPCR[1] // { iLocalId, -1, iRemoteId, 2, 0, -1 }, // iPCR[2]: cmp not supported }; printf( "local node id %d\n", iLocalId & ~0xffc0); printf( "remote node id %d\n", iRemoteId & ~0xffc0); for ( unsigned int i = 0; i < sizeof( cons ) / sizeof( cons[0] ); ++i ) { Connection* pCons = &cons[i]; // the bandwith calculation fails, so its better to use // some default values. pCons->m_iBandwith = iec61883_cmp_calc_bandwidth ( pHandle, pCons->m_output, pCons->m_oplug, IEC61883_DATARATE_400 ); sleep(1); pCons->m_iIsoChannel = iec61883_cmp_connect( pHandle, pCons->m_output, &pCons->m_oplug, pCons->m_input, &pCons->m_iplug, &pCons->m_iBandwith ); printf( "%2d -> %2d %cPCR[%2d]: bw = %4d, ch = %2d\n", pCons->m_output & ~0xffc0, pCons->m_input & ~0xffc0, pCons->m_oplug == -1? 'i' : 'o', pCons->m_oplug == -1? pCons->m_iplug: pCons->m_oplug, pCons->m_iBandwith, pCons->m_iIsoChannel ); sleep(1); } sleep( 5 ); for ( unsigned int i = 0; i < sizeof( cons ) / sizeof( cons[0] ); ++i ) { Connection* pCons = &cons[i]; if ( pCons->m_iIsoChannel != -1 ) { printf( "disconnect\n"); iec61883_cmp_disconnect( pHandle, pCons->m_output, pCons->m_oplug, pCons->m_input, pCons->m_iplug, pCons->m_iIsoChannel, pCons->m_iBandwith ); } } raw1394_destroy_handle( pHandle ); return 0; } libffado-2.4.5/tests/test-timestampedbuffer.cpp0000644000175000001440000004721514206145246021226 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "src/debugmodule/debugmodule.h" #include "libutil/ByteSwap.h" #include "src/libieee1394/cycletimer.h" #include "src/libutil/TimestampedBuffer.h" #include "libutil/Time.h" #include using namespace Util; class TimestampedBufferTestClient : public TimestampedBufferClient { public: bool processReadBlock(char *data, unsigned int nevents, unsigned int offset) {return true;}; bool processWriteBlock(char *data, unsigned int nevents, unsigned int offset) {return true;}; void setVerboseLevel(int l) {setDebugLevel(l);}; private: DECLARE_DEBUG_MODULE; }; IMPL_DEBUG_MODULE( TimestampedBufferTestClient, TimestampedBufferTestClient, DEBUG_LEVEL_VERBOSE ); DECLARE_GLOBAL_DEBUG_MODULE; int run; // Program documentation. static char doc[] = "FFADO -- Timestamped buffer test\n\n"; // A description of the arguments we accept. static char args_doc[] = ""; struct arguments { short verbose; uint64_t wrap_at; uint64_t frames_per_packet; uint64_t events_per_frame; float rate; uint64_t total_cycles; uint64_t buffersize; uint64_t start_at_cycle; }; // The options we understand. static struct argp_option options[] = { {"verbose", 'v', "n", 0, "Verbose level" }, {"wrap", 'w', "n", 0, "Wrap at (ticks) (3072000)" }, {"fpp", 'f', "n", 0, "Frames per packet (8)" }, {"epf", 'e', "n", 0, "Events per frame (10)" }, {"rate", 'r', "n", 0, "Rate (ticks/frame) (512.0)" }, {"cycles", 'c', "n", 0, "Total cycles to run (2000)" }, {"buffersize", 'b', "n", 0, "Buffer size (in frames) (1024)" }, {"startcycle", 's', "n", 0, "Start at cycle (0)" }, { 0 } }; //------------------------------------------------------------- // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': if (arg) { arguments->verbose = strtoll( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } else { if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'w': if (arg) { arguments->wrap_at = strtoll( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'wrap' argument\n" ); return ARGP_ERR_UNKNOWN; } } else { if ( errno ) { fprintf( stderr, "Could not parse 'wrap' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'f': if (arg) { arguments->frames_per_packet = strtoll( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'fpp' argument\n" ); return ARGP_ERR_UNKNOWN; } } else { if ( errno ) { fprintf( stderr, "Could not parse 'fpp' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'e': if (arg) { arguments->events_per_frame = strtoll( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'epf' argument\n" ); return ARGP_ERR_UNKNOWN; } } else { if ( errno ) { fprintf( stderr, "Could not parse 'epf' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'c': if (arg) { arguments->total_cycles = strtoll( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'cycles' argument\n" ); return ARGP_ERR_UNKNOWN; } } else { if ( errno ) { fprintf( stderr, "Could not parse 'cycles' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 's': if (arg) { arguments->start_at_cycle = strtoll( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'startcycle' argument\n" ); return ARGP_ERR_UNKNOWN; } } else { if ( errno ) { fprintf( stderr, "Could not parse 'startcycle' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'b': if (arg) { arguments->buffersize = strtoll( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'buffersize' argument\n" ); return ARGP_ERR_UNKNOWN; } } else { if ( errno ) { fprintf( stderr, "Could not parse 'buffersize' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; case 'r': if (arg) { arguments->rate = strtof( arg, &tail ); if ( errno ) { fprintf( stderr, "Could not parse 'rate' argument\n" ); return ARGP_ERR_UNKNOWN; } } else { if ( errno ) { fprintf( stderr, "Could not parse 'rate' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; default: return ARGP_ERR_UNKNOWN; } return 0; } // Our argp parser. static struct argp argp = { options, parse_opt, args_doc, doc }; static void sighandler (int sig) { run = 0; } int main(int argc, char *argv[]) { TimestampedBuffer *t=NULL; TimestampedBufferTestClient *c=NULL; struct arguments arguments; // Default values. arguments.verbose = 0; arguments.wrap_at = 3072000LLU; // 1000 cycles arguments.frames_per_packet = 8; arguments.events_per_frame = 10; arguments.rate = 512.0; arguments.total_cycles = 2000; arguments.buffersize = 1024; arguments.start_at_cycle = 0; // Parse our arguments; every option seen by `parse_opt' will // be reflected in `arguments'. if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { fprintf( stderr, "Could not parse command line\n" ); exit(1); } setDebugLevel(arguments.verbose); run=1; signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); c=new TimestampedBufferTestClient(); if(!c) { debugOutput(DEBUG_LEVEL_NORMAL, "Could not create TimestampedBufferTestClient\n"); exit(1); } c->setVerboseLevel(arguments.verbose); t=new TimestampedBuffer(c); if(!t) { debugOutput(DEBUG_LEVEL_NORMAL, "Could not create TimestampedBuffer\n"); delete c; exit(1); } t->setVerboseLevel(arguments.verbose); // Setup the buffer t->setBufferSize(arguments.buffersize); t->setEventSize(sizeof(int)); t->setEventsPerFrame(arguments.events_per_frame); t->setUpdatePeriod(arguments.frames_per_packet); t->setNominalRate(arguments.rate); t->setWrapValue(arguments.wrap_at); t->prepare(); SleepRelativeUsec(1000); debugOutput(DEBUG_LEVEL_NORMAL, "Start setBufferHeadTimestamp test...\n"); { bool pass=true; uint64_t time=arguments.start_at_cycle*3072; int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet]; // initialize the timestamp uint64_t timestamp=time; if (timestamp >= arguments.wrap_at) { // here we need a modulo because start_at_cycle can be large timestamp %= arguments.wrap_at; } // account for the fact that there is offset, // and that setBufferHeadTimestamp doesn't take offset // into account uint64_t timestamp2=timestamp; if (timestamp2>=arguments.wrap_at) { timestamp2-=arguments.wrap_at; } t->setBufferHeadTimestamp(timestamp2); timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet); if (timestamp >= arguments.wrap_at) { timestamp -= arguments.wrap_at; } // write some packets for (unsigned int i=0;i<20;i++) { t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp); timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet); if (timestamp >= arguments.wrap_at) { timestamp -= arguments.wrap_at; } } for(unsigned int cycle=arguments.start_at_cycle; cycle < arguments.start_at_cycle+arguments.total_cycles; cycle++) { ffado_timestamp_t ts_head_tmp; uint64_t ts_head; signed int fc_head; t->setBufferHeadTimestamp(timestamp); t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head); ts_head=(uint64_t)ts_head_tmp; if (timestamp != ts_head) { debugError(" cycle %4u error: %011" PRIu64 " != %011" PRIu64 "\n", cycle, timestamp, ts_head); pass=false; } timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet); if (timestamp >= arguments.wrap_at) { timestamp -= arguments.wrap_at; } // simulate the cycle timer clock in ticks time += 3072; if (time >= arguments.wrap_at) { time -= arguments.wrap_at; } // allow for the messagebuffer thread to catch up SleepRelativeUsec(200); if(!run) break; } if(!pass) { debugError("Test failed, exiting...\n"); delete t; delete c; return -1; } } debugOutput(DEBUG_LEVEL_NORMAL, "Start read/write test...\n"); { int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet]; int dummyframe_out[arguments.events_per_frame*arguments.frames_per_packet]; for (unsigned int i=0;i= arguments.wrap_at) { // here we need a modulo because start_at_cycle can be large timestamp %= arguments.wrap_at; } t->setBufferTailTimestamp(timestamp); timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet); if (timestamp >= arguments.wrap_at) { timestamp -= arguments.wrap_at; } for(unsigned int cycle=arguments.start_at_cycle; cycle < arguments.start_at_cycle+arguments.total_cycles; cycle++) { // simulate the rate adaptation int64_t diff=(time%arguments.wrap_at)-timestamp; if (diff>(int64_t)arguments.wrap_at/2) { diff -= arguments.wrap_at; } else if (diff<(-(int64_t)arguments.wrap_at)/2){ diff += arguments.wrap_at; } debugOutput(DEBUG_LEVEL_NORMAL, "Simulating cycle %d @ time=%011" PRIu64 ", diff=%" PRId64 "\n", cycle, time, diff); if(diff > 0) { ffado_timestamp_t ts_head_tmp, ts_tail_tmp; uint64_t ts_head, ts_tail; signed int fc_head, fc_tail; // write one packet t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp); // read the buffer head timestamp t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head); t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail); ts_head=(uint64_t)ts_head_tmp; ts_tail=(uint64_t)ts_tail_tmp; debugOutput(DEBUG_LEVEL_NORMAL, " TS after write: HEAD: %011" PRIu64 ", FC=%04u\n", ts_head,fc_head); debugOutput(DEBUG_LEVEL_NORMAL, " TAIL: %011" PRIu64 ", FC=%04u\n", ts_tail,fc_tail); // read one packet t->readFrames(arguments.frames_per_packet, (char *)&dummyframe_out); // read the buffer head timestamp t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head); t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail); ts_head=(uint64_t)ts_head_tmp; ts_tail=(uint64_t)ts_tail_tmp; debugOutput(DEBUG_LEVEL_NORMAL, " TS after write: HEAD: %011" PRIu64 ", FC=%04u\n", ts_head,fc_head); debugOutput(DEBUG_LEVEL_NORMAL, " TAIL: %011" PRIu64 ", FC=%04u\n", ts_tail,fc_tail); // check bool pass=true; for (unsigned int i=0;i= arguments.wrap_at) { timestamp -= arguments.wrap_at; } } // simulate the cycle timer clock in ticks time += 3072; if (time >= arguments.wrap_at) { time -= arguments.wrap_at; } // allow for the messagebuffer thread to catch up SleepRelativeUsec(200); if(!run) break; } } // second run, now do block processing debugOutput(DEBUG_LEVEL_NORMAL, "Start block read test...\n"); { unsigned int blocksize=32; int dummyframe_out_block[arguments.events_per_frame*arguments.frames_per_packet*blocksize]; int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet]; for (unsigned int i=0;i= arguments.wrap_at) { // here we need a modulo because start_at_cycle can be large timestamp %= arguments.wrap_at; } t->setBufferTailTimestamp(timestamp); timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet); if (timestamp >= arguments.wrap_at) { timestamp -= arguments.wrap_at; } for(unsigned int cycle=arguments.start_at_cycle; cycle < arguments.start_at_cycle+arguments.total_cycles; cycle++) { // simulate the rate adaptation int64_t diff=(time%arguments.wrap_at)-timestamp; if (diff>(int64_t)arguments.wrap_at/2) { diff -= arguments.wrap_at; } else if (diff<(-(int64_t)arguments.wrap_at)/2){ diff += arguments.wrap_at; } debugOutput(DEBUG_LEVEL_NORMAL, "Simulating cycle %d @ time=%011" PRIu64 ", diff=%" PRId64 "\n", cycle, time, diff); if(diff>0) { ffado_timestamp_t ts_head_tmp, ts_tail_tmp; uint64_t ts_head, ts_tail; signed int fc_head, fc_tail; // write one packet t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp); // read the buffer head timestamp t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head); t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail); ts_head=(uint64_t)ts_head_tmp; ts_tail=(uint64_t)ts_tail_tmp; debugOutput(DEBUG_LEVEL_NORMAL, " TS after write: HEAD: %011" PRIu64 ", FC=%04u\n", ts_head,fc_head); debugOutput(DEBUG_LEVEL_NORMAL, " TAIL: %011" PRIu64 ", FC=%04u\n", ts_tail,fc_tail); if (fc_head > (signed int)blocksize) { debugOutput(DEBUG_LEVEL_NORMAL,"Reading one block (%u frames)\n",blocksize); // read one block t->readFrames(blocksize, (char *)&dummyframe_out_block); // read the buffer head timestamp t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head); t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail); ts_head=(uint64_t)ts_head_tmp; ts_tail=(uint64_t)ts_tail_tmp; debugOutput(DEBUG_LEVEL_NORMAL, " TS after read: HEAD: %011" PRIu64 ", FC=%04u\n", ts_head,fc_head); debugOutput(DEBUG_LEVEL_NORMAL, " TAIL: %011" PRIu64 ", FC=%04u\n", ts_tail,fc_tail); } // update the timestamp timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet); if (timestamp >= arguments.wrap_at) { timestamp -= arguments.wrap_at; } } // simulate the cycle timer clock in ticks time += 3072; if (time >= arguments.wrap_at) { time -= arguments.wrap_at; } // allow for the messagebuffer thread to catch up SleepRelativeUsec(200); if(!run) break; } } delete t; delete c; return EXIT_SUCCESS; } libffado-2.4.5/tests/test-volume.cpp0000644000175000001440000001061414206145246017020 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "libavc/audiosubunit/avc_function_block.h" #include "libutil/serialize.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include const bool bVerbose = false; using namespace AVC; using namespace Util; using namespace Util::Cmd; short int getVolume( Ieee1394Service& ieee1394service, int node_id, int ffb_id, FunctionBlockCmd::EControlAttribute control_attrib ) { FunctionBlockCmd fbCmd( ieee1394service, FunctionBlockCmd::eFBT_Feature, ffb_id, control_attrib ); fbCmd.setNodeId( node_id ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Status ); fbCmd.m_pFBFeature->m_audioChannelNumber = 0; fbCmd.m_pFBFeature->m_controlSelector=FunctionBlockFeature::eCSE_Feature_Volume; fbCmd.m_pFBFeature->m_pVolume->m_volume = 0; fbCmd.setVerbose( bVerbose ); if (bVerbose) { ieee1394service.setVerboseLevel( DEBUG_LEVEL_VERBOSE ); } if ( !fbCmd.fire() ) { printf( "cmd failed\n" ); } if ( bVerbose ) { CoutSerializer se; fbCmd.serialize( se ); } return fbCmd.m_pFBFeature->m_pVolume->m_volume; } bool setVolume( Ieee1394Service& ieee1394service, int node_id, int ffb_id, int vol ) { FunctionBlockCmd fbCmd( ieee1394service, FunctionBlockCmd::eFBT_Feature, ffb_id, FunctionBlockCmd::eCA_Current ); fbCmd.setNodeId( node_id ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Control ); fbCmd.m_pFBFeature->m_audioChannelNumber = 0; fbCmd.m_pFBFeature->m_controlSelector=FunctionBlockFeature::eCSE_Feature_Volume; fbCmd.m_pFBFeature->m_pVolume->m_volume = vol; fbCmd.setVerbose( bVerbose ); if (bVerbose) { ieee1394service.setVerboseLevel( DEBUG_LEVEL_VERBOSE ); } bool bStatus = fbCmd.fire(); if ( !bStatus ) { printf( "cmd failed\n" ); } if ( bVerbose ) { CoutSerializer se; fbCmd.serialize( se ); } return bStatus; } bool doApp( Ieee1394Service& ieee1394service, int node_id, int fb_id, int vol ) { short int maxVolume = getVolume( ieee1394service, node_id, fb_id, FunctionBlockCmd::eCA_Maximum ); short int minVolume = getVolume( ieee1394service, node_id, fb_id, FunctionBlockCmd::eCA_Minimum ); short int curVolume = getVolume( ieee1394service, node_id, fb_id, FunctionBlockCmd::eCA_Current ); printf( "max volume value = %d\n", maxVolume ); printf( "min volume value = %d\n", minVolume ); printf( "old volume value = %d\n", curVolume); setVolume( ieee1394service, node_id, fb_id, vol ); curVolume = getVolume( ieee1394service, node_id, fb_id, FunctionBlockCmd::eCA_Current ); printf( "new volume value = %d\n", curVolume ); return true; } /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { if (argc < 3) { printf("usage: NODE_ID FB_ID VOL\n"); exit(0); } errno = 0; char* tail; int node_id = strtol( argv[1], &tail, 0 ); int fb_id = strtol( argv[2], &tail, 0 ); int vol = strtol( argv[3], &tail, 0 ); if (errno) { perror("argument parsing failed:"); return -1; } Ieee1394Service ieee1394service; if ( !ieee1394service.initialize( 0 ) ) { fprintf( stderr, "could not set port on ieee1394service\n" ); return -1; } doApp( ieee1394service, node_id, fb_id, vol ); return 0; } libffado-2.4.5/tests/test-watchdog.cpp0000644000175000001440000001305114206145246017307 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Pieter Palmers * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "src/debugmodule/debugmodule.h" #include "libutil/PosixThread.h" #include "libutil/Watchdog.h" #include "libutil/SystemTimeSource.h" #include using namespace Util; DECLARE_GLOBAL_DEBUG_MODULE; class HangTask : public Util::RunnableInterface { public: HangTask(unsigned int time_usecs, unsigned int nb_hangs) : m_time( time_usecs ) , m_nb_hangs(nb_hangs) {}; virtual ~HangTask() {}; bool Init() {return true;}; bool Execute() { debugOutput(DEBUG_LEVEL_VERBOSE, "execute\n"); ffado_microsecs_t start = Util::SystemTimeSource::getCurrentTimeAsUsecs(); ffado_microsecs_t stop_at = start + m_time; int cnt; int dummyvar = 0; while(Util::SystemTimeSource::getCurrentTimeAsUsecs() < stop_at) { cnt=1000; while(cnt--) { dummyvar++; } } // ensure that dummyvar doesn't get optimized away bool always_true = (dummyvar + Util::SystemTimeSource::getCurrentTimeAsUsecs() != 0); bool stop = (m_nb_hangs == 0); m_nb_hangs--; // we sleep for 100ms after a 'hang' Util::SystemTimeSource::SleepUsecRelative(1000*100); // we want the thread to exit after m_nb_hangs 'hangs' return always_true && !stop; }; unsigned int m_time; unsigned int m_nb_hangs; }; int run; // Program documentation. static char doc[] = "FFADO -- Watchdog test\n\n"; // A description of the arguments we accept. static char args_doc[] = ""; struct arguments { short verbose; }; // The options we understand. static struct argp_option options[] = { {"verbose", 'v', "n", 0, "Verbose level" }, { 0 } }; //------------------------------------------------------------- // Parse a single option. static error_t parse_opt( int key, char* arg, struct argp_state* state ) { // Get the input argument from `argp_parse', which we // know is a pointer to our arguments structure. struct arguments* arguments = ( struct arguments* ) state->input; char* tail; errno = 0; switch (key) { case 'v': if (arg) { arguments->verbose = strtoll( arg, &tail, 0 ); if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } else { if ( errno ) { fprintf( stderr, "Could not parse 'verbose' argument\n" ); return ARGP_ERR_UNKNOWN; } } break; default: return ARGP_ERR_UNKNOWN; } return 0; } // Our argp parser. static struct argp argp = { options, parse_opt, args_doc, doc }; static void sighandler (int sig) { run = 0; } int main(int argc, char *argv[]) { Watchdog *w=NULL; struct arguments arguments; // Default values. arguments.verbose = DEBUG_LEVEL_VERY_VERBOSE; // Parse our arguments; every option seen by `parse_opt' will // be reflected in `arguments'. if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) { fprintf( stderr, "Could not parse command line\n" ); exit(1); } setDebugLevel(arguments.verbose); run=1; signal (SIGINT, sighandler); signal (SIGPIPE, sighandler); w=new Watchdog(1000*1000*1, true, 99); // check time is one second w->setVerboseLevel(arguments.verbose); HangTask *task1=new HangTask(1000*10, 10); // ten millisecond, 10 hangs PosixThread *thread1 = new Util::PosixThread(task1, true, 10, PTHREAD_CANCEL_DEFERRED); thread1->setVerboseLevel(arguments.verbose); HangTask *task2=new HangTask(1000*1000*2, 10); // two seconds, 10 hangs PosixThread *thread2 = new Util::PosixThread(task2, true, 10, PTHREAD_CANCEL_DEFERRED); thread2->setVerboseLevel(arguments.verbose); w->registerThread(thread1); w->registerThread(thread2); // start the watchdog w->start(); Util::SystemTimeSource::SleepUsecRelative(1000*1000*1); // start the first thread, should be harmless since it's hang time is too low thread1->Start(); Util::SystemTimeSource::SleepUsecRelative(1000*1000*1); // start the second thread, should be rescheduled since it hangs too long thread2->Start(); // wait for a while Util::SystemTimeSource::SleepUsecRelative(1000*1000*5); thread1->Stop(); thread2->Stop(); w->unregisterThread(thread1); w->unregisterThread(thread2); delete thread1; delete thread2; delete task1; delete task2; delete w; return EXIT_SUCCESS; } libffado-2.4.5/tests/unmute-ozonic.cpp0000644000175000001440000000652614206145246017357 0ustar jwoitheusers/* * Copyright (C) 2005-2008 by Daniel Wagner * * This file is part of FFADO * FFADO = Free FireWire (pro-)audio drivers for Linux * * FFADO is based upon FreeBoB. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * "unmute-ozonic" by Mark Brand (orania), based on "test-enhanced-mixer" by Daniel Wagner * This is a knee-jerk reaction to the pathetic situation of requiring a dual-boot setup * for the sole and only purpose of unmuting the outputs of my M-Audio Ozonic. Seriously!? ;) * * This utility requires patches to avc_function_block.cpp and bebob_function_block.cpp * which might be problematic for other devices? I've included my patch to tests/SConscript. * * Also had a preliminary go at the mixer, which seems to work... but is incomplete. * All were originally based on revision 1957, though everything works for me under 1985. */ #include "libavc/audiosubunit/avc_function_block.h" #include "libutil/cmd_serialize.h" #include "libieee1394/ieee1394service.h" #include #include using namespace AVC; bool doApp( Ieee1394Service& ieee1394service, int node_id, int fb_id, int inputChannel, int outputChannel, int value ) { AVC::FunctionBlockCmd fbCmd( ieee1394service, FunctionBlockCmd::eFBT_Processing, fb_id, FunctionBlockCmd::eCA_Current); fbCmd.setNodeId( node_id ); fbCmd.setSubunitId( 0x00 ); fbCmd.setCommandType( AVCCommand::eCT_Control ); // Daniel says: "Ok, this enhanced mixer setting here is just a hack, we need // a sane way to set processing features (read pointer management)" delete fbCmd.m_pFBProcessing->m_pMixer; fbCmd.m_pFBProcessing->m_pMixer = 0; AVC::FunctionBlockProcessingEnhancedMixer em; fbCmd.m_pFBProcessing->m_pEnhancedMixer = em.clone(); fbCmd.m_pFBProcessing->m_inputAudioChannelNumber = inputChannel; fbCmd.m_pFBProcessing->m_outputAudioChannelNumber = outputChannel; fbCmd.m_pFBProcessing->m_pEnhancedMixer->m_statusSelector = FunctionBlockProcessingEnhancedMixer::eSS_Level; fbCmd.m_pFBProcessing->m_pEnhancedMixer->m_LevelData.clear(); fbCmd.m_pFBProcessing->m_pEnhancedMixer->m_LevelData.push_back((mixer_level_t) value); if ( !fbCmd.fire() ) { printf( "cmd failed\n" ); return false; } return true; } /////////////////////////// // main ////////////////////////// int main(int argc, char **argv) { Ieee1394Service ieee1394service; if ( !ieee1394service.initialize( 0 ) ) { fprintf( stderr, "could not set port on ieee1394service\n" ); return -1; } /* MB: various values work below, these chosen *almost* arbitrarily */ doApp( ieee1394service, 0, 1, 1, 1, 0 ); return 0; } libffado-2.4.5/tests/.gitignore0000644000175000001440000000055712132617070016020 0ustar jwoitheusersdumpiso_mod ffado-test scan-devreg set-default-router-config-dice-eap test-avccmd test-bufferops test-cycle-time test-devicestringparser test-dice-eap test-echomixer test-enhanced-mixer test-focusrite test-fw410 test-ieee1394service test-ipcringbuffer test-messagequeue test-scs test-shm test-streamdump test-timestampedbuffer test-volume test-watchdog unmute-ozonic libffado-2.4.5/tests/test-cycle-time.c0000644000175000001440000001202411735313352017200 0ustar jwoitheusers/* * Isochronous Cycle Timer register test * * Copyright 2010 Stefan Richter * You may freely use, modify, and/or redistribute this program. * * Version imported into FFADO: v20100125, from * http://user.in-berlin.de/~s5r6/linux1394/utils/ * * This is a tool to test the reliability of one particular hardware feature * of OHCI-1394 (FireWire) controllers: The isochronous cycle timer register. * Some controllers do not access this register atomically, resulting in the * cycle time seemingly jumping backwards occasionally. * * The firewire-ohci driver contains a workaround for unreliable isochronous * cycle timer hardware, but this workaround is only activated for known bad * hardware. You can use this tool to check whether you have an affected * controller and firewire-ohci misses the necessary quirks entry. * * Usage: * * - Compile with "gcc test_cycle_time_v20100125.c". * * - Run with "sudo ./a.out /dev/fw0". Use a different /dev/fw* file if you * have multiple controllers in your machine and want to test the second * or following controller. Be patient, the test runs for 60 seconds. * * - If the very last lines of the resulting output contains only * "0 cycleOffset backwards", "0 cycleCount backwards", "0 cycleSeconds * backwards", then either the hardware works correctly or the driver * already uses the workaround to compensate for a cycle timer hardware * bug. * But if the last lines of the output show one or more of the three cycle * timer components having gone backwards, then the hardware is buggy and * the driver does not yet contain the necessary quirks entry. * * In the latter case, please report your findings at * . This mailinglist is open for * posting without prior subscription. Please include the type and identifiers * of your FireWire controller(s) in your posting, as obtained by "lspci -nn". * * Remark: * * This program optionally accesses /usr/share/misc/oui.db to translate the * Globally Unique Identifier of the device that is associated with the chosen * /dev/fw* to the company name of the device manufacturer. The oui.db file * is not necessary for this program to work though. If you want you can * generate oui.db this way: * wget -O - http://standards.ieee.org/regauth/oui/oui.txt | * grep -E '(base 16).*\w+.*$' | * sed -e 's/\s*(base 16)\s*'/' /' > oui.db * sudo mv oui.db /usr/share/misc/ * which will download ~2 MB data and result in a ~0.4 MB large oui.db. */ #include #include #include #include #include #include #include #include #define TEST_DURATION 60 /* seconds */ static void cooked_ioctl(int fd, int req, void *arg, const char *name) { if (ioctl(fd, req, arg) < 0) { fprintf(stderr, "Failed %s ioctl: %m\n", name); exit(1); } } static int rolled_over(__u32 c0, __u32 c1) { return (c0 >> 25) == 127 && (c1 >> 25) == 0 && (c0 >> 12 & 0x1fff) > 8000 - 3 && (c1 >> 12 & 0x1fff) < 0000 + 3; } static void print_ct(__u32 c, __u64 l) { printf("%03d %04d %04d - %lld.%06lld\n", c >> 25, c >> 12 & 0x1fff, c & 0xfff, l / 1000000, l % 1000000); } int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "Usage: %s /dev/fw[0-n]*\n", argv[0]); return -1; } int fd = open(argv[1], O_RDWR); if (fd < 0) { fprintf(stderr, "Failed to open %s: %m\n", argv[0]); return -1; } __u32 rom[4]; struct fw_cdev_event_bus_reset reset; struct fw_cdev_get_info info = { .rom = (unsigned long)rom, .rom_length = sizeof(rom), .bus_reset = (unsigned long)&reset, }; cooked_ioctl(fd, FW_CDEV_IOC_GET_INFO, &info, "info"); if (reset.node_id != reset.local_node_id) { fprintf(stderr, "Not a local node\n"); return -1; } struct fw_cdev_get_cycle_timer ct; __u32 c0, c1; __u64 l0, l1, end_time; int i, j1, j2, j3; cooked_ioctl(fd, FW_CDEV_IOC_GET_CYCLE_TIMER, &ct, "cycle timer"); c1 = ct.cycle_timer; l1 = ct.local_time; end_time = l1 + TEST_DURATION * 1000000ULL; for (i = j1 = j2 = j3 = 0; l1 < end_time; i++) { cooked_ioctl(fd, FW_CDEV_IOC_GET_CYCLE_TIMER, &ct, "cycle timer"); c0 = c1; l0 = l1; c1 = ct.cycle_timer; l1 = ct.local_time; if (c1 <= c0 && !rolled_over(c0, c1)) { print_ct(c0, l0); print_ct(c1, l1); printf("\n"); j1++; if ((c1 & 0xfffff000) < (c0 & 0xfffff000)) j2++; if ((c1 & 0xfe000000) < (c0 & 0xfe000000)) j3++; } } printf("--------------------------------------------------------\n\n"); fflush(stdout); char buf[200]; sprintf(buf, "grep %06X /usr/share/misc/oui.db", rom[3] >> 8); if (system(buf) != 0) printf("%06X (unknown Vendor OUI)\n", rom[3] >> 8); printf("\n%d cycleOffset backwards out of %d samples (%.2e)\n", j1, i, (double)j1 / (double)i); printf("%d cycleCount backwards (%.2e)\n", j2, (double)j2 / (double)i); printf("%d cycleSeconds backwards (%.2e)\n", j3, (double)j3 / (double)i); return 0; } libffado-2.4.5/tests/dumpiso_mod.cpp0000644000175000001440000002254311276072751017063 0ustar jwoitheusers/* * libraw1394 - library for raw access to the 1394 bus with the Linux subsystem. * * Copyright (C) 1999,2000 Andreas Bombe * * This library is licensed under the GNU Lesser General Public License (LGPL), * version 2.1 or later. See the file COPYING.LIB in the distribution for * details. */ #include #include #include #include #include #include #include #include "src/libieee1394/cycletimer.h" #include "src/debugmodule/debugmodule.h" #include "src/libstreaming/util/cip.h" #include "libutil/ByteSwap.h" #define BUFFER 1000 #define PACKET_MAX 4096 // one 32 bit value as marker for start and stop #define PACKET_START_MARKER 0x01020304 #define PACKET_STOP_MARKER 0x04030201 u_int64_t listen_channels; unsigned long which_port; char *filename; int file; enum raw1394_iso_dma_recv_mode mode = RAW1394_DMA_DEFAULT; DECLARE_GLOBAL_DEBUG_MODULE; raw1394handle_t global_handle; void usage_exit(int exitcode) { fprintf(stderr, "Usage: dumpiso [opts] [FILE]\n" "Dump IEEE 1394 isochronous channels to FILE or standard output.\n" "\n" "-c --channels CHANNELS Listen on these channels; CHANNELS is either a\n" " number X or a range X-Y.\n" "-p --port PORT Choose 1394 chip PORT. (default: 0)\n" "-h --help Show this help.\n" ); exit(exitcode); } void parse_args(int argc, char **argv) { char *tail; unsigned long i, chan1, chan2; int c; int index; static struct option opts[] = { { "channels", required_argument, NULL, 'c' }, { "port", required_argument, NULL, 'p' }, { "help", no_argument, NULL, 'h' }, { 0 } }; while (1) { c = getopt_long(argc, argv, "hc:p:", opts, &index); if (c == -1) break; switch (c) { case 'c': chan1 = strtoul(optarg, &tail, 10); chan2 = chan1; if (*tail) { if (tail[0] != '-' || !tail[1]) { fprintf(stderr, "invalid argument to channels: %s\n", optarg); usage_exit(1); } tail++; chan2 = strtoul(tail, &tail, 10); if (*tail) { fprintf(stderr, "invalid argument to channels: %s\n", optarg); usage_exit(1); } } else { mode = RAW1394_DMA_PACKET_PER_BUFFER; } if (chan2 < chan1) { unsigned long x = chan1; chan1 = chan2; chan2 = x; } if (chan2 > 63) { fprintf(stderr, "invalid channel numbers: %s\n", optarg); exit(1); } for (i = chan1; i <= chan2; i++) listen_channels |= 1ULL << i; break; case 'p': which_port = strtoul(optarg, &tail, 10); if (*tail) { fprintf(stderr, "invalid argument to port: %s\n", optarg); usage_exit(1); } break; case 'h': usage_exit(0); case '?': usage_exit(1); default: abort(); } } argv += optind; argc -= optind; if (argc > 1) { fprintf(stderr, "Too many arguments.\n"); usage_exit(1); } if (argc) filename = *argv; if (!listen_channels) listen_channels = ~0ULL; } void write_header() { static char header[32] = "1394 isodump v3"; int i; for (i = 0; i < 8; i++) header[i+16] = (listen_channels >> (56 - 8*i)) & 0xff; i = 0; while (i < 32) { int ret; ret = write(file, header + i, 32 - i); if (ret < 0) { perror("header write"); exit(1); } i += ret; } } void open_dumpfile() { if (!filename || !filename[0] || (filename[0] == '-' && !filename[1])) { file = fileno(stdout); write_header(); return; } file = open(filename, O_CREAT | O_WRONLY, 0666); if (file < 0) { perror("dumpfile open"); exit(1); } ftruncate(file, 0); write_header(); } static enum raw1394_iso_disposition iso_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) { int ret; static unsigned int counter = 0; uint32_t cycle_timer; uint64_t local_time; struct iec61883_packet *packet = (struct iec61883_packet *) data; if (++counter % 1000 == 0) fprintf(stderr, "\r%uK packets", counter/1000); raw1394_read_cycle_timer(global_handle, &cycle_timer, &local_time); uint32_t timestamp = 0; bool ok = (packet->syt != 0xFFFF) && (packet->fdf != 0xFF) && (packet->fmt == 0x10) && (packet->dbs > 0) && (length >= 2*sizeof(quadlet_t)); if(ok) { timestamp = sytRecvToFullTicks((uint32_t)CondSwapFromBus16(packet->syt), cycle, cycle_timer); } /* write header */ unsigned short length2 = length; unsigned short cycle2 = cycle; unsigned int marker = PACKET_START_MARKER; write(file, &marker, 4); write(file, &length2, sizeof(length2)); write(file, &cycle2, sizeof(cycle2)); write(file, &channel, sizeof(channel)); write(file, &cycle_timer, sizeof(cycle_timer)); write(file, ×tamp, sizeof(timestamp)); write(file, &tag, sizeof(tag)); write(file, &sy, sizeof(sy)); sy = 0; write(file, &sy, sizeof(sy)); while (length) { ret = write(file, data, length); if (ret < 0) { perror("data write"); return RAW1394_ISO_ERROR; } length -= ret; data += ret; } marker = PACKET_STOP_MARKER; write(file, &marker, 4); return RAW1394_ISO_OK; } int main(int argc, char **argv) { raw1394handle_t handle; int i, ret; parse_args(argc, argv); fprintf(stderr, "port: %ld\nchannels: %#016llx\nfile: %s\n", which_port, (long long unsigned int)(listen_channels), filename); handle = raw1394_new_handle(); if (!handle) { if (!errno) fprintf(stderr, "No working kernel driver found.\n"); else perror("raw1394_get_handle"); exit(1); } do { if ((unsigned long)(raw1394_get_port_info(handle, NULL, 0)) <= which_port) { fprintf(stderr, "Port %ld does not exist.\n", which_port); exit(1); } ret = raw1394_set_port(handle, which_port); } while (ret < 0 && errno == ESTALE); if (ret < 0) { perror("raw1394_set_port"); exit(1); } global_handle = raw1394_new_handle_on_port(which_port); if (!global_handle) { perror("raw1394_new_handle_on_port"); exit(1); } open_dumpfile(); if (mode == RAW1394_DMA_DEFAULT) { raw1394_iso_multichannel_recv_init(handle, iso_handler, BUFFER, 2048, -1); /* >2048 makes rawiso stall! */ raw1394_iso_recv_set_channel_mask(handle, listen_channels); } else for (i = 0; i < 64; i++) { if (!(listen_channels & 1ULL << i)) continue; raw1394_iso_recv_init(handle, iso_handler, BUFFER, PACKET_MAX, i, mode, -1); } raw1394_iso_recv_start(handle, -1, -1, 0); while (raw1394_loop_iterate(handle) == 0); fprintf(stderr, "\n"); raw1394_iso_shutdown(handle); raw1394_destroy_handle(handle); return 0; } libffado-2.4.5/version.h.in0000644000175000001440000000146413246707102015132 0ustar jwoitheusers/* version.h.in. */ #ifndef VERSION_H #define VERSION_H /* Name of package */ #define PACKAGE $PACKAGE /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "" /* Define to the full name of this package. */ #define PACKAGE_NAME "$PACKAGE" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "$PACKAGE $VERSION$REVISIONSTRING" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "$PACKAGE" /* Define to the version of this package. */ #define PACKAGE_VERSION "$VERSION$REVISIONSTRING" /* Define to the version of the cace. */ #define CACHE_VERSION "$VERSION$REVISIONSTRING" /* Define to the api version */ #define FFADO_API_VERSION $FFADO_API_VERSION #endif /* VERSION_H */ libffado-2.4.5/AUTHORS0000644000175000001440000000056012303607741013734 0ustar jwoitheusersDaniel Wagner Pieter Palmers Jonathan Woithe Arnold Krille Adrian Knoth Philippe Carriere Takashi Sakamoto Stefan Richter Jano Svitok libffado-2.4.5/.gitignore0000644000175000001440000000013212132617070014643 0ustar jwoitheuserscache/ .sconsign.dblite config.h config_debug.h libffado.pc version.h *.pyc *.o *.os *.so libffado-2.4.5/config.h.in0000644000175000001440000002751112064043446014714 0ustar jwoitheusers/* config.h.in. */ #ifndef CONFIG_H #define CONFIG_H #define BINDIR "$BINDIR" #define LIBDIR "$LIBDIR" #define SHAREDIR "$SHAREDIR" /* configuration file locations */ #define USER_CONFIG_FILE "$USER_CONFIG_FILE" #define SYSTEM_CONFIG_FILE "$SYSTEM_CONFIG_FILE" /* Define indicating availability of lrint() */ #define HAVE_LRINT $HAVE_LRINT /* Define indicatin availability of lrintf() */ #define HAVE_LRINTF $HAVE_LRINTF // serialization #define SERIALIZE_USE_EXPAT $SERIALIZE_USE_EXPAT #define CACHEDIR "$CACHEDIR" // discovery #define ENABLE_DISCOVERY_CACHE 1 // watchdog #define WATCHDOG_DEFAULT_CHECK_INTERVAL_USECS (1000*1000*4) #define WATCHDOG_DEFAULT_RUN_REALTIME 1 #define WATCHDOG_DEFAULT_PRIORITY 98 // threading #define THREAD_MAX_RTPRIO 98 #define THREAD_MIN_RTPRIO 1 // time // we should be using absolute clock_nanosleep // but on my system it causes a problem on shutdown. #define USE_ABSOLUTE_NANOSLEEP 1 // 1394 service constants #define IEEE1394SERVICE_USE_CYCLETIMER_DLL 1 #define IEEE1394SERVICE_CYCLETIMER_DLL_UPDATE_INTERVAL_USEC 200000 #define IEEE1394SERVICE_CYCLETIMER_DLL_BANDWIDTH_HZ 0.5 #define IEEE1394SERVICE_MAX_FIREWIRE_PORTS 4 #define IEEE1394SERVICE_MIN_SPLIT_TIMEOUT_USECS 1000000 #define IEEE1394SERVICE_CYCLETIMER_HELPER_RUN_REALTIME 1 #define IEEE1394SERVICE_CYCLETIMER_HELPER_PRIO 1 // config rom read wait interval #define IEEE1394SERVICE_CONFIGROM_READ_WAIT_USECS 1000 // FCP defines #define IEEE1394SERVICE_FCP_MAX_TRIES 2 #define IEEE1394SERVICE_FCP_SLEEP_BETWEEN_FAILURES_USECS 1000 #define IEEE1394SERVICE_FCP_POLL_TIMEOUT_MSEC 200 #define IEEE1394SERVICE_FCP_RESPONSE_TIMEOUT_USEC 200000 // The current version of libiec61883 doesn't seem to calculate // the bandwidth correctly. Defining this to non-zero skips // bandwidth allocation when doing CMP connections. #define IEEE1394SERVICE_SKIP_IEC61883_BANDWIDTH_ALLOCATION 1 #define MINIMUM_INTERRUPTS_PER_PERIOD 2U // These are the result of a lot of trial and error // due to weirdness in the kernel layer #define MAX_XMIT_PACKET_SIZE (2048-16) #define MIN_XMIT_PACKET_SIZE 72 #define MAX_XMIT_NB_BUFFERS 128 #define MAX_RECV_NB_BUFFERS 128 #define MIN_RECV_PACKET_SIZE 72 // the default ISO receive mode. // 0 = auto, 1 = packet-per-buffer, 2 = bufferfill. // 'auto' will automatically choose the mode that is expected // to perform best for the given situation. For large periods // this is 'bufferfill' mode, for small periods this is // 'packet-per-buffer' mode. The 'BUFFERFILL_MODE_THRESHOLD' // defines what a 'large period' is. #define DEFAULT_ISO_RECEIVE_MODE 0 // the number of packets required to fill one period from which // the bufferfill mode is to be used #define BUFFERFILL_MODE_THRESHOLD 64 #define ISOHANDLER_FLUSH_BEFORE_ITERATE 0 #define ISOHANDLER_DEATH_DETECT_TIMEOUT_USECS 1000000LL #define ISOHANDLER_CHECK_CTR_RECONSTRUCTION 1 #define ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT 16 #define ISOHANDLERMANAGER_MAX_STREAMS_PER_ISOTHREAD 16 // The best setup is if the receive handlers have lower priority // than the client thread since that ensures that as soon as we // received sufficient frames, the client thread runs. // The transmit thread should have higher priority to ensure that // all available data is flushed to the ISO kernel buffers as // soon as possible // At this moment, the jack backend uses base+5 to init ffado // prio #define ISOHANDLERMANAGER_ISO_PRIO_INCREASE 0 #define ISOHANDLERMANAGER_ISO_PRIO_INCREASE_RECV -1 #define ISOHANDLERMANAGER_ISO_PRIO_INCREASE_XMIT 1 // the timeout for ISO activity on any thread // NOTE: don't make this 0 #define ISOHANDLERMANAGER_ISO_TASK_WAIT_TIMEOUT_USECS 1000000LL // allows to add some processing margin. This shifts the time // at which the buffer is transfer()'ed, making 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. This can be counteracted by increasing the // STREAMPROCESSORMANAGER_XMIT_PREBUFFER_FRAMES option #define STREAMPROCESSORMANAGER_SIGNAL_DELAY_TICKS (3072*0) // the number of frames that we prebuffer in the 1394 ISO layer // this adds directly to the roundtrip latency #define STREAMPROCESSORMANAGER_XMIT_PREBUFFER_FRAMES 100 // causes the waitForPeriod() call to wait until sufficient // data is present in the buffer such that a transfer() will // succeed. Normally we wait for the period of time that theoretically // would mean that his is true. However sometimes the kernel hasn't // flushed everything to userspace (or the card hasn't IRQ'd). // the side-effect of this is some jitter in the return timing // whenever this occurs. #define STREAMPROCESSORMANAGER_ALLOW_DELAYED_PERIOD_SIGNAL 1 // startup control #define STREAMPROCESSORMANAGER_CYCLES_FOR_DRYRUN 40000 #define STREAMPROCESSORMANAGER_CYCLES_FOR_STARTUP 200 #define STREAMPROCESSORMANAGER_PRESTART_CYCLES_FOR_XMIT 20 #define STREAMPROCESSORMANAGER_PRESTART_CYCLES_FOR_RECV 0 #define STREAMPROCESSORMANAGER_SYNCSTART_TRIES 10 #define STREAMPROCESSORMANAGER_SYNC_WAIT_TIME_MSEC 200 #define STREAMPROCESSORMANAGER_NB_ALIGN_TRIES 40 #define STREAMPROCESSORMANAGER_ALIGN_AVERAGE_TIME_MSEC 400 #define STREAMPROCESSORMANAGER_DYNAMIC_SYNC_DELAY 0 // the default bandwidth of the stream processor timestamp DLL when synchronizing (should be fast) #define STREAMPROCESSOR_DLL_FAST_BW_HZ 5.0 // the default bandwidth of the stream processor timestamp DLL when streaming #define STREAMPROCESSOR_DLL_BW_HZ 0.1 // -- AMDTP options -- // // in ticks // as per AMDTP2.1: 354.17us + 125us, but FFADO measures from the // packet transmission time, so subtract 125 us: // 354.17us @ 24.576ticks/usec = 8704 ticks #define AMDTP_TRANSMIT_TRANSFER_DELAY (8704U) // 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 AMDTP_TRANSMIT_TRANSFER_DELAY (in ticks), but in case we // are too late for that, this constant defines how late we can // be. #define AMDTP_MIN_CYCLES_BEFORE_PRESENTATION 1 // 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 AMDTP_TRANSMIT_TRANSFER_DELAY (in ticks), but we can send // packets early if we want to. (not completely according to spec) // (for spec compliance you need to specify a value of 0) #define AMDTP_MAX_CYCLES_TO_TRANSMIT_EARLY 1 // ensure that the AMDTP SP clips all float values to [-1.0..1.0] #define AMDTP_CLIP_FLOATS 1 // Allow that devices request that the AMDTP transmit SP adds // payload to the NO-DATA packets. #define AMDTP_ALLOW_PAYLOAD_IN_NODATA_XMIT 1 // Default setting for the payload setting if // AMDTP_ALLOW_PAYLOAD_IN_NODATA_XMIT is enabled. Devices can // explicity override this #define AMDTP_SEND_PAYLOAD_IN_NODATA_XMIT_BY_DEFAULT true // -- MOTU options -- // // the transfer delay is substracted from the ideal presentation // time to obtain a corrected presentation time. This values is // then used to stamp the packet and determine the transmission // time instant. #define MOTU_TRANSMIT_TRANSFER_DELAY (0U) // 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. #define MOTU_MIN_CYCLES_BEFORE_PRESENTATION 0 // 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. #define MOTU_MAX_CYCLES_TO_TRANSMIT_EARLY 2 // ensure that the MOTU tx SP clips all float values to [-1.0..1.0] #define MOTU_CLIP_FLOATS 1 // -- RME options -- // // the transfer delay is substracted from the ideal presentation // time to obtain a corrected presentation time. This value is // then used to stamp the packet and determine the transmission // time instant. This is in ticks, and has been determined // experimentally to provide the most stability. #define RME_TRANSMIT_TRANSFER_DELAY (3U*TICKS_PER_CYCLE) // 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 RME_TRANSMIT_TRANSFER_DELAY (in ticks), but in case we // are too late for that, this constant defines how late we can // be. #define RME_MIN_CYCLES_BEFORE_PRESENTATION 1 // 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 RME_TRANSMIT_TRANSFER_DELAY (in ticks), but we can send // packets early if we want to. #define RME_MAX_CYCLES_TO_TRANSMIT_EARLY 5 // The transfer delay assumed for incoming packets from an RME. // Normally this would be the same as the transmit transfer delay. // This value is in ticks. #define RME_RECEIVE_TRANSFER_DELAY RME_TRANSMIT_TRANSFER_DELAY // ensure that the RME tx SP clips all float values to [-1.0..1.0] #define RME_CLIP_FLOATS 1 // -- Digidesign options -- // // // Currently these are just verbatim copies of the values which // seem to work for MOTU devices. Some tweaks may well be // necessary once testing begins with real devices. // the transfer delay is substracted from the ideal presentation // time to obtain a corrected presentation time. This values is // then used to stamp the packet and determine the transmission // time instant. #define DIGIDESIGN_TRANSMIT_TRANSFER_DELAY (0U) // 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 DIGIDESIGN_TRANSMIT_TRANSFER_DELAY (in ticks), but // in case we are too late for that, this constant defines how // late we can be. #define DIGIDESIGN_MIN_CYCLES_BEFORE_PRESENTATION 0 // 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 DIGIDESIGN_TRANSMIT_TRANSFER_DELAY (in ticks), but // we can send packets early if we want to. #define DIGIDESIGN_MAX_CYCLES_TO_TRANSMIT_EARLY 2 // ensure that the DIGIDESIGN tx SP clips all float values to [-1.0..1.0] #define DIGIDESIGN_CLIP_FLOATS 1 /// The unavoidable device specific hacks // Use the information in the music plug instead of that in the // cluster info block for the stream configuration. Should not // be necessary #define AVC_STREAMCONFIG_USE_MUSICPLUG 0 #endif // CONFIG_H libffado-2.4.5/config_debug.h.in0000644000175000001440000000326611201564721016057 0ustar jwoitheusers/* config_debug.h.in. */ #ifndef CONFIG_DEBUG_H #define CONFIG_DEBUG_H // use a RT-safe message buffer for debug output // useful to disable this when the code aborts/segfaults to // not lose debug output. should be enabled though. #define DEBUG_USE_MESSAGE_BUFFER 1 // max message length in the debug messagebuffer #define DEBUG_MAX_MESSAGE_LENGTH 2048 // number of messages in the debug messagebuffer (power of two) #define DEBUG_MB_BUFFERS 1024 // use an RT thread for reading out the messagebuffer. // can reduce the number of buffer xruns, and // avoids priority inversion issues #define DEBUG_MESSAGE_BUFFER_REALTIME 1 #define DEBUG_MESSAGE_BUFFER_REALTIME_PRIO 1 // When a write can't get the buffer lock, how many times // should it retry, and wait how long between retries? #define DEBUG_MESSAGE_BUFFER_COLLISION_WAIT_NTRIES 2 #define DEBUG_MESSAGE_BUFFER_COLLISION_WAIT_NSEC 50000 // support a debug backlog // note that this does not influence non-debug builds #define DEBUG_BACKLOG_SUPPORT 0 // number of messages in the backlog buffer (power of two) #define DEBUG_BACKLOG_MB_BUFFERS 64 // support backtrace debugging // note that this does not influence non-debug builds #define DEBUG_BACKTRACE_SUPPORT 0 // max length of backtrace #define DEBUG_MAX_BACKTRACE_LENGTH 8 // max amount of function pointers to keep track of #define DEBUG_MAX_BACKTRACE_FUNCTIONS_SEEN 64 // lock debugging #define DEBUG_LOCK_COLLISION_TRACING 0 // make this zero to disable the most extreme // debug logging in the critical sections #define DEBUG_EXTREME_ENABLE 0 #endif // CONFIG_DEBUG_H libffado-2.4.5/libffado.pc.in0000644000175000001440000000014210744643363015366 0ustar jwoitheusersName: libffado Description: FFADO Version: $VERSION Libs: -L$LIBDIR -lffado Cflags: -I$INCLUDEDIR libffado-2.4.5/LICENSE.GPLv20000644000175000001440000004310310744621175014566 0ustar jwoitheusers GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. 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. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the 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 a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 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. 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 convey 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision 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, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This 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. libffado-2.4.5/LICENSE.GPLv30000644000175000001440000010451310744621175014572 0ustar jwoitheusers 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 . libffado-2.4.5/ChangeLog0000644000175000001440000000011110723527646014437 0ustar jwoitheusersREMARK: Please use the SVN commit messages as replacement for ChangeLog libffado-2.4.5/TODO0000644000175000001440000000000010723527646013352 0ustar jwoitheuserslibffado-2.4.5/NEWS0000644000175000001440000000000010723527646013361 0ustar jwoitheusers