From a16f3d704ebdfda681cb9f6825cb24e87dad04af Mon Sep 17 00:00:00 2001 From: hackerman-kl Date: Sun, 26 Apr 2026 21:04:21 +0200 Subject: [PATCH] milan-avb: aecp-vendor-unique-milan-v12: dispatch via per-cmd table per Milan v1.2 Section 5.4.4 --- src/modules/meson.build | 1 + .../cmd-get-milan-info.c | 84 +++++++++++ .../cmd-get-milan-info.h | 12 ++ .../cmd-resp-helpers.h | 69 +++++++++ .../module-avb/aecp-vendor-unique-milan-v12.c | 131 ++++++------------ .../module-avb/aecp-vendor-unique-milan-v12.h | 26 +--- src/tools/meson.build | 1 + test/meson.build | 1 + 8 files changed, 217 insertions(+), 108 deletions(-) create mode 100644 src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-get-milan-info.c create mode 100644 src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-get-milan-info.h create mode 100644 src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-resp-helpers.h diff --git a/src/modules/meson.build b/src/modules/meson.build index 203ef09a5..242b9fad3 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -797,6 +797,7 @@ if build_module_avb 'module-avb/aecp.c', 'module-avb/aecp-aem.c', 'module-avb/aecp-vendor-unique-milan-v12.c', + 'module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-get-milan-info.c', 'module-avb/aecp-aem-cmds-resps/cmd-available.c', 'module-avb/aecp-aem-cmds-resps/cmd-get-set-control.c', 'module-avb/aecp-aem-cmds-resps/cmd-get-set-name.c', diff --git a/src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-get-milan-info.c b/src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-get-milan-info.c new file mode 100644 index 000000000..a665f43e2 --- /dev/null +++ b/src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-get-milan-info.c @@ -0,0 +1,84 @@ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki */ +/* SPDX-License-Identifier: MIT */ + +#include +#include +#include + +#include +#include + +#include "../aecp.h" +#include "../aecp-vendor-unique-milan-v12.h" +#include "../internal.h" + +#include "cmd-get-milan-info.h" +#include "cmd-resp-helpers.h" + +/* Milan v1.2 Section 5.4.4.1 GET_MILAN_INFO Response payload. */ +struct avb_packet_aecp_mvu_get_milan_info_rsp { + uint32_t protocol_version; + uint32_t features_flags; + uint32_t certification_version; +} __attribute__ ((__packed__)); + +/* Milan v1.2 Section 5.4.4.1: protocol_version is 1 across every Milan release. */ +#define MILAN_MVU_PROTOCOL_VERSION 0x00000001u + +/* Milan v1.2 Section 5.4.4.1 Table 5.20: features_flags bit 0 = REDUNDANCY (not + * implemented), upper bits reserved. */ +#define MILAN_MVU_FEATURES_FLAGS 0x00000000u + +/* Milan v1.2 Section 5.4.4.1: certification_version byte-encoded + * major.minor.revision.build. Milan v1.2 = 0x01020000. */ +#define MILAN_MVU_CERTIFICATION_VERSION 0x01020000u + +int handle_cmd_mvu_get_milan_info_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len) +{ + struct server *server = aecp->server; + uint8_t buf[2048]; + struct avb_ethernet_header *h_reply; + struct avb_packet_aecp_header *aecp_hdr; + struct avb_packet_aecp_vendor_unique *vu_reply; + struct avb_packet_aecp_mvu_get_milan_info_rsp *body; + int total = (int)(sizeof(struct avb_ethernet_header) + + sizeof(struct avb_packet_aecp_vendor_unique) + + sizeof(struct avb_packet_aecp_mvu_get_milan_info_rsp)); + int wire = total < 60 ? 60 : total; + + (void)now; + + if (wire > (int)sizeof(buf)) + return -EMSGSIZE; + + memcpy(buf, m, len); + if (len < wire) + memset(buf + len, 0, wire - len); + + h_reply = (struct avb_ethernet_header *)buf; + aecp_hdr = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + vu_reply = (struct avb_packet_aecp_vendor_unique *)aecp_hdr; + + AVB_PACKET_AECP_SET_MESSAGE_TYPE(aecp_hdr, + AVB_AECP_MESSAGE_TYPE_VENDOR_UNIQUE_RESPONSE); + AVB_PACKET_AECP_SET_STATUS(aecp_hdr, AVB_AECP_STATUS_SUCCESS); + + /* IEEE 1722-2011 Section 5.3 / IEEE 1722.1-2021 Section 9.2.1.1.7: CDL excludes + * the 12-octet AVTPDU common header. */ + AVB_PACKET_SET_LENGTH(&aecp_hdr->hdr, + (uint16_t)(total - sizeof(struct avb_ethernet_header) - + sizeof(struct avb_packet_header) - + sizeof(uint64_t))); + + ((uint8_t *)&vu_reply->command_type)[0] &= 0x7f; + vu_reply->reserved = 0; + + body = (struct avb_packet_aecp_mvu_get_milan_info_rsp *)vu_reply->payload; + body->protocol_version = htonl(MILAN_MVU_PROTOCOL_VERSION); + body->features_flags = htonl(MILAN_MVU_FEATURES_FLAGS); + body->certification_version = htonl(MILAN_MVU_CERTIFICATION_VERSION); + + return avb_server_send_packet(server, h_reply->src, AVB_TSN_ETH, + buf, wire); +} diff --git a/src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-get-milan-info.h b/src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-get-milan-info.h new file mode 100644 index 000000000..aab9bb563 --- /dev/null +++ b/src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-get-milan-info.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_AECP_MVU_CMD_GET_MILAN_INFO_H__ +#define __AVB_AECP_MVU_CMD_GET_MILAN_INFO_H__ + +#include "../aecp-vendor-unique-milan-v12.h" + +int handle_cmd_mvu_get_milan_info_milan_v12(struct aecp *aecp, int64_t now, + const void *m, int len); + +#endif /* __AVB_AECP_MVU_CMD_GET_MILAN_INFO_H__ */ diff --git a/src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-resp-helpers.h b/src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-resp-helpers.h new file mode 100644 index 000000000..8cb4d16f8 --- /dev/null +++ b/src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-resp-helpers.h @@ -0,0 +1,69 @@ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki */ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AVB_AECP_MVU_HELPERS_H__ +#define __AVB_AECP_MVU_HELPERS_H__ + +#include +#include +#include + +#include +#include + +#include "../aecp.h" +#include "../aecp-vendor-unique-milan-v12.h" +#include "../internal.h" + +/* Milan v1.2 Section 5.4.3.2.2: r bit is reserved (must be 0) in commands and + * responses. Command vs response is signalled by AECP message_type. */ +static inline int reply_mvu_status(struct aecp *aecp, int status, + const void *m, int len) +{ + uint8_t buf[2048]; + struct server *server = aecp->server; + struct avb_ethernet_header *h = (void *)buf; + struct avb_packet_aecp_header *reply = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_vendor_unique *vu_reply; + int wire = len < 60 ? 60 : len; + + if (len < 0 || (size_t)wire > sizeof(buf)) { + pw_log_warn("reply_mvu_status: invalid len %d", len); + return -EINVAL; + } + + memcpy(buf, m, len); + if (wire > len) + memset(buf + len, 0, wire - len); + AVB_PACKET_AECP_SET_MESSAGE_TYPE(reply, + AVB_AECP_MESSAGE_TYPE_VENDOR_UNIQUE_RESPONSE); + AVB_PACKET_AECP_SET_STATUS(reply, status); + + if ((size_t)wire >= sizeof(*h) + sizeof(*vu_reply)) { + vu_reply = SPA_PTROFF(h, sizeof(*h), void); + ((uint8_t *)&vu_reply->command_type)[0] &= 0x7f; + } + + return avb_server_send_packet(server, h->src, AVB_TSN_ETH, buf, wire); +} + +static inline int reply_mvu_not_implemented(struct aecp *aecp, + const void *m, int len) +{ + return reply_mvu_status(aecp, AVB_AECP_STATUS_NOT_IMPLEMENTED, m, len); +} + +static inline int direct_reply_mvu_not_implemented(struct aecp *aecp, + int64_t now, const void *m, int len) +{ + (void)now; + return reply_mvu_not_implemented(aecp, m, len); +} + +static inline int reply_mvu_success(struct aecp *aecp, + const void *m, int len) +{ + return reply_mvu_status(aecp, AVB_AECP_STATUS_SUCCESS, m, len); +} + +#endif /* __AVB_AECP_MVU_HELPERS_H__ */ diff --git a/src/modules/module-avb/aecp-vendor-unique-milan-v12.c b/src/modules/module-avb/aecp-vendor-unique-milan-v12.c index 0c5b3bf34..3ef543fe7 100644 --- a/src/modules/module-avb/aecp-vendor-unique-milan-v12.c +++ b/src/modules/module-avb/aecp-vendor-unique-milan-v12.c @@ -1,8 +1,10 @@ -/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki */ +/* SPDX-FileCopyrightText: Copyright © 2025 Alexandre Malki */ /* SPDX-License-Identifier: MIT */ #include +#include #include +#include #include #include @@ -12,15 +14,27 @@ #include "aecp-vendor-unique-milan-v12.h" #include "internal.h" -/* Milan v1.2: protocol version this implementation supports. The high 16 - * bits encode the MVU protocol major version; bits 15..0 the minor. v1.0 - * of MVU = 0x00010000. Most controllers (Hive, etc.) accept anything ≥ - * 0x00010000 and don't gate features on this value. */ -#define MILAN_MVU_PROTOCOL_VERSION 0x00010000u +#include "aecp-vendor-unique-milan-v12-cmds-resps/cmd-resp-helpers.h" +#include "aecp-vendor-unique-milan-v12-cmds-resps/cmd-get-milan-info.h" -#define MILAN_MVU_FEATURES_FLAGS 0x00000000u +struct mvu_cmd_info { + int (*handle_command) (struct aecp *aecp, int64_t now, + const void *m, int len); +}; -#define MILAN_MVU_CERTIFICATION_VERSION 0x00000000u +#define MVU_HANDLE_CMD(cmd, handle_exec) \ + [cmd] = { \ + .handle_command = handle_exec, \ + } + +static const char * const mvu_cmd_names[] = { + [AVB_AECP_MVU_CMD_GET_MILAN_INFO] = "get-milan-info", +}; + +static const struct mvu_cmd_info mvu_cmd_info_milan_v12[] = { + MVU_HANDLE_CMD(AVB_AECP_MVU_CMD_GET_MILAN_INFO, + handle_cmd_mvu_get_milan_info_milan_v12), +}; static const uint8_t mvu_protocol_id[6] = { AVB_AECP_MVU_PROTOCOL_ID_0, @@ -31,83 +45,15 @@ static const uint8_t mvu_protocol_id[6] = { AVB_AECP_MVU_PROTOCOL_ID_5, }; -static int reply_mvu_status(struct aecp *aecp, uint8_t status, - const void *m, int len) -{ - struct server *server = aecp->server; - uint8_t buf[2048]; - struct avb_ethernet_header *h = (void *)buf; - struct avb_packet_aecp_header *aecp_hdr; - - if (len > (int)sizeof(buf)) - return -EMSGSIZE; - - memcpy(buf, m, len); - aecp_hdr = SPA_PTROFF(h, sizeof(*h), void); - - AVB_PACKET_AECP_SET_MESSAGE_TYPE(aecp_hdr, - AVB_AECP_MESSAGE_TYPE_VENDOR_UNIQUE_RESPONSE); - AVB_PACKET_AECP_SET_STATUS(aecp_hdr, status); - - return avb_server_send_packet(server, h->src, AVB_TSN_ETH, buf, len); -} - -static int handle_get_milan_info(struct aecp *aecp, const void *m, int len) -{ - struct server *server = aecp->server; - uint8_t buf[2048]; - struct avb_ethernet_header *h_reply; - struct avb_packet_aecp_header *aecp_hdr; - struct avb_packet_aecp_vendor_unique *vu_reply; - struct avb_packet_aecp_mvu_get_milan_info_rsp *body; - int total = (int)(sizeof(struct avb_ethernet_header) + - sizeof(struct avb_packet_aecp_vendor_unique) + - sizeof(struct avb_packet_aecp_mvu_get_milan_info_rsp)); - - if (total > (int)sizeof(buf)) - return -EMSGSIZE; - - memcpy(buf, m, len); - if (len < total) - memset(buf + len, 0, total - len); - - h_reply = (struct avb_ethernet_header *)buf; - aecp_hdr = SPA_PTROFF(h_reply, sizeof(*h_reply), void); - vu_reply = (struct avb_packet_aecp_vendor_unique *)aecp_hdr; - - AVB_PACKET_AECP_SET_MESSAGE_TYPE(aecp_hdr, - AVB_AECP_MESSAGE_TYPE_VENDOR_UNIQUE_RESPONSE); - AVB_PACKET_AECP_SET_STATUS(aecp_hdr, AVB_AECP_STATUS_SUCCESS); - - AVB_PACKET_SET_LENGTH(&aecp_hdr->hdr, - (uint16_t)(total - sizeof(struct avb_ethernet_header) - - sizeof(struct avb_packet_header))); - - /* protocol_id and command_type are echoed from the command — the U - * flag is preserved but reset for the response per Section 5.4.4.2. */ - vu_reply->command_type = htons(ntohs(vu_reply->command_type) & - AVB_AECP_MVU_CMD_TYPE_CMD_MASK); - - body = (struct avb_packet_aecp_mvu_get_milan_info_rsp *)vu_reply->payload; - body->protocol_version = htonl(MILAN_MVU_PROTOCOL_VERSION); - body->features_flags = htonl(MILAN_MVU_FEATURES_FLAGS); - body->certification_version = htonl(MILAN_MVU_CERTIFICATION_VERSION); - - pw_log_info("MVU GET_MILAN_INFO reply proto=0x%08x features=0x%08x " - "cert=0x%08x", MILAN_MVU_PROTOCOL_VERSION, - MILAN_MVU_FEATURES_FLAGS, MILAN_MVU_CERTIFICATION_VERSION); - - return avb_server_send_packet(server, h_reply->src, AVB_TSN_ETH, - buf, total); -} - int aecp_vendor_unique_milan_v12_handle_command(struct aecp *aecp, const void *m, int len) { const struct avb_ethernet_header *h = m; const struct avb_packet_aecp_vendor_unique *vu; - uint16_t command_type; - uint16_t cmd; + const struct mvu_cmd_info *info; + uint16_t command_type, cmd; + struct timespec ts_now = {0}; + int64_t now; if (len < (int)(sizeof(*h) + sizeof(*vu))) return 0; @@ -121,16 +67,23 @@ int aecp_vendor_unique_milan_v12_handle_command(struct aecp *aecp, command_type = ntohs(vu->command_type); cmd = command_type & AVB_AECP_MVU_CMD_TYPE_CMD_MASK; - pw_log_debug("MVU command 0x%04x (U=%d)", cmd, - (command_type & AVB_AECP_MVU_CMD_TYPE_U_FLAG_MASK) ? 1 : 0); + pw_log_info("mvu command %s (r=%d)", + cmd < SPA_N_ELEMENTS(mvu_cmd_names) && mvu_cmd_names[cmd] + ? mvu_cmd_names[cmd] : "unknown", + (command_type & AVB_AECP_MVU_CMD_TYPE_R_FLAG_MASK) ? 1 : 0); - switch (cmd) { - case AVB_AECP_MVU_CMD_GET_MILAN_INFO: - (void)handle_get_milan_info(aecp, m, len); - return 1; - default: - (void)reply_mvu_status(aecp, AVB_AECP_STATUS_NOT_IMPLEMENTED, - m, len); + if (cmd >= SPA_N_ELEMENTS(mvu_cmd_info_milan_v12) || + mvu_cmd_info_milan_v12[cmd].handle_command == NULL) { + (void)reply_mvu_not_implemented(aecp, m, len); return 1; } + + info = &mvu_cmd_info_milan_v12[cmd]; + + if (clock_gettime(CLOCK_TAI, &ts_now)) + pw_log_warn("clock_gettime(CLOCK_TAI): %m"); + now = SPA_TIMESPEC_TO_NSEC(&ts_now); + + (void)info->handle_command(aecp, now, m, len); + return 1; } diff --git a/src/modules/module-avb/aecp-vendor-unique-milan-v12.h b/src/modules/module-avb/aecp-vendor-unique-milan-v12.h index 019cfff82..9be2f139d 100644 --- a/src/modules/module-avb/aecp-vendor-unique-milan-v12.h +++ b/src/modules/module-avb/aecp-vendor-unique-milan-v12.h @@ -8,17 +8,8 @@ #include "aecp.h" -/* Milan v1.2 Section 5.4.4 — Milan Vendor Unique (MVU) protocol carried inside an - * IEEE 1722.1 AECP VENDOR_UNIQUE_COMMAND/RESPONSE (message_type 6/7). - * - * Wire layout after the standard AECP common header: - * protocol_id (6) | command_type (2) | command-specific data - * - * Milan v1.2 Section 5.4.3.2.1: protocol_id is the Avnu OUI-36 00-1B-C5-0A-C - * appended with the 12-bit MVU protocol id 0x100, giving the 6-byte - * marker 00-1B-C5-0A-C1-00. command_type is a 1-bit U flag (MSB) + - * 15-bit command code. */ - +/* Milan v1.2 Section 5.4.3.2.1 protocol_id: Avnu OUI-36 00-1B-C5-0A-C + + * 12-bit MVU id 0x100 = 00-1B-C5-0A-C1-00. */ #define AVB_AECP_MVU_PROTOCOL_ID_0 0x00 #define AVB_AECP_MVU_PROTOCOL_ID_1 0x1B #define AVB_AECP_MVU_PROTOCOL_ID_2 0xC5 @@ -26,25 +17,22 @@ #define AVB_AECP_MVU_PROTOCOL_ID_4 0xC1 #define AVB_AECP_MVU_PROTOCOL_ID_5 0x00 -/* Milan MVU command codes (Milan v1.2 Section 5.4.4). */ +/* Milan v1.2 Section 5.4.3.2.3 Table 5.18 MVU command codes. */ #define AVB_AECP_MVU_CMD_GET_MILAN_INFO 0x0000 -#define AVB_AECP_MVU_CMD_TYPE_U_FLAG_MASK 0x8000 +/* Milan v1.2 Section 5.4.3.2.2: r is reserved (must be 0). */ +#define AVB_AECP_MVU_CMD_TYPE_R_FLAG_MASK 0x8000 #define AVB_AECP_MVU_CMD_TYPE_CMD_MASK 0x7FFF +/* Milan v1.2 Section 5.4.3.2 Figure 5.2 MVU payload format. */ struct avb_packet_aecp_vendor_unique { struct avb_packet_aecp_header hdr; uint8_t protocol_id[6]; uint16_t command_type; + uint16_t reserved; uint8_t payload[0]; } __attribute__ ((__packed__)); -struct avb_packet_aecp_mvu_get_milan_info_rsp { - uint32_t protocol_version; - uint32_t features_flags; - uint32_t certification_version; -} __attribute__ ((__packed__)); - int aecp_vendor_unique_milan_v12_handle_command(struct aecp *aecp, const void *m, int len); diff --git a/src/tools/meson.build b/src/tools/meson.build index ef5afe4fc..2def79257 100644 --- a/src/tools/meson.build +++ b/src/tools/meson.build @@ -121,6 +121,7 @@ if build_avb_virtual '../modules/module-avb/aecp.c', '../modules/module-avb/aecp-aem.c', '../modules/module-avb/aecp-vendor-unique-milan-v12.c', + '../modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-get-milan-info.c', '../modules/module-avb/aecp-aem-cmds-resps/cmd-available.c', '../modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-control.c', '../modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-name.c', diff --git a/test/meson.build b/test/meson.build index 44e603b21..a9ca3fc77 100644 --- a/test/meson.build +++ b/test/meson.build @@ -176,6 +176,7 @@ if build_avb_virtual '../src/modules/module-avb/aecp.c', '../src/modules/module-avb/aecp-aem.c', '../src/modules/module-avb/aecp-vendor-unique-milan-v12.c', + '../src/modules/module-avb/aecp-vendor-unique-milan-v12-cmds-resps/cmd-get-milan-info.c', '../src/modules/module-avb/aecp-aem-cmds-resps/cmd-available.c', '../src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-control.c', '../src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-name.c',