pipewire/src/modules/module-avb/es-builder.c

200 lines
6 KiB
C
Raw Normal View History

/* PipeWire */
/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki <alexandre.malki@kebag-logic.com> */
/* SPDX-License-Identifier: MIT */
#include "es-builder.h"
#include "aecp-aem.h"
#include "aecp-aem-state.h"
#include "utils.h"
typedef struct descriptor *(*es_builder_cb_t) (struct server *server, uint16_t type,
uint16_t index, size_t size, const void *ptr);
struct es_builder_st {
es_builder_cb_t build_descriptor_cb;
};
static struct descriptor *es_builder_desc_entity_milan_v12(struct server *server,
uint16_t type, uint16_t index, size_t size, const void *ptr)
{
return server_add_descriptor(server, type, index,
sizeof(struct aecp_aem_entity_milan_state), size, ptr);
}
static struct descriptor *es_buidler_desc_stream_general_prepare(struct server *server,
uint16_t type, uint16_t index, size_t size, const void *ptr)
{
struct descriptor *desc;
struct stream *stream;
enum spa_direction direction;
uint64_t out_mtt_ns = 0;
if (type == AVB_AEM_DESC_STREAM_INPUT) {
struct aecp_aem_stream_input_state_milan_v12 *w;
const struct avb_aem_desc_stream *body = ptr;
desc = server_add_descriptor(server, type, index,
sizeof(*w), size, ptr);
if (!desc) {
pw_log_error("Allocation failed\n");
return NULL;
}
w = desc->ptr;
/* Milan v1.2 Section 5.3.8.7: started/stopped state defaults to started. */
w->stream_in_sta.started = true;
struct avb_aem_stream_format_info fi;
avb_aem_stream_format_decode(body->current_format, &fi);
if (fi.kind == AVB_AEM_STREAM_FORMAT_KIND_CRF)
return desc;
stream = &w->stream_in_sta.common.stream;
direction = SPA_DIRECTION_INPUT;
} else if (type == AVB_AEM_DESC_STREAM_OUTPUT) {
struct aecp_aem_stream_output_state_milan_v12 *w;
desc = server_add_descriptor(server, type, index,
sizeof(*w), size, ptr);
if (!desc) {
pw_log_error("Allocation failed\n");
return NULL;
}
w = desc->ptr;
/* Milan v1.2 Section 5.3.7.6: default presentation time offset is 2 ms. */
w->stream_out_sta.presentation_time_offset_ns = 2000000;
w->stream_out_sta.max_transit_time_ns = 2000000;
out_mtt_ns = w->stream_out_sta.max_transit_time_ns;
stream = &w->stream_out_sta.common.stream;
direction = SPA_DIRECTION_OUTPUT;
} else {
pw_log_error("Only STREAM_INPUT and STREAM_OUTPUT\n");
return NULL;
}
if (!server_create_stream(server, stream, direction, index)) {
pw_log_error("Could not create/initialize a stream");
return NULL;
}
if (direction == SPA_DIRECTION_OUTPUT)
stream->mtt = (int)out_mtt_ns;
return desc;
}
static struct descriptor *es_buidler_desc_avb_interface(struct server *server,
uint16_t type, uint16_t index, size_t size, const void *ptr)
{
struct aecp_aem_avb_interface_state *if_ptr;
struct descriptor *desc;
desc = server_add_descriptor(server, type, index,
sizeof(*if_ptr), size, ptr);
if (!desc) {
pw_log_error("Error durring allocation\n");
spa_assert(0);
}
if_ptr = desc->ptr;
/* Milan Section 5.4.2.25 / Table 5.13: seed LINK_UP=1 at startup. The interface
* is up by the time descriptors are built (we wouldn't have a working
* raw socket otherwise). Hive infers current link state from
* (LINK_UP > LINK_DOWN); without this it sees the link as down.
*
* Mark counters dirty so the next periodic emits an unsolicited
* GET_COUNTERS Milan Section 5.4.5 only emits on update, no periodic
* heartbeat. */
if_ptr->counters.link_up = 1;
if_ptr->counters_dirty = true;
avb_msrp_attribute_new(server->msrp, &if_ptr->domain_attr,
AVB_MSRP_ATTRIBUTE_TYPE_DOMAIN);
if_ptr->domain_attr.attr.domain.sr_class_id = AVB_MSRP_CLASS_ID_DEFAULT;
if_ptr->domain_attr.attr.domain.sr_class_priority = AVB_MSRP_PRIORITY_DEFAULT;
if_ptr->domain_attr.attr.domain.sr_class_vid = htons(AVB_DEFAULT_VLAN);
avb_mrp_attribute_begin(if_ptr->domain_attr.mrp, 0);
avb_mrp_attribute_join(if_ptr->domain_attr.mrp, 0, true);
return desc;
}
#define HELPER_ES_BUIDLER(type, callback) \
[type] = { .build_descriptor_cb = callback }
static const struct es_builder_st es_builder_milan_v12[AVB_AEM_DESC_LAST_RESERVED_17221 + 1] =
{
HELPER_ES_BUIDLER(AVB_AEM_DESC_ENTITY, es_builder_desc_entity_milan_v12),
HELPER_ES_BUIDLER(AVB_AEM_DESC_STREAM_OUTPUT, es_buidler_desc_stream_general_prepare),
HELPER_ES_BUIDLER(AVB_AEM_DESC_STREAM_INPUT, es_buidler_desc_stream_general_prepare),
HELPER_ES_BUIDLER(AVB_AEM_DESC_AVB_INTERFACE, es_buidler_desc_avb_interface),
};
static const struct es_builder_st es_builder_legacy_avb[AVB_AEM_DESC_LAST_RESERVED_17221 + 1] =
{
HELPER_ES_BUIDLER(AVB_AEM_DESC_STREAM_OUTPUT, es_buidler_desc_stream_general_prepare),
HELPER_ES_BUIDLER(AVB_AEM_DESC_STREAM_INPUT, es_buidler_desc_stream_general_prepare),
};
static const struct {
const struct es_builder_st *es_builder;
size_t count;
} es_builders[] = {
[AVB_MODE_LEGACY] = {
.es_builder = es_builder_legacy_avb,
.count = SPA_N_ELEMENTS(es_builder_legacy_avb),
},
[AVB_MODE_MILAN_V12] = {
.es_builder = es_builder_milan_v12,
.count = SPA_N_ELEMENTS(es_builder_milan_v12),
},
};
void es_builder_add_descriptor(struct server *server, uint16_t type,
uint16_t index, size_t size, void *ptr_aem)
{
const struct es_builder_st *es_builder;
enum avb_mode avb_mode;
bool std_processing = false;
if (!server) {
pw_log_error("Invalid server, it is empty %p\n", server);
spa_assert(0);
}
avb_mode = server->avb_mode;
if (avb_mode >= AVB_MODE_MAX) {
pw_log_error("AVB mode is not valid received %d\n", avb_mode);
spa_assert(0);
}
es_builder = es_builders[avb_mode].es_builder;
if (type >= es_builders[avb_mode].count) {
std_processing = true;
} else if (!es_builder[type].build_descriptor_cb) {
std_processing = true;
}
if (std_processing) {
if (!server_add_descriptor(server, type, index, 0, size, ptr_aem)) {
pw_log_error("Could not allocate descriptor %u at "
"index %u the avb aem type\n", type, index);
spa_assert(0);
}
} else {
if (!es_builder[type].build_descriptor_cb(server, type,
index, size, ptr_aem)) {
pw_log_error("Could not allocate specific descriptr "
"%u at index %u the avb aem type\n",
type, index);
spa_assert(0);
}
}
}