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
|
|
@ -5,6 +5,7 @@ subdir('spa')
|
|||
module_sources = [
|
||||
'module-access.c',
|
||||
'module-adapter.c',
|
||||
'module-avbtp.c',
|
||||
'module-client-device.c',
|
||||
'module-client-node.c',
|
||||
'module-echo-cancel.c',
|
||||
|
|
@ -516,3 +517,20 @@ pipewire_module_fallback_sink = shared_library('pipewire-module-fallback-sink',
|
|||
install_rpath: modules_install_dir,
|
||||
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