mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-04-30 06:46:49 -04:00
module-avb: milan: introducing GET_DYNAMIC_INFO
This commit is contained in:
parent
42d51098ae
commit
d46523e6ad
5 changed files with 242 additions and 0 deletions
|
|
@ -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-get-set-configuration.c',
|
||||||
'module-avb/aecp-aem-cmds-resps/cmd-lock-entity.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-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/aecp-aem-cmds-resps/reply-unsol-helpers.c',
|
||||||
'module-avb/es-builder.c',
|
'module-avb/es-builder.c',
|
||||||
'module-avb/avdecc.c',
|
'module-avb/avdecc.c',
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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__ */
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
#include "aecp-aem-cmds-resps/cmd-get-set-stream-format.h"
|
#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-get-set-clock-source.h"
|
||||||
#include "aecp-aem-cmds-resps/cmd-lock-entity.h"
|
#include "aecp-aem-cmds-resps/cmd-lock-entity.h"
|
||||||
|
#include "aecp-aem-cmds-resps/cmd-get-dynamic-info.h"
|
||||||
|
|
||||||
|
|
||||||
/* ACQUIRE_ENTITY */
|
/* 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,
|
AECP_AEM_HANDLE_CMD(AVB_AECP_AEM_CMD_GET_SAMPLING_RATE, true,
|
||||||
handle_cmd_get_sampling_rate_common),
|
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 {
|
static const struct {
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,44 @@ struct avb_packet_aecp_aem_operation_status {
|
||||||
uint16_t percent_complete;
|
uint16_t percent_complete;
|
||||||
} __attribute__ ((__packed__));
|
} __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_aem {
|
||||||
struct avb_packet_aecp_header aecp;
|
struct avb_packet_aecp_header aecp;
|
||||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue