module-avb: milan: introducing GET_DYNAMIC_INFO

This commit is contained in:
hackerman-kl 2026-04-18 10:28:53 +02:00 committed by Wim Taymans
parent 42d51098ae
commit d46523e6ad
5 changed files with 242 additions and 0 deletions

View file

@ -806,6 +806,7 @@ if build_module_avb
'module-avb/aecp-aem-cmds-resps/cmd-get-set-configuration.c',
'module-avb/aecp-aem-cmds-resps/cmd-lock-entity.c',
'module-avb/aecp-aem-cmds-resps/cmd-register-unsolicited-notifications.c',
'module-avb/aecp-aem-cmds-resps/cmd-get-dynamic-info.c',
'module-avb/aecp-aem-cmds-resps/reply-unsol-helpers.c',
'module-avb/es-builder.c',
'module-avb/avdecc.c',

View file

@ -0,0 +1,187 @@
/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki */
/* SPDX-License-Identifier: MIT */
#include "../aecp.h"
#include "../aecp-aem.h"
#include "../aecp-aem-descriptors.h"
#include "../aecp-aem-state.h"
#include "../internal.h"
#include "cmd-get-dynamic-info.h"
#include "cmd-resp-helpers.h"
/**
* \see IEEE 1722.1-2021 Section 7.4.76 GET_DYNAMIC_INFO
* \see Milan v1.2 Section 5.4.2.29
*
* Returns the current dynamic state for all descriptors in the requested
* configuration. Each descriptor type contributes a fixed-size record;
* descriptor types with no mutable runtime state are skipped.
*/
int handle_cmd_get_dynamic_info_milan_v12(struct aecp *aecp, int64_t now,
const void *m, int len)
{
uint8_t buf[AVB_PACKET_MILAN_DEFAULT_MTU + sizeof(struct avb_ethernet_header)];
struct server *server = aecp->server;
const struct avb_ethernet_header *h_in = m;
const struct avb_packet_aecp_aem *p_in = SPA_PTROFF(h_in, sizeof(*h_in), void);
const struct avb_packet_aecp_aem_get_dynamic_info *cmd =
(const struct avb_packet_aecp_aem_get_dynamic_info *)p_in->payload;
struct avb_ethernet_header *h;
struct avb_packet_aecp_aem *reply;
struct avb_packet_aecp_aem_get_dynamic_info *resp_hdr;
uint16_t config_idx;
const struct descriptor *entity_desc;
const struct avb_aem_desc_entity *entity;
const struct descriptor *d;
size_t psize, size;
uint8_t *ptr;
/*
* Milan v1.2 Section 5.4: AECP PDUs shall not exceed the interface MTU.
* Reject anything that would overflow our response buffer before
* touching it.
*/
if ((size_t)len > sizeof(buf)) {
pw_log_warn("%s: command PDU exceeds MTU (%d bytes)", __func__, len);
return reply_bad_arguments(aecp, m, len);
}
config_idx = ntohs(cmd->configuration_index);
entity_desc = server_find_descriptor(server, AVB_AEM_DESC_ENTITY, 0);
if (entity_desc == NULL)
return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len);
entity = (const struct avb_aem_desc_entity *)entity_desc->ptr;
if (config_idx >= ntohs(entity->configurations_count))
return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len);
memcpy(buf, m, len);
h = (struct avb_ethernet_header *)buf;
reply = SPA_PTROFF(h, sizeof(*h), void);
AVB_PACKET_AECP_SET_MESSAGE_TYPE(&reply->aecp, AVB_AECP_MESSAGE_TYPE_AEM_RESPONSE);
AVB_PACKET_AECP_SET_STATUS(&reply->aecp, AVB_AECP_AEM_STATUS_SUCCESS);
resp_hdr = (struct avb_packet_aecp_aem_get_dynamic_info *)reply->payload;
resp_hdr->configuration_index = htons(config_idx);
resp_hdr->reserved = 0;
psize = sizeof(*resp_hdr);
size = sizeof(*h) + sizeof(*reply) + psize;
ptr = buf + size;
spa_list_for_each(d, &server->descriptors, link) {
switch (d->type) {
case AVB_AEM_DESC_ENTITY: {
const struct avb_aem_desc_entity *e =
(const struct avb_aem_desc_entity *)d->ptr;
struct avb_aem_dynamic_info_entity rec;
if (size + sizeof(rec) > sizeof(buf)) {
pw_log_warn("%s: buffer full, truncating response", __func__);
goto done;
}
rec.hdr.descriptor_type = htons(d->type);
rec.hdr.descriptor_index = htons(d->index);
rec.current_configuration = e->current_configuration;
rec.reserved = 0;
memcpy(ptr, &rec, sizeof(rec));
ptr += sizeof(rec);
psize += sizeof(rec);
size += sizeof(rec);
break;
}
case AVB_AEM_DESC_AUDIO_UNIT: {
const struct avb_aem_desc_audio_unit *au =
(const struct avb_aem_desc_audio_unit *)d->ptr;
struct avb_aem_dynamic_info_audio_unit rec;
if (size + sizeof(rec) > sizeof(buf)) {
pw_log_warn("%s: buffer full, truncating response", __func__);
goto done;
}
rec.hdr.descriptor_type = htons(d->type);
rec.hdr.descriptor_index = htons(d->index);
rec.current_sampling_rate = au->current_sampling_rate.pull_frequency;
memcpy(ptr, &rec, sizeof(rec));
ptr += sizeof(rec);
psize += sizeof(rec);
size += sizeof(rec);
break;
}
case AVB_AEM_DESC_STREAM_INPUT: {
const struct aecp_aem_stream_input_state *state =
(const struct aecp_aem_stream_input_state *)d->ptr;
struct avb_aem_dynamic_info_stream rec;
if (size + sizeof(rec) > sizeof(buf)) {
pw_log_warn("%s: buffer full, truncating response", __func__);
goto done;
}
memset(&rec, 0, sizeof(rec));
rec.hdr.descriptor_type = htons(d->type);
rec.hdr.descriptor_index = htons(d->index);
rec.stream_id = 0;
rec.stream_format = state->desc.current_format;
if (state->desc.current_format != 0)
rec.stream_info_flags =
htonl(AVB_AEM_STREAM_INFO_FLAG_STREAM_FORMAT_VALID);
memcpy(ptr, &rec, sizeof(rec));
ptr += sizeof(rec);
psize += sizeof(rec);
size += sizeof(rec);
break;
}
case AVB_AEM_DESC_STREAM_OUTPUT: {
const struct aecp_aem_stream_output_state *state =
(const struct aecp_aem_stream_output_state *)d->ptr;
struct avb_aem_dynamic_info_stream rec;
if (size + sizeof(rec) > sizeof(buf)) {
pw_log_warn("%s: buffer full, truncating response", __func__);
goto done;
}
memset(&rec, 0, sizeof(rec));
rec.hdr.descriptor_type = htons(d->type);
rec.hdr.descriptor_index = htons(d->index);
rec.stream_id = 0;
rec.stream_format = state->desc.current_format;
if (state->desc.current_format != 0)
rec.stream_info_flags =
htonl(AVB_AEM_STREAM_INFO_FLAG_STREAM_FORMAT_VALID);
memcpy(ptr, &rec, sizeof(rec));
ptr += sizeof(rec);
psize += sizeof(rec);
size += sizeof(rec);
break;
}
case AVB_AEM_DESC_CLOCK_DOMAIN: {
const struct avb_aem_desc_clock_domain *cd =
(const struct avb_aem_desc_clock_domain *)d->ptr;
struct avb_aem_dynamic_info_clock_domain rec;
if (size + sizeof(rec) > sizeof(buf)) {
pw_log_warn("%s: buffer full, truncating response", __func__);
goto done;
}
rec.hdr.descriptor_type = htons(d->type);
rec.hdr.descriptor_index = htons(d->index);
rec.clock_source_index = cd->clock_source_index;
rec.reserved = 0;
memcpy(ptr, &rec, sizeof(rec));
ptr += sizeof(rec);
psize += sizeof(rec);
size += sizeof(rec);
break;
}
default:
break;
}
}
done:
AVB_PACKET_SET_LENGTH(&reply->aecp.hdr, psize + AVB_PACKET_CONTROL_DATA_OFFSET);
return avb_server_send_packet(server, h->src, AVB_TSN_ETH, buf, size);
}

View file

@ -0,0 +1,12 @@
/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki */
/* SPDX-License-Identifier: MIT */
#ifndef __AVB_AECP_AEM_CMD_GET_DYNAMIC_INFO_H__
#define __AVB_AECP_AEM_CMD_GET_DYNAMIC_INFO_H__
#include "../aecp-aem.h"
int handle_cmd_get_dynamic_info_milan_v12(struct aecp *aecp, int64_t now,
const void *m, int len);
#endif /* __AVB_AECP_AEM_CMD_GET_DYNAMIC_INFO_H__ */

View file

@ -21,6 +21,7 @@
#include "aecp-aem-cmds-resps/cmd-get-set-stream-format.h"
#include "aecp-aem-cmds-resps/cmd-get-set-clock-source.h"
#include "aecp-aem-cmds-resps/cmd-lock-entity.h"
#include "aecp-aem-cmds-resps/cmd-get-dynamic-info.h"
/* ACQUIRE_ENTITY */
@ -364,6 +365,9 @@ static const struct cmd_info cmd_info_milan_v12[] = {
AECP_AEM_HANDLE_CMD(AVB_AECP_AEM_CMD_GET_SAMPLING_RATE, true,
handle_cmd_get_sampling_rate_common),
AECP_AEM_HANDLE_CMD(AVB_AECP_AEM_CMD_GET_DYNAMIC_INFO, true,
handle_cmd_get_dynamic_info_milan_v12),
};
static const struct {

View file

@ -226,6 +226,44 @@ struct avb_packet_aecp_aem_operation_status {
uint16_t percent_complete;
} __attribute__ ((__packed__));
/* GET_DYNAMIC_INFO (IEEE 1722.1-2021 Section 7.4.76, Milan v1.2 Section 5.4.2.29) */
struct avb_packet_aecp_aem_get_dynamic_info {
uint16_t configuration_index;
uint16_t reserved;
} __attribute__ ((__packed__));
struct avb_aem_dynamic_info_hdr {
uint16_t descriptor_type;
uint16_t descriptor_index;
} __attribute__ ((__packed__));
struct avb_aem_dynamic_info_entity {
struct avb_aem_dynamic_info_hdr hdr;
uint16_t current_configuration;
uint16_t reserved;
} __attribute__ ((__packed__));
struct avb_aem_dynamic_info_audio_unit {
struct avb_aem_dynamic_info_hdr hdr;
uint32_t current_sampling_rate;
} __attribute__ ((__packed__));
struct avb_aem_dynamic_info_stream {
struct avb_aem_dynamic_info_hdr hdr;
uint64_t stream_id;
uint64_t stream_format;
uint32_t stream_info_flags;
uint16_t acmp_connection_count;
uint8_t flags_ex;
uint8_t pbsta;
} __attribute__ ((__packed__));
struct avb_aem_dynamic_info_clock_domain {
struct avb_aem_dynamic_info_hdr hdr;
uint16_t clock_source_index;
uint16_t reserved;
} __attribute__ ((__packed__));
struct avb_packet_aecp_aem {
struct avb_packet_aecp_header aecp;
#if __BYTE_ORDER == __BIG_ENDIAN