mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	avbtp: add beginnings of AVB manager module
This commit is contained in:
		
							parent
							
								
									af4875eb47
								
							
						
					
					
						commit
						f470354e67
					
				
					 14 changed files with 1657 additions and 0 deletions
				
			
		| 
						 | 
					@ -71,6 +71,7 @@ conf_files = [
 | 
				
			||||||
 'jack.conf',
 | 
					 'jack.conf',
 | 
				
			||||||
 'minimal.conf',
 | 
					 'minimal.conf',
 | 
				
			||||||
 'pipewire-pulse.conf',
 | 
					 'pipewire-pulse.conf',
 | 
				
			||||||
 | 
					 'pipewire-avb.conf',
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
foreach c : conf_files
 | 
					foreach c : conf_files
 | 
				
			||||||
| 
						 | 
					@ -100,6 +101,14 @@ executable('pipewire-pulse',
 | 
				
			||||||
  dependencies : [ spa_dep, pipewire_dep, ],
 | 
					  dependencies : [ spa_dep, pipewire_dep, ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					executable('pipewire-avb',
 | 
				
			||||||
 | 
					  pipewire_daemon_sources,
 | 
				
			||||||
 | 
					  install: true,
 | 
				
			||||||
 | 
					  c_args : pipewire_c_args,
 | 
				
			||||||
 | 
					  include_directories : [ configinc ],
 | 
				
			||||||
 | 
					  dependencies : [ spa_dep, pipewire_dep, ],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ln = find_program('ln')
 | 
					ln = find_program('ln')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
custom_target('pipewire-uninstalled',
 | 
					custom_target('pipewire-uninstalled',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										84
									
								
								src/daemon/pipewire-avb.conf.in
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/daemon/pipewire-avb.conf.in
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,84 @@
 | 
				
			||||||
 | 
					# PulseAudio config file for PipeWire version @VERSION@ #
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copy and edit this file in @PIPEWIRE_CONFIG_DIR@ for system-wide changes
 | 
				
			||||||
 | 
					# or in ~/.config/pipewire for local changes.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# It is also possible to place a file with an updated section in
 | 
				
			||||||
 | 
					# @PIPEWIRE_CONFIG_DIR@/pipewire-pulse.conf.d/ for system-wide changes or in
 | 
				
			||||||
 | 
					# ~/.config/pipewire/pipewire-pulse.conf.d/ for local changes.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					context.properties = {
 | 
				
			||||||
 | 
					    ## Configure properties in the system.
 | 
				
			||||||
 | 
					    #mem.warn-mlock  = false
 | 
				
			||||||
 | 
					    #mem.allow-mlock = true
 | 
				
			||||||
 | 
					    #mem.mlock-all   = false
 | 
				
			||||||
 | 
					    #log.level       = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #default.clock.quantum-limit = 8192
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					context.spa-libs = {
 | 
				
			||||||
 | 
					    audio.convert.* = audioconvert/libspa-audioconvert
 | 
				
			||||||
 | 
					    support.*       = support/libspa-support
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					context.modules = [
 | 
				
			||||||
 | 
					    { name = libpipewire-module-rt
 | 
				
			||||||
 | 
					        args = {
 | 
				
			||||||
 | 
					            nice.level   = -11
 | 
				
			||||||
 | 
					            #rt.prio      = 88
 | 
				
			||||||
 | 
					            #rt.time.soft = -1
 | 
				
			||||||
 | 
					            #rt.time.hard = -1
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        flags = [ ifexists nofail ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    { name = libpipewire-module-client-device }
 | 
				
			||||||
 | 
					    { name = libpipewire-module-adapter }
 | 
				
			||||||
 | 
					    { name = libpipewire-module-avbtp
 | 
				
			||||||
 | 
					        args = {
 | 
				
			||||||
 | 
						    # contents of avb.properties can also be placed here
 | 
				
			||||||
 | 
						    # to have config per server.
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Extra modules can be loaded here. Setup in default.pa can be moved here
 | 
				
			||||||
 | 
					context.exec = [
 | 
				
			||||||
 | 
					    #{ path = "pactl"        args = "load-module module-always-sink" }
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					stream.properties = {
 | 
				
			||||||
 | 
					    #node.latency          = 1024/48000
 | 
				
			||||||
 | 
					    #node.autoconnect      = true
 | 
				
			||||||
 | 
					    #resample.quality      = 4
 | 
				
			||||||
 | 
					    #channelmix.normalize  = false
 | 
				
			||||||
 | 
					    #channelmix.mix-lfe    = false
 | 
				
			||||||
 | 
					    #channelmix.upmix      = true
 | 
				
			||||||
 | 
					    #channelmix.lfe-cutoff = 120
 | 
				
			||||||
 | 
					    #channelmix.fc-cutoff  = 6000
 | 
				
			||||||
 | 
					    #channelmix.rear-delay = 12.0
 | 
				
			||||||
 | 
					    #channelmix.stereo-widen = 0.1
 | 
				
			||||||
 | 
					    #channelmix.hilbert-taps = 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					avb.properties = {
 | 
				
			||||||
 | 
					    # the addresses this server listens on
 | 
				
			||||||
 | 
					    ifname = "enp3s0"
 | 
				
			||||||
 | 
					    server.address = [
 | 
				
			||||||
 | 
					        "unix:native"
 | 
				
			||||||
 | 
					        #"unix:/tmp/something"              # absolute paths may be used
 | 
				
			||||||
 | 
					        #"tcp:4713"                         # IPv4 and IPv6 on all addresses
 | 
				
			||||||
 | 
					        #"tcp:[::]:9999"                    # IPv6 on all addresses
 | 
				
			||||||
 | 
					        #"tcp:127.0.0.1:8888"               # IPv4 on a single address
 | 
				
			||||||
 | 
					        #
 | 
				
			||||||
 | 
					        #{ address = "tcp:4713"             # address
 | 
				
			||||||
 | 
					        #  max-clients = 64                 # maximum number of clients
 | 
				
			||||||
 | 
					        #  listen-backlog = 32              # backlog in the server listen queue
 | 
				
			||||||
 | 
					        #  client.access = "restricted"     # permissions for clients
 | 
				
			||||||
 | 
					        #}
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    # These overrides are only applied when running in a vm.
 | 
				
			||||||
 | 
					    vm.overrides = {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ subdir('spa')
 | 
				
			||||||
module_sources = [
 | 
					module_sources = [
 | 
				
			||||||
  'module-access.c',
 | 
					  'module-access.c',
 | 
				
			||||||
  'module-adapter.c',
 | 
					  'module-adapter.c',
 | 
				
			||||||
 | 
					  'module-avbtp.c',
 | 
				
			||||||
  'module-client-device.c',
 | 
					  'module-client-device.c',
 | 
				
			||||||
  'module-client-node.c',
 | 
					  'module-client-node.c',
 | 
				
			||||||
  'module-echo-cancel.c',
 | 
					  'module-echo-cancel.c',
 | 
				
			||||||
| 
						 | 
					@ -516,3 +517,20 @@ pipewire_module_fallback_sink = shared_library('pipewire-module-fallback-sink',
 | 
				
			||||||
  install_rpath: modules_install_dir,
 | 
					  install_rpath: modules_install_dir,
 | 
				
			||||||
  dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep],
 | 
					  dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					build_module_avbtp = get_option('avb').allowed()
 | 
				
			||||||
 | 
					if build_module_avbtp
 | 
				
			||||||
 | 
					pipewire_module_avbtp = shared_library('pipewire-module-avbtp',
 | 
				
			||||||
 | 
					  [ 'module-avbtp.c',
 | 
				
			||||||
 | 
					    'module-avbtp/avb.c',
 | 
				
			||||||
 | 
					    'module-avbtp/adp.c',
 | 
				
			||||||
 | 
					    'module-avbtp/avdecc.c',
 | 
				
			||||||
 | 
					    'module-avbtp/maap.c' ],
 | 
				
			||||||
 | 
					  include_directories : [configinc],
 | 
				
			||||||
 | 
					  install : true,
 | 
				
			||||||
 | 
					  install_dir : modules_install_dir,
 | 
				
			||||||
 | 
					  install_rpath: modules_install_dir,
 | 
				
			||||||
 | 
					  dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					summary({'avbtp': build_module_avbtp}, bool_yn: true, section: 'Optional Modules')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										134
									
								
								src/modules/module-avbtp.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/modules/module-avbtp.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,134 @@
 | 
				
			||||||
 | 
					/* PipeWire
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Wim Taymans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#include <sys/stat.h>
 | 
				
			||||||
 | 
					#include <fcntl.h>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <spa/utils/result.h>
 | 
				
			||||||
 | 
					#include <spa/utils/string.h>
 | 
				
			||||||
 | 
					#include <spa/utils/json.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pipewire/impl.h>
 | 
				
			||||||
 | 
					#include <pipewire/private.h>
 | 
				
			||||||
 | 
					#include <pipewire/i18n.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "module-avbtp/avb.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** \page page_module_avb PipeWire Module: AVB
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NAME "avb"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
 | 
				
			||||||
 | 
					#define PW_LOG_TOPIC_DEFAULT mod_topic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MODULE_USAGE	" "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct spa_dict_item module_props[] = {
 | 
				
			||||||
 | 
						{ PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
 | 
				
			||||||
 | 
						{ PW_KEY_MODULE_DESCRIPTION, "Manage an AVB network" },
 | 
				
			||||||
 | 
						{ PW_KEY_MODULE_USAGE, MODULE_USAGE },
 | 
				
			||||||
 | 
						{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct impl {
 | 
				
			||||||
 | 
						struct pw_context *context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct pw_impl_module *module;
 | 
				
			||||||
 | 
						struct spa_hook module_listener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct pw_properties *properties;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct pw_avb *avb;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void impl_free(struct impl *impl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pw_properties_free(impl->properties);
 | 
				
			||||||
 | 
						free(impl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void module_destroy(void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl = data;
 | 
				
			||||||
 | 
						spa_hook_remove(&impl->module_listener);
 | 
				
			||||||
 | 
						impl_free(impl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct pw_impl_module_events module_events = {
 | 
				
			||||||
 | 
						PW_VERSION_IMPL_MODULE_EVENTS,
 | 
				
			||||||
 | 
						.destroy = module_destroy,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SPA_EXPORT
 | 
				
			||||||
 | 
					int pipewire__module_init(struct pw_impl_module *module, const char *args)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pw_context *context = pw_impl_module_get_context(module);
 | 
				
			||||||
 | 
						struct pw_properties *props;
 | 
				
			||||||
 | 
						struct impl *impl;
 | 
				
			||||||
 | 
						int res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PW_LOG_TOPIC_INIT(mod_topic);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl = calloc(1, sizeof(struct impl));
 | 
				
			||||||
 | 
						if (impl == NULL)
 | 
				
			||||||
 | 
							goto error_errno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_log_debug("module %p: new %s", impl, args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (args == NULL)
 | 
				
			||||||
 | 
							args = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						props = pw_properties_new_string(args);
 | 
				
			||||||
 | 
						if (props == NULL)
 | 
				
			||||||
 | 
							goto error_errno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl->module = module;
 | 
				
			||||||
 | 
						impl->context = context;
 | 
				
			||||||
 | 
						impl->properties = props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl->avb = pw_avb_new(context, props, 0);
 | 
				
			||||||
 | 
						if (impl->avb == NULL)
 | 
				
			||||||
 | 
							goto error_errno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					error_errno:
 | 
				
			||||||
 | 
						res = -errno;
 | 
				
			||||||
 | 
						if (impl)
 | 
				
			||||||
 | 
							impl_free(impl);
 | 
				
			||||||
 | 
						return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										139
									
								
								src/modules/module-avbtp/aaf.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								src/modules/module-avbtp/aaf.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,139 @@
 | 
				
			||||||
 | 
					/* AVBTP support
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Wim Taymans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef AVBTP_AAF_H
 | 
				
			||||||
 | 
					#define AVBTP_AAF_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct avbtp_packet_aaf {
 | 
				
			||||||
 | 
						uint8_t subtype;
 | 
				
			||||||
 | 
					#if __BYTE_ORDER == __BIG_ENDIAN
 | 
				
			||||||
 | 
						unsigned sv:1;
 | 
				
			||||||
 | 
						unsigned version:3;
 | 
				
			||||||
 | 
						unsigned mr:1;
 | 
				
			||||||
 | 
						unsigned _r1:1;
 | 
				
			||||||
 | 
						unsigned gv:1;
 | 
				
			||||||
 | 
						unsigned tv:1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint8_t seq_number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned _r2:7;
 | 
				
			||||||
 | 
						unsigned tu:1;
 | 
				
			||||||
 | 
					#elif __BYTE_ORDER == __LITTLE_ENDIAN
 | 
				
			||||||
 | 
						unsigned tv:1;
 | 
				
			||||||
 | 
						unsigned gv:1;
 | 
				
			||||||
 | 
						unsigned _r1:1;
 | 
				
			||||||
 | 
						unsigned mr:1;
 | 
				
			||||||
 | 
						unsigned version:3;
 | 
				
			||||||
 | 
						unsigned sv:1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint8_t seq_num;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned tu:1;
 | 
				
			||||||
 | 
						unsigned _r2:7;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						uint64_t stream_id;
 | 
				
			||||||
 | 
						uint32_t timestamp;
 | 
				
			||||||
 | 
					#define AVBTP_AAF_FORMAT_USER		0x00
 | 
				
			||||||
 | 
					#define AVBTP_AAF_FORMAT_FLOAT_32BIT	0x01
 | 
				
			||||||
 | 
					#define AVBTP_AAF_FORMAT_INT_32BIT	0x02
 | 
				
			||||||
 | 
					#define AVBTP_AAF_FORMAT_INT_24BIT	0x03
 | 
				
			||||||
 | 
					#define AVBTP_AAF_FORMAT_INT_16BIT	0x04
 | 
				
			||||||
 | 
					#define AVBTP_AAF_FORMAT_AES3_32BIT	0x05
 | 
				
			||||||
 | 
						uint8_t format;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_NSR_USER		0x00
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_NSR_8KHZ		0x01
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_NSR_16KHZ		0x02
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_NSR_32KHZ		0x03
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_NSR_44_1KHZ	0x04
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_NSR_48KHZ		0x05
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_NSR_88_2KHZ	0x06
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_NSR_96KHZ		0x07
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_NSR_176_4KHZ	0x08
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_NSR_192KHZ	0x09
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_NSR_24KHZ		0x0A
 | 
				
			||||||
 | 
					#if __BYTE_ORDER == __BIG_ENDIAN
 | 
				
			||||||
 | 
						unsigned nsr:4;
 | 
				
			||||||
 | 
						unsigned _r3:4;
 | 
				
			||||||
 | 
					#elif __BYTE_ORDER == __LITTLE_ENDIAN
 | 
				
			||||||
 | 
						unsigned _r3:4;
 | 
				
			||||||
 | 
						unsigned nsr:4;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						uint8_t chan_per_frame;
 | 
				
			||||||
 | 
						uint8_t bit_depth;
 | 
				
			||||||
 | 
						uint16_t data_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_SP_NORMAL		0x00
 | 
				
			||||||
 | 
					#define AVBTP_AAF_PCM_SP_SPARSE		0x01
 | 
				
			||||||
 | 
					#if __BYTE_ORDER == __BIG_ENDIAN
 | 
				
			||||||
 | 
						unsigned _r4:3;
 | 
				
			||||||
 | 
						unsigned sp:1;
 | 
				
			||||||
 | 
						unsigned event:4;
 | 
				
			||||||
 | 
					#elif __BYTE_ORDER == __LITTLE_ENDIAN
 | 
				
			||||||
 | 
						unsigned event:4;
 | 
				
			||||||
 | 
						unsigned sp:1;
 | 
				
			||||||
 | 
						unsigned _r4:3;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						uint8_t _r5;
 | 
				
			||||||
 | 
						uint8_t payload[0];
 | 
				
			||||||
 | 
					} __attribute__ ((__packed__));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_SUBTYPE(p,v)		((p)->subtype = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_SV(p,v)			((p)->sv = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_VERSION(p,v)		((p)->version = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_MR(p,v)			((p)->mr = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_GV(p,v)			((p)->gv = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_TV(p,v)			((p)->tv = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_SEQ_NUM(p,v)		((p)->seq_num = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_TU(p,v)			((p)->tu = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_STREAM_ID(p,v)		((p)->stream_id = htobe64(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_TIMESTAMP(p,v)		((p)->timestamp = htonl(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_DATA_LEN(p,v)		((p)->data_len = htons(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_FORMAT(p,v)		((p)->format = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_NSR(p,v)			((p)->nsr = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_CHAN_PER_FRAME(p,v)	((p)->chan_per_frame = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_BIT_DEPTH(p,v)		((p)->bit_depth = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_SP(p,v)			((p)->sp = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_SET_EVENT(p,v)			((p)->event = (v))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_SUBTYPE(p)			((p)->subtype)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_SV(p)			((p)->sv)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_VERSION(p)			((p)->version)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_MR(p)			((p)->mr)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_GV(p)			((p)->gv)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_TV(p)			((p)->tv)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_SEQ_NUM(p)			((p)->seq_num)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_TU(p)			((p)->tu)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_STREAM_ID(p)		be64toh((p)->stream_id)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_TIMESTAMP(p)		ntohl((p)->timestamp)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_DATA_LEN(p)		ntohs((p)->data_len)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_FORMAT(p)			((p)->format)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_NSR(p)			((p)->nsr)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_CHAN_PER_FRAME(p)		((p)->chan_per_frame)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_BIT_DEPTH(p)		((p)->bit_depth)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_SP(p)			((p)->sp)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_AAF_GET_EVENT(p)			((p)->event)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* AVBTP_AAF_H */
 | 
				
			||||||
							
								
								
									
										382
									
								
								src/modules/module-avbtp/adp.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								src/modules/module-avbtp/adp.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,382 @@
 | 
				
			||||||
 | 
					/* AVB support
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Wim Taymans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <spa/utils/json.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pipewire/pipewire.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "adp.h"
 | 
				
			||||||
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct entity {
 | 
				
			||||||
 | 
						struct spa_list link;
 | 
				
			||||||
 | 
						struct avbtp_packet_adp packet;
 | 
				
			||||||
 | 
						uint64_t last_time;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct adp {
 | 
				
			||||||
 | 
						struct server *server;
 | 
				
			||||||
 | 
						struct spa_hook server_listener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct spa_list entities;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct entity *find_entity_by_id(struct adp *adp, uint64_t id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct entity *e;
 | 
				
			||||||
 | 
						spa_list_for_each(e, &adp->entities, link)
 | 
				
			||||||
 | 
							if (AVBTP_PACKET_ADP_GET_ENTITY_ID(&e->packet) == id)
 | 
				
			||||||
 | 
								return e;
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct bit_info {
 | 
				
			||||||
 | 
						uint32_t bits;
 | 
				
			||||||
 | 
						const char *value;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct bit_info entity_capabilities_info[] = {
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_EFU_MODE, "EFU Mode" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_ADDRESS_ACCESS_SUPPORTED, "Address Access Supported" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_GATEWAY_ENTITY, "Gateway Entity" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_AEM_SUPPORTED, "AEM Supported" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_LEGACY_AVC, "Legacy AVC" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_ASSOCIATION_ID_SUPPORTED, "Association Id Supported" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_ASSOCIATION_ID_VALID, "Association Id Valid" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_VENDOR_UNIQUE_SUPPORTED, "Vendor Unique Supported" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_CLASS_A_SUPPORTED, "Class A Supported" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_CLASS_B_SUPPORTED, "Class B Supported" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_GPTP_SUPPORTED, "gPTP Supported" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_AEM_AUTHENTICATION_SUPPORTED, "AEM Authentication Supported" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_AEM_AUTHENTICATION_REQUIRED, "AEM Authentication Required" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_AEM_PERSISTENT_ACQUIRE_SUPPORTED, "AEM Persisitent Acquire Supported" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_AEM_IDENTIFY_CONTROL_INDEX_VALID, "AEM Identify Control Index Valid" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_AEM_INTERFACE_INDEX_VALID, "AEM Interface Index Valid" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_GENERAL_CONTROLLER_IGNORE, "General Controller Ignore" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_ENTITY_CAPABILITY_ENTITY_NOT_READY, "Entity Not Ready" },
 | 
				
			||||||
 | 
						{ 0, NULL },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static const struct bit_info talker_capabilities_info[] = {
 | 
				
			||||||
 | 
						{ AVBTP_ADP_TALKER_CAPABILITY_IMPLEMENTED, "Implemented" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_TALKER_CAPABILITY_OTHER_SOURCE, "Other Source" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_TALKER_CAPABILITY_CONTROL_SOURCE, "Control Source" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_TALKER_CAPABILITY_MEDIA_CLOCK_SOURCE, "Media Clock Source" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_TALKER_CAPABILITY_SMPTE_SOURCE, "SMPTE Source" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_TALKER_CAPABILITY_MIDI_SOURCE, "MIDI Source" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_TALKER_CAPABILITY_AUDIO_SOURCE, "Audio Source" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_TALKER_CAPABILITY_VIDEO_SOURCE, "Video Source" },
 | 
				
			||||||
 | 
						{ 0, NULL },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct bit_info listener_capabilities_info[] = {
 | 
				
			||||||
 | 
						{ AVBTP_ADP_LISTENER_CAPABILITY_IMPLEMENTED, "Implemented" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_LISTENER_CAPABILITY_OTHER_SINK, "Other Sink" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_LISTENER_CAPABILITY_CONTROL_SINK, "Control Sink" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_LISTENER_CAPABILITY_MEDIA_CLOCK_SINK, "Media Clock Sink" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_LISTENER_CAPABILITY_SMPTE_SINK, "SMPTE Sink" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_LISTENER_CAPABILITY_MIDI_SINK, "MIDI Sink" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_LISTENER_CAPABILITY_AUDIO_SINK, "Audio Sink" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_LISTENER_CAPABILITY_VIDEO_SINK, "Video Sink" },
 | 
				
			||||||
 | 
						{ 0, NULL },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct bit_info controller_capabilities_info[] = {
 | 
				
			||||||
 | 
						{ AVBTP_ADP_CONTROLLER_CAPABILITY_IMPLEMENTED, "Implemented" },
 | 
				
			||||||
 | 
						{ AVBTP_ADP_CONTROLLER_CAPABILITY_LAYER3_PROXY, "Layer 3 Proxy" },
 | 
				
			||||||
 | 
						{ 0, NULL },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static void print_bit_info(int indent, uint32_t bits, const struct bit_info *info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint32_t i;
 | 
				
			||||||
 | 
						for (i = 0; info[i].value; i++) {
 | 
				
			||||||
 | 
							if ((info[i].bits & bits) == info[i].bits)
 | 
				
			||||||
 | 
								pw_log_info("%*.s%08x %s", indent, "", info[i].bits, info[i].value);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *message_type_as_string(uint8_t message_type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (message_type) {
 | 
				
			||||||
 | 
						case AVBTP_ADP_MESSAGE_TYPE_ENTITY_AVAILABLE:
 | 
				
			||||||
 | 
							return "ENTITY_AVAIALABLE";
 | 
				
			||||||
 | 
						case AVBTP_ADP_MESSAGE_TYPE_ENTITY_DEPARTING:
 | 
				
			||||||
 | 
							return "ENTITY_DEPARTING";
 | 
				
			||||||
 | 
						case AVBTP_ADP_MESSAGE_TYPE_ENTITY_DISCOVER:
 | 
				
			||||||
 | 
							return "ENTITY_DISCOVER";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "INVALID";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define KEY_VALID_TIME			"valid-time"
 | 
				
			||||||
 | 
					#define KEY_ENTITY_ID			"entity-id"
 | 
				
			||||||
 | 
					#define KEY_ENTITY_MODEL_ID		"entity-model-id"
 | 
				
			||||||
 | 
					#define KEY_ENTITY_CAPABILITIES		"entity-capabilities"
 | 
				
			||||||
 | 
					#define KEY_TALKER_STREAM_SOURCES	"talker-stream-sources"
 | 
				
			||||||
 | 
					#define KEY_TALKER_CAPABILITIES		"talker-capabilities"
 | 
				
			||||||
 | 
					#define KEY_LISTENER_STREAM_SINKS	"listener-stream-sinks"
 | 
				
			||||||
 | 
					#define KEY_LISTENER_CAPABILITIES	"listener-capabilities"
 | 
				
			||||||
 | 
					#define KEY_CONTROLLER_CAPABILITIES	"controller-capabilities"
 | 
				
			||||||
 | 
					#define KEY_AVAILABLE_INDEX		"available-index"
 | 
				
			||||||
 | 
					#define KEY_GPTP_GRANDMASTER_ID		"gptp-grandmaster-id"
 | 
				
			||||||
 | 
					#define KEY_ASSOCIATION_ID		"association-id"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline char *format_id(char *str, size_t size, const uint64_t id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x:%04x",
 | 
				
			||||||
 | 
								(uint8_t)(id >> 56),
 | 
				
			||||||
 | 
								(uint8_t)(id >> 48),
 | 
				
			||||||
 | 
								(uint8_t)(id >> 40),
 | 
				
			||||||
 | 
								(uint8_t)(id >> 32),
 | 
				
			||||||
 | 
								(uint8_t)(id >> 24),
 | 
				
			||||||
 | 
								(uint8_t)(id >> 16),
 | 
				
			||||||
 | 
								(uint16_t)(id));
 | 
				
			||||||
 | 
						return str;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					static void adp_message_debug(struct adp *adp, const struct avbtp_packet_adp *p)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint32_t v;
 | 
				
			||||||
 | 
						char buf[256];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v = AVBTP_PACKET_ADP_GET_MESSAGE_TYPE(p);
 | 
				
			||||||
 | 
						pw_log_info("message-type: %d (%s)", v, message_type_as_string(v));
 | 
				
			||||||
 | 
						pw_log_info("  length: %d", AVBTP_PACKET_ADP_GET_LENGTH(p));
 | 
				
			||||||
 | 
						pw_log_info("  "KEY_VALID_TIME": %d", AVBTP_PACKET_ADP_GET_VALID_TIME(p));
 | 
				
			||||||
 | 
						pw_log_info("  "KEY_ENTITY_ID": %s", format_id(buf, sizeof(buf), AVBTP_PACKET_ADP_GET_ENTITY_ID(p)));
 | 
				
			||||||
 | 
						pw_log_info("  "KEY_ENTITY_MODEL_ID": 0x%"PRIx64, AVBTP_PACKET_ADP_GET_ENTITY_MODEL_ID(p));
 | 
				
			||||||
 | 
						v = AVBTP_PACKET_ADP_GET_ENTITY_CAPABILITIES(p);
 | 
				
			||||||
 | 
						pw_log_info("  "KEY_ENTITY_CAPABILITIES": 0x%08x", v);
 | 
				
			||||||
 | 
						print_bit_info(4, v, entity_capabilities_info);
 | 
				
			||||||
 | 
						pw_log_info("  "KEY_TALKER_STREAM_SOURCES": %d", AVBTP_PACKET_ADP_GET_TALKER_STREAM_SOURCES(p));
 | 
				
			||||||
 | 
						v = AVBTP_PACKET_ADP_GET_TALKER_CAPABILITIES(p);
 | 
				
			||||||
 | 
						pw_log_info("  "KEY_TALKER_CAPABILITIES": %04x", v);
 | 
				
			||||||
 | 
						print_bit_info(4, v, talker_capabilities_info);
 | 
				
			||||||
 | 
						pw_log_info("  "KEY_LISTENER_STREAM_SINKS": %d", AVBTP_PACKET_ADP_GET_LISTENER_STREAM_SINKS(p));
 | 
				
			||||||
 | 
						v = AVBTP_PACKET_ADP_GET_LISTENER_CAPABILITIES(p);
 | 
				
			||||||
 | 
						pw_log_info("  "KEY_LISTENER_CAPABILITIES": %04x", v);
 | 
				
			||||||
 | 
						print_bit_info(4, v, listener_capabilities_info);
 | 
				
			||||||
 | 
						v = AVBTP_PACKET_ADP_GET_CONTROLLER_CAPABILITIES(p);
 | 
				
			||||||
 | 
						pw_log_info("  "KEY_CONTROLLER_CAPABILITIES": %08x", v);
 | 
				
			||||||
 | 
						print_bit_info(4, v, controller_capabilities_info);
 | 
				
			||||||
 | 
						pw_log_info("  "KEY_AVAILABLE_INDEX": 0x%08x", AVBTP_PACKET_ADP_GET_AVAILABLE_INDEX(p));
 | 
				
			||||||
 | 
						pw_log_info("  "KEY_GPTP_GRANDMASTER_ID": 0x%"PRIx64, AVBTP_PACKET_ADP_GET_GPTP_GRANDMASTER_ID(p));
 | 
				
			||||||
 | 
						pw_log_info("  "KEY_ASSOCIATION_ID": 0x%08x", AVBTP_PACKET_ADP_GET_ASSOCIATION_ID(p));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int adp_message(void *data, uint64_t now, const void *message, int len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct adp *adp = data;
 | 
				
			||||||
 | 
						const struct avbtp_packet_adp *p = message;
 | 
				
			||||||
 | 
						struct entity *e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (AVBTP_PACKET_GET_SUBTYPE(p) != AVBTP_SUBTYPE_ADP)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e = find_entity_by_id(adp, AVBTP_PACKET_ADP_GET_ENTITY_ID(p));
 | 
				
			||||||
 | 
						if (e == NULL) {
 | 
				
			||||||
 | 
							e = calloc(1, sizeof(*e));
 | 
				
			||||||
 | 
							if (e == NULL)
 | 
				
			||||||
 | 
								return -errno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							e->packet = *p;
 | 
				
			||||||
 | 
							spa_list_append(&adp->entities, &e->link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (adp->server->debug_messages)
 | 
				
			||||||
 | 
								adp_message_debug(adp, p);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e->last_time = now;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void adp_destroy(void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct adp *adp = data;
 | 
				
			||||||
 | 
						spa_hook_remove(&adp->server_listener);
 | 
				
			||||||
 | 
						free(adp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void adp_periodic(void *data, uint64_t now)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct adp *adp = data;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int parse_id(const char *value, int len, uint64_t *id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char str[64];
 | 
				
			||||||
 | 
						uint8_t v[6];
 | 
				
			||||||
 | 
						uint16_t unique_id;
 | 
				
			||||||
 | 
						if (spa_json_parse_stringn(value, len, str, sizeof(str)) <= 0)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						if (sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hx",
 | 
				
			||||||
 | 
								&v[0], &v[1], &v[2], &v[3],
 | 
				
			||||||
 | 
								&v[4], &v[5], &unique_id) != 7)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						*id = (uint64_t) v[0] << 56 |
 | 
				
			||||||
 | 
							    (uint64_t) v[1] << 48 |
 | 
				
			||||||
 | 
							    (uint64_t) v[2] << 40 |
 | 
				
			||||||
 | 
							    (uint64_t) v[3] << 32 |
 | 
				
			||||||
 | 
							    (uint64_t) v[4] << 24 |
 | 
				
			||||||
 | 
							    (uint64_t) v[5] << 16 |
 | 
				
			||||||
 | 
							    unique_id;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					static int parse_bits(const char *value, int len, int *bits)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						*bits = 0;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int do_advertise(struct adp *adp, const char *args)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct entity *e;
 | 
				
			||||||
 | 
						struct spa_json it[2];
 | 
				
			||||||
 | 
						char key[128];
 | 
				
			||||||
 | 
						struct avbtp_packet_adp *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e = calloc(1, sizeof(*e));
 | 
				
			||||||
 | 
						if (e == NULL)
 | 
				
			||||||
 | 
							return -errno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_json_init(&it[0], args, strlen(args));
 | 
				
			||||||
 | 
						if (spa_json_enter_object(&it[0], &it[1]) <= 0)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p = &e->packet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (spa_json_get_string(&it[1], key, sizeof(key)) > 0) {
 | 
				
			||||||
 | 
							int len, int_val;
 | 
				
			||||||
 | 
							const char *value;
 | 
				
			||||||
 | 
							uint64_t id_val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if ((len = spa_json_next(&it[1], &value)) <= 0)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (spa_json_is_null(value, len))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (spa_streq(key, KEY_VALID_TIME)) {
 | 
				
			||||||
 | 
								if (spa_json_parse_int(value, len, &int_val))
 | 
				
			||||||
 | 
									AVBTP_PACKET_ADP_SET_VALID_TIME(p, int_val);
 | 
				
			||||||
 | 
							} else if (spa_streq(key, KEY_ENTITY_ID)) {
 | 
				
			||||||
 | 
								if (parse_id(value, len, &id_val))
 | 
				
			||||||
 | 
									AVBTP_PACKET_ADP_SET_ENTITY_ID(p, id_val);
 | 
				
			||||||
 | 
							} else if (spa_streq(key, KEY_ENTITY_MODEL_ID)) {
 | 
				
			||||||
 | 
								if (parse_id(value, len, &id_val))
 | 
				
			||||||
 | 
									AVBTP_PACKET_ADP_SET_ENTITY_MODEL_ID(p, id_val);
 | 
				
			||||||
 | 
							} else if (spa_streq(key, KEY_ENTITY_CAPABILITIES)) {
 | 
				
			||||||
 | 
								if (parse_bits(value, len, &int_val))
 | 
				
			||||||
 | 
									AVBTP_PACKET_ADP_SET_ENTITY_CAPABILITIES(p, int_val);
 | 
				
			||||||
 | 
							} else if (spa_streq(key, KEY_TALKER_STREAM_SOURCES)) {
 | 
				
			||||||
 | 
								if (spa_json_parse_int(value, len, &int_val))
 | 
				
			||||||
 | 
									AVBTP_PACKET_ADP_SET_TALKER_STREAM_SOURCES(p, int_val);
 | 
				
			||||||
 | 
							} else if (spa_streq(key, KEY_TALKER_CAPABILITIES)) {
 | 
				
			||||||
 | 
								if (parse_bits(value, len, &int_val))
 | 
				
			||||||
 | 
									AVBTP_PACKET_ADP_SET_TALKER_CAPABILITIES(p, int_val);
 | 
				
			||||||
 | 
							} else if (spa_streq(key, KEY_LISTENER_STREAM_SINKS)) {
 | 
				
			||||||
 | 
								if (spa_json_parse_int(value, len, &int_val))
 | 
				
			||||||
 | 
									AVBTP_PACKET_ADP_SET_LISTENER_STREAM_SINKS(p, int_val);
 | 
				
			||||||
 | 
							} else if (spa_streq(key, KEY_LISTENER_CAPABILITIES)) {
 | 
				
			||||||
 | 
								if (parse_bits(value, len, &int_val))
 | 
				
			||||||
 | 
									AVBTP_PACKET_ADP_SET_LISTENER_CAPABILITIES(p, int_val);
 | 
				
			||||||
 | 
							} else if (spa_streq(key, KEY_CONTROLLER_CAPABILITIES)) {
 | 
				
			||||||
 | 
								if (parse_bits(value, len, &int_val))
 | 
				
			||||||
 | 
									AVBTP_PACKET_ADP_SET_CONTROLLER_CAPABILITIES(p, int_val);
 | 
				
			||||||
 | 
							} else if (spa_streq(key, KEY_AVAILABLE_INDEX)) {
 | 
				
			||||||
 | 
								if (spa_json_parse_int(value, len, &int_val))
 | 
				
			||||||
 | 
									AVBTP_PACKET_ADP_SET_AVAILABLE_INDEX(p, int_val);
 | 
				
			||||||
 | 
							} else if (spa_streq(key, KEY_GPTP_GRANDMASTER_ID)) {
 | 
				
			||||||
 | 
								if (parse_id(value, len, &id_val))
 | 
				
			||||||
 | 
									AVBTP_PACKET_ADP_SET_GPTP_GRANDMASTER_ID(p, id_val);
 | 
				
			||||||
 | 
							} else if (spa_streq(key, KEY_ASSOCIATION_ID)) {
 | 
				
			||||||
 | 
								if (spa_json_parse_int(value, len, &int_val))
 | 
				
			||||||
 | 
									AVBTP_PACKET_ADP_SET_ASSOCIATION_ID(p, int_val);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (find_entity_by_id(adp, AVBTP_PACKET_ADP_GET_ENTITY_ID(p))) {
 | 
				
			||||||
 | 
							free(e);
 | 
				
			||||||
 | 
							return -EEXIST;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						spa_list_append(&adp->entities, &e->link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (adp->server->debug_messages)
 | 
				
			||||||
 | 
							adp_message_debug(adp, p);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int do_depart(struct adp *adp, const char *args)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int do_discover(struct adp *adp, const char *args)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int adp_command(void *data, const char *command, const char *args)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct adp *adp = data;
 | 
				
			||||||
 | 
						int res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (spa_streq(command, "/adp/advertise"))
 | 
				
			||||||
 | 
							res = do_advertise(adp, args);
 | 
				
			||||||
 | 
						else if (spa_streq(command, "/adp/depart"))
 | 
				
			||||||
 | 
							res = do_depart(adp, args);
 | 
				
			||||||
 | 
						else if (spa_streq(command, "/adp/discover"))
 | 
				
			||||||
 | 
							res = do_discover(adp, args);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							res = -ENOTSUP;
 | 
				
			||||||
 | 
						return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct server_events server_events = {
 | 
				
			||||||
 | 
						AVBTP_VERSION_SERVER_EVENTS,
 | 
				
			||||||
 | 
						.destroy = adp_destroy,
 | 
				
			||||||
 | 
						.message = adp_message,
 | 
				
			||||||
 | 
						.periodic = adp_periodic,
 | 
				
			||||||
 | 
						.command = adp_command
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct avbtp_adp *avbtp_adp_register(struct server *server)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct adp *adp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						adp = calloc(1, sizeof(*adp));
 | 
				
			||||||
 | 
						if (adp == NULL)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						adp->server = server;
 | 
				
			||||||
 | 
						spa_list_init(&adp->entities);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						avdecc_server_add_listener(server, &adp->server_listener, &server_events, adp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (struct avbtp_adp*)adp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void avbtp_adp_unregister(struct avbtp_adp *adp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						adp_destroy(adp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										148
									
								
								src/modules/module-avbtp/adp.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								src/modules/module-avbtp/adp.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,148 @@
 | 
				
			||||||
 | 
					/* AVB support
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Wim Taymans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef AVBTP_ADP_H
 | 
				
			||||||
 | 
					#define AVBTP_ADP_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "packets.h"
 | 
				
			||||||
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_ADP_MESSAGE_TYPE_ENTITY_AVAILABLE		0
 | 
				
			||||||
 | 
					#define AVBTP_ADP_MESSAGE_TYPE_ENTITY_DEPARTING		1
 | 
				
			||||||
 | 
					#define AVBTP_ADP_MESSAGE_TYPE_ENTITY_DISCOVER		2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_EFU_MODE				(1u<<0)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_ADDRESS_ACCESS_SUPPORTED		(1u<<1)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_GATEWAY_ENTITY			(1u<<2)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_AEM_SUPPORTED			(1u<<3)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_LEGACY_AVC				(1u<<4)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_ASSOCIATION_ID_SUPPORTED		(1u<<5)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_ASSOCIATION_ID_VALID		(1u<<6)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_VENDOR_UNIQUE_SUPPORTED		(1u<<7)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_CLASS_A_SUPPORTED			(1u<<8)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_CLASS_B_SUPPORTED			(1u<<9)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_GPTP_SUPPORTED			(1u<<10)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_AEM_AUTHENTICATION_SUPPORTED	(1u<<11)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_AEM_AUTHENTICATION_REQUIRED		(1u<<12)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_AEM_PERSISTENT_ACQUIRE_SUPPORTED	(1u<<13)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_AEM_IDENTIFY_CONTROL_INDEX_VALID	(1u<<14)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_AEM_INTERFACE_INDEX_VALID		(1u<<15)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_GENERAL_CONTROLLER_IGNORE		(1u<<16)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_ENTITY_CAPABILITY_ENTITY_NOT_READY			(1u<<17)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_ADP_TALKER_CAPABILITY_IMPLEMENTED				(1u<<0)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_TALKER_CAPABILITY_OTHER_SOURCE			(1u<<9)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_TALKER_CAPABILITY_CONTROL_SOURCE			(1u<<10)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_TALKER_CAPABILITY_MEDIA_CLOCK_SOURCE			(1u<<11)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_TALKER_CAPABILITY_SMPTE_SOURCE			(1u<<12)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_TALKER_CAPABILITY_MIDI_SOURCE				(1u<<13)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_TALKER_CAPABILITY_AUDIO_SOURCE			(1u<<14)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_TALKER_CAPABILITY_VIDEO_SOURCE			(1u<<15)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_ADP_LISTENER_CAPABILITY_IMPLEMENTED			(1u<<0)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_LISTENER_CAPABILITY_OTHER_SINK			(1u<<9)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_LISTENER_CAPABILITY_CONTROL_SINK			(1u<<10)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_LISTENER_CAPABILITY_MEDIA_CLOCK_SINK			(1u<<11)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_LISTENER_CAPABILITY_SMPTE_SINK			(1u<<12)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_LISTENER_CAPABILITY_MIDI_SINK				(1u<<13)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_LISTENER_CAPABILITY_AUDIO_SINK			(1u<<14)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_LISTENER_CAPABILITY_VIDEO_SINK			(1u<<15)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_ADP_CONTROLLER_CAPABILITY_IMPLEMENTED			(1u<<0)
 | 
				
			||||||
 | 
					#define AVBTP_ADP_CONTROLLER_CAPABILITY_LAYER3_PROXY			(1u<<1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct avbtp_packet_adp {
 | 
				
			||||||
 | 
						uint8_t subtype;
 | 
				
			||||||
 | 
					#if __BYTE_ORDER == __BIG_ENDIAN
 | 
				
			||||||
 | 
						unsigned sv:1;
 | 
				
			||||||
 | 
						unsigned version:3;
 | 
				
			||||||
 | 
						unsigned message_type:4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned valid_time:5;
 | 
				
			||||||
 | 
						unsigned len1:3;
 | 
				
			||||||
 | 
					#elif __BYTE_ORDER == __LITTLE_ENDIAN
 | 
				
			||||||
 | 
						unsigned message_type:4;
 | 
				
			||||||
 | 
						unsigned version:3;
 | 
				
			||||||
 | 
						unsigned sv:1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned len1:3;
 | 
				
			||||||
 | 
						unsigned valid_time:5;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						uint8_t len2:8;
 | 
				
			||||||
 | 
						uint64_t entity_id;
 | 
				
			||||||
 | 
						uint64_t entity_model_id;
 | 
				
			||||||
 | 
						uint32_t entity_capabilities;
 | 
				
			||||||
 | 
						uint16_t talker_stream_sources;
 | 
				
			||||||
 | 
						uint16_t talker_capabilities;
 | 
				
			||||||
 | 
						uint16_t listener_stream_sinks;
 | 
				
			||||||
 | 
						uint16_t listener_capabilities;
 | 
				
			||||||
 | 
						uint32_t controller_capabilities;
 | 
				
			||||||
 | 
						uint32_t available_index;
 | 
				
			||||||
 | 
						uint64_t gptp_grandmaster_id;
 | 
				
			||||||
 | 
						uint32_t reserved0;
 | 
				
			||||||
 | 
						uint16_t identify_control_index;
 | 
				
			||||||
 | 
						uint16_t interface_index;
 | 
				
			||||||
 | 
						uint32_t association_id;
 | 
				
			||||||
 | 
						uint32_t reserved1;
 | 
				
			||||||
 | 
					} __attribute__ ((__packed__));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_SUBTYPE(p,v)		((p)->subtype = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_SV(p,v)			((p)->sv = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_VERSION(p,v)		((p)->version = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_MESSAGE_TYPE(p,v)		((p)->message_type = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_VALID_TIME(p,v)		((p)->valid_time = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_LENGTH(p,v)		((p)->len1 = ((v) >> 8),(p)->len2 = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_ENTITY_ID(p,v)		((p)->entity_id = htobe64(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_ENTITY_MODEL_ID(p,v)	((p)->entity_model_id = htobe64(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_ENTITY_CAPABILITIES(p,v)	((p)->entity_capabilities = htonl(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_TALKER_STREAM_SOURCES(p,v)	((p)->talker_stream_sources = htons(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_TALKER_CAPABILITIES(p,v)	((p)->talker_capabilities = htons(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_LISTENER_STREAM_SINKS(p,v)	((p)->listener_stream_sinks = htons(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_LISTENER_CAPABILITIES(p,v)	((p)->listener_capabilities = htons(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_CONTROLLER_CAPABILITIES(p,v)	((p)->controller_capabilities = htonl(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_AVAILABLE_INDEX(p,v)	((p)->available_index = htonl(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_GPTP_GRANDMASTER_ID(p,v)	((p)->gptp_grandmaster_id = htobe64(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_SET_ASSOCIATION_ID(p,v)	((p)->association_id = htonl(v))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_SUBTYPE(p)			((p)->subtype)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_SV(p)			((p)->sv)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_VERSION(p)			((p)->version)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_MESSAGE_TYPE(p)		((p)->message_type)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_VALID_TIME(p)		((p)->valid_time)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_LENGTH(p)			(((p)->len1 << 8) | (p)->len2)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_ENTITY_ID(p)		be64toh((p)->entity_id)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_ENTITY_MODEL_ID(p)		be64toh((p)->entity_model_id)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_ENTITY_CAPABILITIES(p)	ntohl((p)->entity_capabilities)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_TALKER_STREAM_SOURCES(p)	ntohs((p)->talker_stream_sources)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_TALKER_CAPABILITIES(p)	ntohs((p)->talker_capabilities)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_LISTENER_STREAM_SINKS(p)	ntohs((p)->listener_stream_sinks)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_LISTENER_CAPABILITIES(p)	ntohs((p)->listener_capabilities)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_CONTROLLER_CAPABILITIES(p)	ntohl((p)->controller_capabilities)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_AVAILABLE_INDEX(p)		ntohl((p)->available_index)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_GPTP_GRANDMASTER_ID(p)	be64toh((p)->gptp_grandmaster_id)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_ADP_GET_ASSOCIATION_ID(p)		ntohl((p)->association_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct avbtp_adp *avbtp_adp_register(struct server *server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* AVBTP_ADP_H */
 | 
				
			||||||
							
								
								
									
										93
									
								
								src/modules/module-avbtp/avb.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/modules/module-avbtp/avb.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,93 @@
 | 
				
			||||||
 | 
					/* PipeWire
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Wim Taymans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <spa/support/cpu.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pw_avb *pw_avb_new(struct pw_context *context,
 | 
				
			||||||
 | 
							struct pw_properties *props, size_t user_data_size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl;
 | 
				
			||||||
 | 
						const struct spa_support *support;
 | 
				
			||||||
 | 
						uint32_t n_support;
 | 
				
			||||||
 | 
						struct spa_cpu *cpu;
 | 
				
			||||||
 | 
						const char *str;
 | 
				
			||||||
 | 
						int res = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl = calloc(1, sizeof(*impl) + user_data_size);
 | 
				
			||||||
 | 
						if (impl == NULL)
 | 
				
			||||||
 | 
							goto error_exit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (props == NULL)
 | 
				
			||||||
 | 
							props = pw_properties_new(NULL, NULL);
 | 
				
			||||||
 | 
						if (props == NULL)
 | 
				
			||||||
 | 
							goto error_free;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						support = pw_context_get_support(context, &n_support);
 | 
				
			||||||
 | 
						cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_context_conf_update_props(context, "avb.properties", props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((str = pw_properties_get(props, "vm.overrides")) != NULL) {
 | 
				
			||||||
 | 
							if (cpu != NULL && spa_cpu_get_vm_type(cpu) != SPA_CPU_VM_NONE)
 | 
				
			||||||
 | 
								pw_properties_update_string(props, str, strlen(str));
 | 
				
			||||||
 | 
							pw_properties_set(props, "vm.overrides", NULL);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl->context = context;
 | 
				
			||||||
 | 
						impl->loop = pw_context_get_main_loop(context);
 | 
				
			||||||
 | 
						impl->props = props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl->work_queue = pw_context_get_work_queue(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_list_init(&impl->servers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						avdecc_server_new(impl, pw_properties_get(props, "ifname"),  NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (struct pw_avb*)impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					error_free:
 | 
				
			||||||
 | 
						free(impl);
 | 
				
			||||||
 | 
					error_exit:
 | 
				
			||||||
 | 
						pw_properties_free(props);
 | 
				
			||||||
 | 
						if (res < 0)
 | 
				
			||||||
 | 
							errno = -res;
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void impl_free(struct impl *impl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct server *s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_list_consume(s, &impl->servers, link)
 | 
				
			||||||
 | 
							avdecc_server_free(s);
 | 
				
			||||||
 | 
						free(impl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void pw_avb_destroy(struct pw_avb *avb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl = (struct impl*)avb;
 | 
				
			||||||
 | 
						impl_free(impl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										44
									
								
								src/modules/module-avbtp/avb.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/modules/module-avbtp/avb.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					/* PipeWire
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Wim Taymans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef PIPEWIRE_AVB_H
 | 
				
			||||||
 | 
					#define PIPEWIRE_AVB_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pw_context;
 | 
				
			||||||
 | 
					struct pw_properties;
 | 
				
			||||||
 | 
					struct pw_avb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pw_avb *pw_avb_new(struct pw_context *context,
 | 
				
			||||||
 | 
							struct pw_properties *props, size_t user_data_size);
 | 
				
			||||||
 | 
					void pw_avb_destroy(struct pw_avb *avb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}  /* extern "C" */
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* PIPEWIRE_AVB_H */
 | 
				
			||||||
							
								
								
									
										214
									
								
								src/modules/module-avbtp/avdecc.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								src/modules/module-avbtp/avdecc.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,214 @@
 | 
				
			||||||
 | 
					/* PipeWire
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Wim Taymans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/if_ether.h>
 | 
				
			||||||
 | 
					#include <linux/if_packet.h>
 | 
				
			||||||
 | 
					#include <linux/net_tstamp.h>
 | 
				
			||||||
 | 
					#include <limits.h>
 | 
				
			||||||
 | 
					#include <net/if.h>
 | 
				
			||||||
 | 
					#include <arpa/inet.h>
 | 
				
			||||||
 | 
					#include <sys/ioctl.h>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <spa/support/cpu.h>
 | 
				
			||||||
 | 
					#include <spa/debug/mem.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pipewire/pipewire.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "avb.h"
 | 
				
			||||||
 | 
					#include "packets.h"
 | 
				
			||||||
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					#include "adp.h"
 | 
				
			||||||
 | 
					#include "maap.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DEFAULT_INTERVAL	1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define server_emit(s,m,v,...) spa_hook_list_call(&s->listener_list, struct server_events, m, v, ##__VA_ARGS__)
 | 
				
			||||||
 | 
					#define server_emit_destroy(s)		server_emit(s, destroy, 0)
 | 
				
			||||||
 | 
					#define server_emit_message(s,n,m,l)	server_emit(s, message, 0, n, m, l)
 | 
				
			||||||
 | 
					#define server_emit_periodic(s,n)	server_emit(s, periodic, 0, n)
 | 
				
			||||||
 | 
					#define server_emit_command(s,c,a)	server_emit(s, command, 0, c, a)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void on_timer_event(void *data, uint64_t expirations)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct server *server = data;
 | 
				
			||||||
 | 
						struct timespec now;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clock_gettime(CLOCK_REALTIME, &now);
 | 
				
			||||||
 | 
						server_emit_periodic(server, SPA_TIMESPEC_TO_NSEC(&now));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void on_socket_data(void *data, int fd, uint32_t mask)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct server *server = data;
 | 
				
			||||||
 | 
						struct timespec now;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mask & SPA_IO_IN) {
 | 
				
			||||||
 | 
							int len;
 | 
				
			||||||
 | 
							uint8_t buffer[2048];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							len = read(fd, buffer, sizeof(buffer));
 | 
				
			||||||
 | 
							if (len < 0) {
 | 
				
			||||||
 | 
								pw_log_warn("got error: %m");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else if (len < (int)sizeof(struct avbtp_packet_common)) {
 | 
				
			||||||
 | 
								pw_log_warn("short packet received (%d < %d)", len,
 | 
				
			||||||
 | 
										(int)sizeof(struct avbtp_packet_common));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								clock_gettime(CLOCK_REALTIME, &now);
 | 
				
			||||||
 | 
								server_emit_message(server, SPA_TIMESPEC_TO_NSEC(&now),
 | 
				
			||||||
 | 
										buffer, len);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int setup_socket(struct server *server)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl = server->impl;
 | 
				
			||||||
 | 
						int fd, res, ifindex;
 | 
				
			||||||
 | 
						struct ifreq req;
 | 
				
			||||||
 | 
						struct packet_mreq mreq;
 | 
				
			||||||
 | 
						struct sockaddr_ll sll;
 | 
				
			||||||
 | 
						struct timespec value, interval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fd = socket(AF_PACKET, SOCK_DGRAM|SOCK_NONBLOCK, htons(ETH_P_TSN));
 | 
				
			||||||
 | 
						if (fd < 0) {
 | 
				
			||||||
 | 
							pw_log_error("socket() failed: %m");
 | 
				
			||||||
 | 
							return -errno;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_zero(req);
 | 
				
			||||||
 | 
						snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", server->ifname);
 | 
				
			||||||
 | 
						if (ioctl(fd, SIOCGIFINDEX, &req) < 0) {
 | 
				
			||||||
 | 
							res = -errno;
 | 
				
			||||||
 | 
							pw_log_error("SIOCGIFINDEX %s failed: %m", server->ifname);
 | 
				
			||||||
 | 
							goto error_close;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ifindex = req.ifr_ifindex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_zero(req);
 | 
				
			||||||
 | 
						snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", server->ifname);
 | 
				
			||||||
 | 
						if (ioctl(fd, SIOCGIFHWADDR, &req) < 0) {
 | 
				
			||||||
 | 
							res = -errno;
 | 
				
			||||||
 | 
							pw_log_error("SIOCGIFHWADDR %s failed: %m", server->ifname);
 | 
				
			||||||
 | 
							goto error_close;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						memcpy(server->mac_addr, req.ifr_hwaddr.sa_data, sizeof(server->mac_addr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_zero(sll);
 | 
				
			||||||
 | 
						sll.sll_family = AF_PACKET;
 | 
				
			||||||
 | 
						sll.sll_protocol = htons(ETH_P_TSN);
 | 
				
			||||||
 | 
						sll.sll_ifindex = ifindex;
 | 
				
			||||||
 | 
						if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) < 0) {
 | 
				
			||||||
 | 
							res = -errno;
 | 
				
			||||||
 | 
							pw_log_error("bind() failed: %m");
 | 
				
			||||||
 | 
							goto error_close;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_zero(mreq);
 | 
				
			||||||
 | 
						mreq.mr_ifindex = ifindex;
 | 
				
			||||||
 | 
						mreq.mr_type = PACKET_MR_ALLMULTI;
 | 
				
			||||||
 | 
						if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
 | 
				
			||||||
 | 
									&mreq, sizeof(struct packet_mreq)) < 0) {
 | 
				
			||||||
 | 
							res = -errno;
 | 
				
			||||||
 | 
							pw_log_error("setsockopt(ADD_MEMBERSHIP) failed: %m");
 | 
				
			||||||
 | 
							goto error_close;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server->source = pw_loop_add_io(impl->loop, fd, SPA_IO_IN, true, on_socket_data, server);
 | 
				
			||||||
 | 
						if (server->source == NULL) {
 | 
				
			||||||
 | 
							res = -errno;
 | 
				
			||||||
 | 
							pw_log_error("server %p: can't create server source: %m", impl);
 | 
				
			||||||
 | 
							goto error_close;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						server->timer = pw_loop_add_timer(impl->loop, on_timer_event, server);
 | 
				
			||||||
 | 
						if (server->timer == NULL) {
 | 
				
			||||||
 | 
							res = -errno;
 | 
				
			||||||
 | 
							pw_log_error("server %p: can't create timer source: %m", impl);
 | 
				
			||||||
 | 
							goto error_close;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						value.tv_sec = 0;
 | 
				
			||||||
 | 
					        value.tv_nsec = 1;
 | 
				
			||||||
 | 
						interval.tv_sec = DEFAULT_INTERVAL;
 | 
				
			||||||
 | 
					        interval.tv_nsec = 0;
 | 
				
			||||||
 | 
					        pw_loop_update_timer(impl->loop, server->timer, &value, &interval, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					error_close:
 | 
				
			||||||
 | 
						close(fd);
 | 
				
			||||||
 | 
						return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct server *avdecc_server_new(struct impl *impl, const char *ifname, struct spa_dict *props)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct server *server;
 | 
				
			||||||
 | 
						int res = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server = calloc(1, sizeof(*server));
 | 
				
			||||||
 | 
						if (server == NULL)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server->impl = impl;
 | 
				
			||||||
 | 
						spa_list_append(&impl->servers, &server->link);
 | 
				
			||||||
 | 
						server->ifname = strdup(ifname);
 | 
				
			||||||
 | 
						spa_hook_list_init(&server->listener_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server->debug_messages = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((res = setup_socket(server)) < 0)
 | 
				
			||||||
 | 
							goto error_free;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						avbtp_adp_register(server);
 | 
				
			||||||
 | 
						avbtp_maap_register(server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					error_free:
 | 
				
			||||||
 | 
						free(server);
 | 
				
			||||||
 | 
						if (res < 0)
 | 
				
			||||||
 | 
							errno = -res;
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void avdecc_server_add_listener(struct server *server,
 | 
				
			||||||
 | 
								   struct spa_hook *listener,
 | 
				
			||||||
 | 
								   const struct server_events *events,
 | 
				
			||||||
 | 
								   void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						spa_hook_list_append(&server->listener_list, listener, events, data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void avdecc_server_free(struct server *server)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl = server->impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_list_remove(&server->link);
 | 
				
			||||||
 | 
						if (server->source)
 | 
				
			||||||
 | 
							pw_loop_destroy_source(impl->loop, server->source);
 | 
				
			||||||
 | 
						if (server->timer)
 | 
				
			||||||
 | 
							pw_loop_destroy_source(impl->loop, server->source);
 | 
				
			||||||
 | 
						spa_hook_list_clean(&server->listener_list);
 | 
				
			||||||
 | 
						free(server);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										83
									
								
								src/modules/module-avbtp/internal.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/modules/module-avbtp/internal.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,83 @@
 | 
				
			||||||
 | 
					/* PipeWire
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Wim Taymans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef AVB_INTERNAL_H
 | 
				
			||||||
 | 
					#define AVB_INTERNAL_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pipewire/pipewire.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct impl {
 | 
				
			||||||
 | 
						struct pw_loop *loop;
 | 
				
			||||||
 | 
						struct pw_context *context;
 | 
				
			||||||
 | 
						struct spa_hook context_listener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct pw_properties *props;
 | 
				
			||||||
 | 
						struct pw_work_queue *work_queue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct spa_list servers;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct server_events {
 | 
				
			||||||
 | 
					#define AVBTP_VERSION_SERVER_EVENTS	0
 | 
				
			||||||
 | 
						uint32_t version;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** the server is destroyed */
 | 
				
			||||||
 | 
						void (*destroy) (void *data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int (*message) (void *data, uint64_t now, const void *message, int len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void (*periodic) (void *data, uint64_t now);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int (*command) (void *data, const char *command, const char *args);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct server {
 | 
				
			||||||
 | 
						struct spa_list link;
 | 
				
			||||||
 | 
						struct impl *impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						char *ifname;
 | 
				
			||||||
 | 
						struct spa_source *source;
 | 
				
			||||||
 | 
						char mac_addr[6];
 | 
				
			||||||
 | 
						struct spa_source *timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct spa_hook_list listener_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned debug_messages:1;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct server *avdecc_server_new(struct impl *impl, const char *ifname, struct spa_dict *props);
 | 
				
			||||||
 | 
					void avdecc_server_free(struct server *server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void avdecc_server_add_listener(struct server *server, struct spa_hook *listener,
 | 
				
			||||||
 | 
							const struct server_events *events, void *data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}  /* extern "C" */
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* AVB_INTERNAL_H */
 | 
				
			||||||
							
								
								
									
										108
									
								
								src/modules/module-avbtp/maap.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/modules/module-avbtp/maap.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,108 @@
 | 
				
			||||||
 | 
					/* AVB support
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Wim Taymans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pipewire/pipewire.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "maap.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct maap {
 | 
				
			||||||
 | 
						struct server *server;
 | 
				
			||||||
 | 
						struct spa_hook server_listener;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *message_type_as_string(uint8_t message_type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (message_type) {
 | 
				
			||||||
 | 
						case AVBTP_MAAP_MESSAGE_TYPE_PROBE:
 | 
				
			||||||
 | 
							return "PROBE";
 | 
				
			||||||
 | 
						case AVBTP_MAAP_MESSAGE_TYPE_DEFEND:
 | 
				
			||||||
 | 
							return "DEFEND";
 | 
				
			||||||
 | 
						case AVBTP_MAAP_MESSAGE_TYPE_ANNOUNCE:
 | 
				
			||||||
 | 
							return "ANNOUNCE";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "INVALID";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void maap_message_debug(struct maap *maap, const struct avbtp_packet_maap *p)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint32_t v;
 | 
				
			||||||
 | 
						const uint8_t *addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v = AVBTP_PACKET_MAAP_GET_MESSAGE_TYPE(p);
 | 
				
			||||||
 | 
						pw_log_info("message-type: %d (%s)", v, message_type_as_string(v));
 | 
				
			||||||
 | 
						pw_log_info("  maap-version: %d", AVBTP_PACKET_MAAP_GET_MAAP_VERSION(p));
 | 
				
			||||||
 | 
						pw_log_info("  length: %d", AVBTP_PACKET_MAAP_GET_LENGTH(p));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_log_info("  stream-id: 0x%"PRIx64, AVBTP_PACKET_MAAP_GET_STREAM_ID(p));
 | 
				
			||||||
 | 
						addr = AVBTP_PACKET_MAAP_GET_REQUEST_START(p);
 | 
				
			||||||
 | 
						pw_log_info("  request-start: %02x:%02x:%02x:%02x:%02x:%02x",
 | 
				
			||||||
 | 
								addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
 | 
				
			||||||
 | 
						pw_log_info("  request-count: %d", AVBTP_PACKET_MAAP_GET_REQUEST_COUNT(p));
 | 
				
			||||||
 | 
						addr = AVBTP_PACKET_MAAP_GET_CONFLICT_START(p);
 | 
				
			||||||
 | 
						pw_log_info("  conflict-start: %02x:%02x:%02x:%02x:%02x:%02x",
 | 
				
			||||||
 | 
								addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
 | 
				
			||||||
 | 
						pw_log_info("  conflict-count: %d", AVBTP_PACKET_MAAP_GET_CONFLICT_COUNT(p));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int maap_message(void *data, uint64_t now, const void *message, int len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct maap *maap = data;
 | 
				
			||||||
 | 
						const struct avbtp_packet_maap *p = message;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (AVBTP_PACKET_GET_SUBTYPE(p) != AVBTP_SUBTYPE_MAAP)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (maap->server->debug_messages)
 | 
				
			||||||
 | 
							maap_message_debug(maap, p);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void maap_destroy(void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct maap *maap = data;
 | 
				
			||||||
 | 
						spa_hook_remove(&maap->server_listener);
 | 
				
			||||||
 | 
						free(maap);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct server_events server_events = {
 | 
				
			||||||
 | 
						AVBTP_VERSION_SERVER_EVENTS,
 | 
				
			||||||
 | 
						.destroy = maap_destroy,
 | 
				
			||||||
 | 
						.message = maap_message
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int avbtp_maap_register(struct server *server)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct maap *maap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						maap = calloc(1, sizeof(*maap));
 | 
				
			||||||
 | 
						if (maap == NULL)
 | 
				
			||||||
 | 
							return -errno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						maap->server = server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						avdecc_server_add_listener(server, &maap->server_listener, &server_events, maap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										86
									
								
								src/modules/module-avbtp/maap.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/modules/module-avbtp/maap.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,86 @@
 | 
				
			||||||
 | 
					/* AVB support
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Wim Taymans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef AVBTP_MAAP_H
 | 
				
			||||||
 | 
					#define AVBTP_MAAP_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "packets.h"
 | 
				
			||||||
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_MAAP_MESSAGE_TYPE_PROBE			1
 | 
				
			||||||
 | 
					#define AVBTP_MAAP_MESSAGE_TYPE_DEFEND			2
 | 
				
			||||||
 | 
					#define AVBTP_MAAP_MESSAGE_TYPE_ANNOUNCE		3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct avbtp_packet_maap {
 | 
				
			||||||
 | 
						uint8_t subtype;
 | 
				
			||||||
 | 
					#if __BYTE_ORDER == __BIG_ENDIAN
 | 
				
			||||||
 | 
						unsigned sv:1;
 | 
				
			||||||
 | 
						unsigned version:3;
 | 
				
			||||||
 | 
						unsigned message_type:4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned maap_version:5;
 | 
				
			||||||
 | 
						unsigned len1:3;
 | 
				
			||||||
 | 
					#elif __BYTE_ORDER == __LITTLE_ENDIAN
 | 
				
			||||||
 | 
						unsigned message_type:4;
 | 
				
			||||||
 | 
						unsigned version:3;
 | 
				
			||||||
 | 
						unsigned sv:1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned len1:3;
 | 
				
			||||||
 | 
						unsigned maap_version:5;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						uint8_t len2:8;
 | 
				
			||||||
 | 
						uint64_t stream_id;
 | 
				
			||||||
 | 
						uint8_t request_start[6];
 | 
				
			||||||
 | 
						uint16_t request_count;
 | 
				
			||||||
 | 
						uint8_t conflict_start[6];
 | 
				
			||||||
 | 
						uint16_t conflict_count;
 | 
				
			||||||
 | 
					} __attribute__ ((__packed__));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_SET_SUBTYPE(p,v)		((p)->subtype = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_SET_SV(p,v)			((p)->sv = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_SET_VERSION(p,v)		((p)->version = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_SET_MESSAGE_TYPE(p,v)		((p)->message_type = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_SET_MAAP_VERSION(p,v)		((p)->maap_version = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_SET_LENGTH(p,v)		((p)->len1 = ((v) >> 8),(p)->len2 = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_SET_STREAM_ID(p,v)		((p)->stream_id = htobe64(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_SET_REQUEST_START(p,v)	memcpy((p)->request_start, (v), 6)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_SET_REQUEST_COUNT(p,v)	((p)->request_count = htons(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_SET_CONFLICT_START(p,v)	memcpy((p)->conflict_start, (v), 6)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_SET_CONFLICT_COUNT(p,v)	((p)->conflict_count = htons(v))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_GET_SUBTYPE(p)		((p)->subtype)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_GET_SV(p)			((p)->sv)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_GET_VERSION(p)		((p)->version)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_GET_MESSAGE_TYPE(p)		((p)->message_type)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_GET_MAAP_VERSION(p)		((p)->maap_version)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_GET_LENGTH(p)			(((p)->len1 << 8) | (p)->len2)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_GET_STREAM_ID(p)		be64toh((p)->stream_id)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_GET_REQUEST_START(p)		((p)->request_start)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_GET_REQUEST_COUNT(p)		ntohs((p)->request_count)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_GET_CONFLICT_START(p)		((p)->conflict_start)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_MAAP_GET_CONFLICT_COUNT(p)		ntohs((p)->conflict_count)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int avbtp_maap_register(struct server *server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* AVBTP_MAAP_H */
 | 
				
			||||||
							
								
								
									
										115
									
								
								src/modules/module-avbtp/packets.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/modules/module-avbtp/packets.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,115 @@
 | 
				
			||||||
 | 
					/* Spa AVB support
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Wim Taymans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef AVBTP_PACKETS_H
 | 
				
			||||||
 | 
					#define AVBTP_PACKETS_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <arpa/inet.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_61883_IIDC	0x00
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_MMA_STREAM	0x01
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_AAF		0x02
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_CVF		0x03
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_CRF		0x04
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_TSCF		0x05
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_SVF		0x06
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_RVF		0x07
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_AEF_CONTINUOUS	0x6E
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_VSF_STREAM	0x6F
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_EF_STREAM		0x7F
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_NTSCF		0x82
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_ESCF		0xEC
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_EECF		0xED
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_AEF_DISCRETE	0xEE
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_ADP		0xFA
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_AECP		0xFB
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_ACMP		0xFC
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_MAAP		0xFE
 | 
				
			||||||
 | 
					#define AVBTP_SUBTYPE_EF_CONTROL	0xFF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct avbtp_packet_common {
 | 
				
			||||||
 | 
						uint8_t subtype;
 | 
				
			||||||
 | 
					#if __BYTE_ORDER == __BIG_ENDIAN
 | 
				
			||||||
 | 
						unsigned sv:1;			/* stream_id valid */
 | 
				
			||||||
 | 
						unsigned version:3;
 | 
				
			||||||
 | 
						unsigned subtype_data1:4;
 | 
				
			||||||
 | 
					#elif __BYTE_ORDER == __LITTLE_ENDIAN
 | 
				
			||||||
 | 
						unsigned subtype_data1:4;
 | 
				
			||||||
 | 
						unsigned version:3;
 | 
				
			||||||
 | 
						unsigned sv:1;
 | 
				
			||||||
 | 
					#elif
 | 
				
			||||||
 | 
					#error "Unknown byte order"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						uint16_t subtype_data2;
 | 
				
			||||||
 | 
						uint64_t stream_id;
 | 
				
			||||||
 | 
						uint8_t payload[0];
 | 
				
			||||||
 | 
					} __attribute__ ((__packed__));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_SET_SUBTYPE(p,v)	((p)->subtype = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_SET_SV(p,v)	((p)->sv = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_SET_VERSION(p,v)	((p)->version = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_SET_STREAM_ID(p,v)	((p)->stream_id = htobe64(v))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_GET_SUBTYPE(p)	((p)->subtype)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_GET_SV(p)		((p)->sv)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_GET_VERSION(p)	((p)->version)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_GET_STREAM_ID(p)	be64toh((p)->stream_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct avbtp_packet_cc {
 | 
				
			||||||
 | 
						uint8_t subtype;
 | 
				
			||||||
 | 
					#if __BYTE_ORDER == __BIG_ENDIAN
 | 
				
			||||||
 | 
						unsigned sv:1;
 | 
				
			||||||
 | 
						unsigned version:3;
 | 
				
			||||||
 | 
						unsigned control_data1:4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned status:5;
 | 
				
			||||||
 | 
						unsigned len1:3;
 | 
				
			||||||
 | 
					#elif __BYTE_ORDER == __LITTLE_ENDIAN
 | 
				
			||||||
 | 
						unsigned control_data1:4;
 | 
				
			||||||
 | 
						unsigned version:3;
 | 
				
			||||||
 | 
						unsigned sv:1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned len1:3;
 | 
				
			||||||
 | 
						unsigned status:5;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						uint8_t len2:8;
 | 
				
			||||||
 | 
						uint64_t stream_id;
 | 
				
			||||||
 | 
						uint8_t payload[0];
 | 
				
			||||||
 | 
					} __attribute__ ((__packed__));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_CC_SET_SUBTYPE(p,v)	((p)->subtype = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_CC_SET_SV(p,v)		((p)->sv = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_CC_SET_VERSION(p,v)	((p)->version = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_CC_SET_STREAM_ID(p,v)	((p)->stream_id = htobe64(v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_CC_SET_STATUS(p,v)		((p)->status = (v))
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_CC_SET_LENGTH(p,v)		((p)->len1 = ((v) >> 8),(p)->len2 = (v))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_CC_GET_SUBTYPE(p)		((p)->subtype)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_CC_GET_SV(p)		((p)->sv)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_CC_GET_VERSION(p)		((p)->version)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_CC_GET_STREAM_ID(p)	be64toh((p)->stream_id)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_CC_GET_STATUS(p)		((p)->status)
 | 
				
			||||||
 | 
					#define AVBTP_PACKET_CC_GET_LENGTH(p)		((p)->len1 << 8 || (p)->len2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* AVBTP_PACKETS_H */
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue