Merge branch '5011_introduce_unsolicited_notififications' into 'master'

5011: module-avb: milan: aecp-aem handle de/registration for unsolicited...

See merge request pipewire/pipewire!2619
This commit is contained in:
hackerman-kl 2025-12-07 17:08:42 +01:00
commit d10b72f799
12 changed files with 440 additions and 7 deletions

View file

@ -738,6 +738,9 @@ if build_module_avb
'module-avb/aecp-aem.c',
'module-avb/aecp-aem-cmds-resps/cmd-lock-entity.c',
'module-avb/aecp-aem-cmds-resps/cmd-available.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/reply-unsol-helpers.c',
'module-avb/es-builder.c',
'module-avb/avdecc.c',
'module-avb/maap.c',

View file

@ -47,7 +47,7 @@ int handle_cmd_entity_available_milan_v12(struct aecp *aecp, int64_t now, const
}
entity_state = desc->ptr;
lock = &entity_state->lock_state;
lock = &entity_state->state.lock_state;
/* Forge the response for the entity that is locking the device */
memcpy(buf, m, len);

View file

@ -0,0 +1,68 @@
/* AVB support */
/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */
/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */
/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */
/* SPDX-License-Identifier: MIT */
#include <pipewire/log.h>
#include <inttypes.h>
#include "../aecp-aem.h"
#include "../aecp-aem-state.h"
#include "../aecp-aem-milan.h"
#include "cmd-deregister-unsolicited-notifications.h"
#include "cmd-resp-helpers.h"
int handle_cmd_deregister_unsol_notif_milan_v12(struct aecp *aecp, int64_t now,
const void *m, int len)
{
const struct avb_ethernet_header *h = m;
const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void);
struct descriptor *desc;
struct aecp_aem_entity_milan_state *entity_state;
struct aecp_aem_unsol_notification_state *unsol;
uint64_t controller_id = htobe64(p->aecp.controller_guid);
const uint32_t ctrler_max = AECP_AEM_MILAN_MAX_CONTROLLER;
uint16_t index;
desc = server_find_descriptor(aecp->server, AVB_AEM_DESC_ENTITY, 0);
if (desc == NULL)
return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len);
entity_state = (struct aecp_aem_entity_milan_state *) desc->ptr;
unsol = entity_state->unsol_notif_state;
/** First check if the controller was already registered */
for (index = 0; index < ctrler_max; index++) {
uint64_t ctrl_eid = unsol[index].ctrler_entity_id;
bool ctrler_reged = unsol[index].is_registered;
if ((ctrl_eid == controller_id) && ctrler_reged) {
pw_log_debug("controller 0x%"PRIx64", already registered",
controller_id);
return reply_success(aecp, m, len);
}
}
/** When one slot is in the array is available use it */
for (index = 0; index < ctrler_max; index++) {
if (!unsol[index].is_registered) {
break;
}
}
/** Reach the maximum controller allocated */
if (index == ctrler_max) {
return reply_no_resources(aecp, m, len);
}
unsol[index].ctrler_entity_id = controller_id;
memcpy(unsol[index].ctrler_mac_addr, h->src, sizeof(h->src));
unsol[index].is_registered = true;
unsol[index].port_id = 0;
unsol[index].next_seq_id = 0;
pw_log_info("Unsol registration for 0x%"PRIx64, controller_id);
return reply_success(aecp, m, len);
}

View file

@ -0,0 +1,23 @@
/* AVB support */
/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */
/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */
/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */
/* SPDX-License-Identifier: MIT */
#ifndef __AVB_DEREGISTER_UNSOLICITED_NOTIFICATIONS_H__
#define __AVB_DEREGISTER_UNSOLICITED_NOTIFICATIONS_H__
#include <stdint.h>
/**
* @brief Command handling will generate the response to the
* received command when deregistering the a controller from
* the list of subscribed controller entities.
*
* @see IEEE1722.1-2021 Section 7.4.38 .
* @see Milan V1.2 Section 5.4.2.22.
*/
int handle_cmd_deregister_unsol_notif_milan_v12(struct aecp *aecp, int64_t now,
const void *m, int len);
#endif // __AVB_DEREGISTER_UNSOLICITED_NOTIFICATIONS_H__

View file

@ -17,6 +17,59 @@
#include "cmd-lock-entity.h"
#include "cmd-resp-helpers.h"
#include "reply-unsol-helpers.h"
static int handle_unsol_lock_common(struct aecp *aecp,
struct aecp_aem_lock_state *lock, bool internal)
{
uint8_t buf[512];
void *m = buf;
// struct aecp_aem_regis_unsols
struct avb_ethernet_header *h = m;
struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void);
struct avb_packet_aecp_aem_lock *ae;
size_t len = sizeof(*h) + sizeof(*p) + sizeof(*ae);
int rc;
memset(buf, 0, sizeof(buf));
ae = (struct avb_packet_aecp_aem_lock*)p->payload;
if (!lock->is_locked) {
ae->locked_guid = 0;
ae->flags = htonl(AECP_AEM_LOCK_ENTITY_FLAG_UNLOCK);
lock->is_locked = false;
lock->base_info.expire_timeout = LONG_MAX;
} else {
ae->locked_guid = htobe64(lock->locked_id);
ae->flags = 0;
}
AVB_PACKET_AEM_SET_COMMAND_TYPE(p, AVB_AECP_AEM_CMD_LOCK_ENTITY);
/** Setup the packet for the unsolicited notification*/
rc = reply_unsolicited_notifications(aecp, &lock->base_info, buf, len, internal);
if (rc) {
pw_log_error("Unsolicited notification failed \n");
}
return rc;
}
static int handle_unsol_lock_entity_milanv12(struct aecp *aecp, struct descriptor *desc,
uint64_t ctrler_id)
{
int rc = -1;
struct aecp_aem_entity_milan_state *entity_state;
struct aecp_aem_lock_state *lock;
entity_state = desc->ptr;
lock = &entity_state->state.lock_state;
lock->base_info.controller_entity_id = ctrler_id;
rc = handle_unsol_lock_common(aecp, lock, false);
return rc;
}
/* LOCK_ENTITY */
/* Milan v1.2, Sec. 5.4.2.2; IEEE 1722.1-2021, Sec. 7.4.2*/
@ -49,7 +102,7 @@ int handle_cmd_lock_entity_milan_v12(struct aecp *aecp, int64_t now, const void
return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len);
entity_state = desc->ptr;
lock = &entity_state->lock_state;
lock = &entity_state->state.lock_state;
if (desc_type != AVB_AEM_DESC_ENTITY || desc_id != 0) {
/*
@ -117,5 +170,11 @@ int handle_cmd_lock_entity_milan_v12(struct aecp *aecp, int64_t now, const void
return reply_entity_locked(aecp, buf, len);
}
return reply_success(aecp, buf, len);
if (reply_success(aecp, buf, len)) {
pw_log_debug("Failed sending success reply\n");
}
/* Then update the state using the system */
return handle_unsol_lock_entity_milanv12(aecp, desc, ctrler_id);
}

View file

@ -0,0 +1,68 @@
/* AVB support */
/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */
/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */
/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */
/* SPDX-License-Identifier: MIT */
#include <pipewire/log.h>
#include <inttypes.h>
#include "../aecp-aem.h"
#include "../aecp-aem-state.h"
#include "../aecp-aem-milan.h"
#include "cmd-register-unsolicited-notifications.h"
#include "cmd-resp-helpers.h"
int handle_cmd_register_unsol_notif_milan_v12(struct aecp *aecp, int64_t now,
const void *m, int len)
{
const struct avb_ethernet_header *h = m;
const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void);
struct descriptor *desc;
struct aecp_aem_entity_milan_state *entity_state;
struct aecp_aem_unsol_notification_state *unsol;
uint64_t controller_id = htobe64(p->aecp.controller_guid);
const uint32_t ctrler_max = AECP_AEM_MILAN_MAX_CONTROLLER;
uint16_t index;
desc = server_find_descriptor(aecp->server, AVB_AEM_DESC_ENTITY, 0);
if (desc == NULL)
return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len);
entity_state = (struct aecp_aem_entity_milan_state *) desc->ptr;
unsol = entity_state->unsol_notif_state;
/** First check if the controller was already registered */
for (index = 0; index < ctrler_max; index++) {
uint64_t ctrl_eid = unsol[index].ctrler_entity_id;
bool ctrler_reged = unsol[index].is_registered;
if ((ctrl_eid == controller_id) && ctrler_reged) {
pw_log_debug("controller 0x%"PRIx64", already registered",
controller_id);
return reply_success(aecp, m, len);
}
}
/** When one slot is in the array is available use it */
for (index = 0; index < ctrler_max; index++) {
if (!unsol[index].is_registered) {
break;
}
}
/** Reach the maximum controller allocated */
if (index == ctrler_max) {
return reply_no_resources(aecp, m, len);
}
unsol[index].ctrler_entity_id = controller_id;
memcpy(unsol[index].ctrler_mac_addr, h->src, sizeof(h->src));
unsol[index].is_registered = true;
unsol[index].port_id = 0;
unsol[index].next_seq_id = 0;
pw_log_info("Unsol registration for 0x%"PRIx64, controller_id);
return reply_success(aecp, m, len);
}

View file

@ -0,0 +1,23 @@
/* AVB support */
/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */
/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */
/* SPDX-FileCopyrightText: Copyright © 2025 Simon Gapp <simon.gapp@kebag-logic.com> */
/* SPDX-License-Identifier: MIT */
#ifndef __AVB_REGISTER_UNSOLICITED_NOTIFICATIONS_H__
#define __AVB_REGISTER_UNSOLICITED_NOTIFICATIONS_H__
#include <stdint.h>
/**
* @brief Command handling will generate the response to the
* received command when registering the a controller from
* the list of subscribed entities.
*
* @see IEEE1722.1-2021 Section 7.4.37 .
* @see Milan V1.2 Section 5.4.2.21 .
*/
int handle_cmd_register_unsol_notif_milan_v12(struct aecp *aecp, int64_t now,
const void *m, int len);
#endif // __AVB_REGISTER_UNSOLICITED_NOTIFICATIONS_H__

View file

@ -0,0 +1,153 @@
/* AVB support */
/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */
/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */
/* SPDX-License-Identifier: MIT */
#include "../aecp-aem.h"
#include "../aecp-aem-state.h"
#include "../internal.h"
#include "reply-unsol-helpers.h"
#include <pipewire/log.h>
#define AECP_UNSOL_BUFFER_SIZE (128U)
#define AECP_AEM_MIN_PACKET_LENGTH (64U)
static int reply_unsol_get_specific_info(struct aecp *aecp, struct descriptor *desc,
struct aecp_aem_unsol_notification_state **unsol_state, size_t *count)
{
enum avb_mode mode = aecp->server->avb_mode;
switch (mode) {
case AVB_MODE_LEGACY:
pw_log_error("Not implemented\n");
break;
case AVB_MODE_MILAN_V12:
struct aecp_aem_entity_milan_state *entity_state;
entity_state = desc->ptr;
*unsol_state = entity_state->unsol_notif_state;
*count = AECP_AEM_MILAN_MAX_CONTROLLER;
return 0;
default:
pw_log_error("Invalid avb_mode %d\n", mode);
break;
}
return -1;
}
static int reply_unsol_send(struct aecp *aecp, uint64_t controller_id,
void *packet, size_t len, bool internal)
{
size_t ctrler_index;
int rc = 0;
struct avb_ethernet_header *h;
struct avb_packet_aecp_aem *p;
struct aecp_aem_unsol_notification_state *unsol_state;
struct descriptor *desc;
size_t max_ctrler;
desc = server_find_descriptor(aecp->server, AVB_AEM_DESC_ENTITY, 0);
if (desc == NULL) {
pw_log_error("Could not find the ENTITY descriptor 0");
return -EINVAL;
}
rc = reply_unsol_get_specific_info(aecp, desc, &unsol_state, &max_ctrler);
if (rc) {
return -EINVAL;
}
h = (struct avb_ethernet_header*) packet;
p = SPA_PTROFF(h, sizeof(*h), void);
/* Loop through all the unsol entities. */
for (ctrler_index = 0; ctrler_index < max_ctrler; ctrler_index++)
{
if (!unsol_state[ctrler_index].is_registered) {
pw_log_debug("Not registered %zu", ctrler_index);
continue;
}
if ((controller_id == unsol_state[ctrler_index].ctrler_entity_id)
&& !internal) {
/* Do not send unsolicited if that the one triggering
changes this is not a timeout. */
pw_log_debug("Do not send twice of %"PRIx64" %"PRIx64,
controller_id,
unsol_state[ctrler_index].ctrler_entity_id);
continue;
}
p->aecp.controller_guid =
htobe64(unsol_state[ctrler_index].ctrler_entity_id);
p->aecp.sequence_id = htons(unsol_state[ctrler_index].next_seq_id);
unsol_state[ctrler_index].next_seq_id++;
rc = avb_server_send_packet(aecp->server,
unsol_state[ctrler_index].ctrler_mac_addr,
AVB_TSN_ETH, packet, len);
if (rc) {
pw_log_error("while sending packet to %"PRIx64,
unsol_state[ctrler_index].ctrler_entity_id);
return rc;
}
}
return rc;
}
static void reply_unsol_notifications_prepare(struct aecp *aecp,
uint8_t *buf, void *packet, size_t len)
{
struct avb_ethernet_header *h;
struct avb_packet_aecp_aem *p;
size_t ctrl_data_length;
/* Here the value of 12 is the delta between the target_entity_id and
start of the AECP message specific data. */
ctrl_data_length = len - (sizeof(*h) + sizeof(*p)) +
AVB_PACKET_CONTROL_DATA_OFFSET;
h = (struct avb_ethernet_header*) packet;
p = SPA_PTROFF(h, sizeof(*h), void);
p->aecp.hdr.subtype = AVB_SUBTYPE_AECP;
AVB_PACKET_AECP_SET_MESSAGE_TYPE(&p->aecp, AVB_AECP_MESSAGE_TYPE_AEM_RESPONSE);
AVB_PACKET_SET_VERSION(&p->aecp.hdr, 0);
AVB_PACKET_AECP_SET_STATUS(&p->aecp, AVB_AECP_AEM_STATUS_SUCCESS);
AVB_PACKET_SET_LENGTH(&p->aecp.hdr, ctrl_data_length);
p->u = 1;
p->aecp.target_guid = htobe64(aecp->server->entity_id);
}
/**
* @brief Sends unsolicited notifications. Does not sends information unless to
* the controller id unless an internal change has happenned (timeout, action
* etc)
*
*/
int reply_unsolicited_notifications(struct aecp *aecp,
struct aecp_aem_base_info *b_state, void *packet, size_t len,
bool internal)
{
uint8_t buf[AECP_UNSOL_BUFFER_SIZE];
if (len < AECP_AEM_MIN_PACKET_LENGTH) {
memset(buf, 0, AECP_AEM_MIN_PACKET_LENGTH);
memcpy(buf, packet, len);
len = AECP_AEM_MIN_PACKET_LENGTH;
packet = buf;
}
/** Retrieve the entity descriptor */
reply_unsol_notifications_prepare(aecp, buf, packet, len);
return reply_unsol_send(aecp, b_state->controller_entity_id, packet, len,
internal);
}

View file

@ -0,0 +1,23 @@
/* AVB support */
/* SPDX-FileCopyrightText: Copyright © 2025 Kebag-Logic */
/* SPDX-FileCopyrightText: Copyright © 2025 Alex Malki <alexandre.malki@kebag-logic.com> */
/* SPDX-License-Identifier: MIT */
#ifndef __AVB_REPLY_UNSOL_HELPER_H__
#define __AVB_REPLY_UNSOL_HELPER_H__
#include <stdint.h>
#include <pipewire/log.h>
/**
* @brief Sends unsolicited notifications. Does not sends information unless to
* the controller id unless an internal change has happenned (timeout, action
* etc)
* @see Milan V1.2 Section 5.4.2.21
* @see IEEE 1722.1-2021 7.5.2 ( Unsolicited Notifications )
*/
int reply_unsolicited_notifications(struct aecp *aecp,
struct aecp_aem_base_info *b_state, void *packet, size_t len,
bool internal);
#endif // __AVB_REPLY_UNSOL_HELPER_H__

View file

@ -57,7 +57,7 @@ struct aecp_aem_unsol_notification_state {
/**
* The controller is that is locking this system
*/
uint64_t ctrler_endity_id;
uint64_t ctrler_entity_id;
/**
* mac Address of the controller
@ -115,6 +115,7 @@ struct aecp_aem_lock_state {
*/
struct aecp_aem_entity_state {
struct avb_aem_desc_entity desc;
struct aecp_aem_lock_state lock_state;
};
/**
@ -122,7 +123,6 @@ struct aecp_aem_entity_state {
*/
struct aecp_aem_entity_milan_state {
struct aecp_aem_entity_state state;
struct aecp_aem_lock_state lock_state;
struct aecp_aem_unsol_notification_state unsol_notif_state[AECP_AEM_MILAN_MAX_CONTROLLER];
};

View file

@ -11,6 +11,8 @@
/* The headers including the command and response of the system */
#include "aecp-aem-cmds-resps/cmd-available.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"
/* ACQUIRE_ENTITY */
@ -292,6 +294,15 @@ static const struct cmd_info cmd_info_milan_v12[] = {
AECP_AEM_HANDLE_CMD(AVB_AECP_AEM_CMD_READ_DESCRIPTOR, true,
handle_read_descriptor_common),
AECP_AEM_HANDLE_CMD(AVB_AECP_AEM_CMD_REGISTER_UNSOLICITED_NOTIFICATION,
false, handle_cmd_register_unsol_notif_milan_v12),
AECP_AEM_HANDLE_CMD(AVB_AECP_AEM_CMD_DEREGISTER_UNSOLICITED_NOTIFICATION,
false, handle_cmd_deregister_unsol_notif_milan_v12),
AECP_AEM_HANDLE_CMD(AVB_AECP_AEM_CMD_GET_AVB_INFO, true,
handle_get_avb_info_common),
};
static const struct {

View file

@ -229,9 +229,11 @@ struct avb_packet_aecp_aem {
uint8_t payload[0];
} __attribute__ ((__packed__));
#define AVB_PACKET_AEM_SET_COMMAND_TYPE(p,v) ((p)->cmd1 = ((v) >> 8),(p)->cmd2 = (v))
#define AVB_PACKET_CONTROL_DATA_OFFSET (12U)
#define AVB_PACKET_AEM_GET_COMMAND_TYPE(p) ((p)->cmd1 << 8 | (p)->cmd2)
#define AVB_PACKET_AEM_SET_COMMAND_TYPE(p,v) ((p)->cmd1 = ((v) >> 8),(p)->cmd2 = (v))
#define AVB_PACKET_AEM_GET_COMMAND_TYPE(p) ((p)->cmd1 << 8 | (p)->cmd2)
int avb_aecp_aem_handle_command(struct aecp *aecp, const void *m, int len);
int avb_aecp_aem_handle_response(struct aecp *aecp, const void *m, int len);