diff --git a/src/modules/meson.build b/src/modules/meson.build index dca7cc7dc..fb0dd47b5 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -739,6 +739,7 @@ if build_module_avb 'module-avb/aecp-aem-cmds-resps/cmd-available.c', 'module-avb/aecp-aem-cmds-resps/cmd-get-set-name.c', 'module-avb/aecp-aem-cmds-resps/cmd-get-set-clock-source.c', + 'module-avb/aecp-aem-cmds-resps/cmd-get-set-sampling-rate.c', 'module-avb/aecp-aem-cmds-resps/cmd-register-unsolicited-notifications.c', 'module-avb/aecp-aem-cmds-resps/cmd-deregister-unsolicited-notifications.c', 'module-avb/aecp-aem-cmds-resps/cmd-register-unsolicited-notifications.c', diff --git a/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-sampling-rate.c b/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-sampling-rate.c new file mode 100644 index 000000000..d7f305add --- /dev/null +++ b/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-sampling-rate.c @@ -0,0 +1,231 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki */ +/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp */ +/* SPDX-License-Identifier: MIT */ + +#include +#include +#include + +#include "../aecp.h" +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" +#include "../aecp-aem-descriptors.h" +#include "../aecp-aem-types.h" + +#include "cmd-resp-helpers.h" +#include "cmd-get-set-sampling-rate.h" + +#include "reply-unsol-helpers.h" + +/** + * \brief Verify if the audio unit supports the sample in the list it has + * \param const uint32_t samplerate: The sample rate command received + * \return true if the samplerate provide is supported by the audio unit + * false otherwise. + */ +static bool valid_sample_rate_audio_unit_compat(const struct avb_aem_desc_audio_unit *au, + const union avb_packet_aecp_aem_pull_frequency *pullfreq) +{ + uint32_t au_pull_freq; + // Descriptors are always are using the network endianees. + uint16_t au_sr_counts = ntohs(au->sampling_rates_count); + const union avb_aem_desc_sampling_rate *sr; + + /* TODO: it's necessary to handle the pull ?!!!*/ + for (uint32_t pos = 0; pos < au_sr_counts; pos++) { + sr = &au->sampling_rates[pos]; + au_pull_freq = ntohl(sr->pull_frequency); + + if (au_pull_freq == pullfreq->pull_frequency) { + return true; + } + } + + pw_log_error("Unsupported Audio Unit sample rate %d\n", + pullfreq->frequency); + + return false; +} + +/** + * \brief Verify if the samplerate provide is supported by Milan V1.2 + * \param const union pullfreq: union holding all the necessary information. + * the function expects the Host endianess. + * \return true if the samplerate provide is supported by Milan V1.2 + * false otherwise. + */ +static bool valid_sample_rate_milan_v12(const union avb_packet_aecp_aem_pull_frequency *pullfreq) +{ + static const uint32_t list_valid_samplerates[] = { + 192000, 96000, 48000 + }; + + static const size_t elmt = SPA_N_ELEMENTS(list_valid_samplerates); + + /* TODO: it's necessary to handle the pull ?!!!*/ + for (uint32_t pos = 0; pos < elmt; pos++) { + if (list_valid_samplerates[pos] == pullfreq->frequency) { + return true; + } + } + + pw_log_error("Unsupported sample rate for Milan V1.2 %d", + pullfreq->frequency); + + return false; +} + +static int send_unsol_get_sampling_rate_milan_v12(struct aecp *aecp, + const uint8_t *m, size_t len, uint64_t ctrler_id) +{ + uint8_t unsol_buf[512]; + struct aecp_aem_base_info info = { 0 }; + int rc = 0; + + memcpy(unsol_buf, m, len); + /* Prepare a template packet */ + info.controller_entity_id = htobe64(ctrler_id); + info.expire_timeout = INT64_MAX; + + rc = reply_unsolicited_notifications(aecp, &info, unsol_buf, len, false); + return rc; +} + +/** + * \brief Handling errors. The standard forces us to return the current value + * if the entity does not support the requested sampling rate for the audio + * unit. + */ +static int sample_rate_invalid_response(struct aecp *aecp, + const struct avb_aem_desc_audio_unit *unit, const uint8_t *m, size_t len) +{ + uint8_t buf[512]; + struct avb_ethernet_header *h = (struct avb_ethernet_header *) buf; + struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem_setget_sampling_rate *cmd; + + memcpy(buf, m, len); + cmd = (struct avb_packet_aecp_aem_setget_sampling_rate *)p->payload; + + memcpy(&cmd->sampling_rate, &unit->current_sampling_rate, + sizeof(unit->current_sampling_rate)); + + return reply_status(aecp, AVB_AECP_AEM_STATUS_NOT_SUPPORTED, p, len); +} + +/** + * \brief Milan sampling rate handles. it sets an internal sampling rate to + * the audio unit and not the sampling rate received from the network. + * \sa Milan V1.2 5.4.2.13 SET_SAMPLING_RATE + * \sa IEEE 1722.1-2021 7.4.21 SET_SAMPLING_RATE + */ +int handle_cmd_set_sampling_rate_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + const struct avb_packet_aecp_aem_setget_sampling_rate *cmd; + union avb_packet_aecp_aem_pull_frequency pullfreq; + struct descriptor *desc; + uint16_t desc_type, desc_id; + int rc; + + /* TODO: take care of the multiplier in the sampling rate. + * This function does not take care call the PULL + */ + cmd = (const struct avb_packet_aecp_aem_setget_sampling_rate *)p->payload; + desc_type = ntohs(cmd->descriptor_type); + desc_id = ntohs(cmd->descriptor_id); + + memcpy(&pullfreq, &cmd->sampling_rate, sizeof(pullfreq)); + pullfreq.pull_frequency = ntohl(pullfreq.pull_frequency); + + desc = server_find_descriptor(server, desc_type, desc_id); + if (desc == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + + if (desc_type == AVB_AEM_DESC_AUDIO_UNIT) { + struct avb_aem_desc_audio_unit *unit = desc->ptr; + /* TODO check if the STREAM_PORT associated with it supportes + * the SSRC/ASRC bit. + */ + + if (!valid_sample_rate_audio_unit_compat(unit, &pullfreq)) { + return sample_rate_invalid_response(aecp, unit, m, len); + } + + if (!valid_sample_rate_milan_v12(&pullfreq)) { + return sample_rate_invalid_response(aecp, unit, m, len); + } + + memcpy(&unit->current_sampling_rate, &cmd->sampling_rate, + sizeof(unit->current_sampling_rate)); + + } else { + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NOT_IMPLEMENTED, p, len); + } + + rc = reply_success(aecp, m, len); + if (rc) { + return rc; + } + + return send_unsol_get_sampling_rate_milan_v12(aecp, m, len, + p->aecp.controller_guid); +} + + /** + * \brief Setting the sample rate for the common AVB + * \sa IEEE 1722.1-2021 7.4.21 SET_SAMPLING_RATE + */ +int handle_cmd_get_sampling_rate_common(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + /** Choose to have ahte maximum power of 2 size that a buffer can hold */ + /** TODO: In the future this could be optimized to know if the payload + * response fits into the buffer */ + uint8_t buf[2048]; + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem *reply_p; + struct avb_ethernet_header *h_reply; + struct avb_packet_aecp_aem_setget_sampling_rate *reply_payload; + uint16_t desc_type, desc_id; + struct descriptor *desc; + union avb_packet_aecp_aem_pull_frequency pullfreq; + + const struct avb_packet_aecp_aem_setget_sampling_rate *cmd = + (const struct avb_packet_aecp_aem_setget_sampling_rate *)p->payload; + + desc_type = ntohs(cmd->descriptor_type); + desc_id = ntohs(cmd->descriptor_id); + + desc = server_find_descriptor(server, desc_type, desc_id); + if (desc == NULL) + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + + if (desc_type == AVB_AEM_DESC_AUDIO_UNIT) { + struct avb_aem_desc_audio_unit *unit = desc->ptr; + memcpy(&pullfreq, &unit->current_sampling_rate, sizeof(pullfreq)); + } else { + return reply_status(aecp, + AVB_AECP_AEM_STATUS_NOT_IMPLEMENTED, p, len); + } + + memcpy(buf, m, len); + h_reply = (struct avb_ethernet_header *)buf; + reply_p = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + reply_payload = + (struct avb_packet_aecp_aem_setget_sampling_rate *)reply_p->payload; + + memcpy(&reply_payload->sampling_rate, &pullfreq, sizeof(pullfreq)); + + return reply_success(aecp, buf, len); +} diff --git a/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-sampling-rate.h b/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-sampling-rate.h new file mode 100644 index 000000000..7745f95af --- /dev/null +++ b/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-sampling-rate.h @@ -0,0 +1,18 @@ +/* AVB support */ +/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_AECP_AEM_CMD_GET_SET_SAMPLING_RATE_H__ +#define __AVB_AECP_AEM_CMD_GET_SET_SAMPLING_RATE_H__ + + +#include + +int handle_cmd_set_sampling_rate_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len); + +int handle_cmd_get_sampling_rate_common(struct aecp *aecp, int64_t now, + const void *m, int len); + +#endif /* __AVB_AECP_AEM_CMD_GET_SET_SAMPLING_RATE_H__ */ diff --git a/src/modules/module-avb/aecp-aem.c b/src/modules/module-avb/aecp-aem.c index 07393df08..1918c18ad 100644 --- a/src/modules/module-avb/aecp-aem.c +++ b/src/modules/module-avb/aecp-aem.c @@ -11,6 +11,7 @@ /* The headers including the command and response of the system */ #include "aecp-aem-cmds-resps/cmd-available.h" #include "aecp-aem-cmds-resps/cmd-get-set-configuration.h" +#include "aecp-aem-cmds-resps/cmd-get-set-sampling-rate.h" #include "aecp-aem-cmds-resps/cmd-lock-entity.h" #include "aecp-aem-cmds-resps/cmd-register-unsolicited-notifications.h" #include "aecp-aem-cmds-resps/cmd-deregister-unsolicited-notifications.h" @@ -285,6 +286,9 @@ static const struct cmd_info cmd_info_avb_legacy[] = { AECP_AEM_HANDLE_CMD(AVB_AECP_AEM_CMD_READ_DESCRIPTOR, true, handle_read_descriptor_common), + 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_AVB_INFO, true, handle_get_avb_info_common), }; @@ -335,6 +339,12 @@ static const struct cmd_info cmd_info_milan_v12[] = { AECP_AEM_HANDLE_CMD(AVB_AECP_AEM_CMD_GET_CLOCK_SOURCE, true, handle_cmd_get_clock_source_milan_v12), + + AECP_AEM_HANDLE_CMD(AVB_AECP_AEM_CMD_SET_SAMPLING_RATE, false, + handle_cmd_set_sampling_rate_milan_v12), + + AECP_AEM_HANDLE_CMD(AVB_AECP_AEM_CMD_GET_SAMPLING_RATE, true, + handle_cmd_get_sampling_rate_common), }; static const struct {