diff --git a/src/modules/module-avb/acmp-cmds-resps/acmp-common.c b/src/modules/module-avb/acmp-cmds-resps/acmp-common.c index bff9af365..29ce587ed 100644 --- a/src/modules/module-avb/acmp-cmds-resps/acmp-common.c +++ b/src/modules/module-avb/acmp-cmds-resps/acmp-common.c @@ -6,60 +6,6 @@ #include "../aecp-aem-state.h" #include "../stream.h" - -struct pending *pending_find(struct acmp *acmp, uint32_t type, uint16_t sequence_id) -{ - struct pending *p; - spa_list_for_each(p, &acmp->pending[type], link) - if (p->sequence_id == sequence_id) - return p; - return NULL; -} - -void pending_free(struct acmp *acmp, struct pending *p) -{ - spa_list_remove(&p->link); - free(p); -} - -void pending_destroy(struct acmp *acmp) -{ - struct pending *p, *t; - for (uint32_t list_id = 0; list_id < PENDING_CONTROLLER; list_id++) { - spa_list_for_each_safe(p, t, &acmp->pending[list_id], link) { - pending_free(acmp, p); - } - } -} - -void *pending_new(struct acmp *acmp, uint32_t type, uint64_t now, uint32_t timeout_ms, - const void *m, size_t size) -{ - struct pending *p; - struct avb_ethernet_header *h; - struct avb_packet_acmp *pm; - - p = calloc(1, sizeof(*p) + size); - if (p == NULL) - return NULL; - p->last_time = now; - p->timeout = timeout_ms * SPA_NSEC_PER_MSEC; - p->sequence_id = acmp->sequence_id[type]++; - p->size = size; - p->ptr = SPA_PTROFF(p, sizeof(*p), void); - memcpy(p->ptr, m, size); - - h = p->ptr; - pm = SPA_PTROFF(h, sizeof(*h), void); - p->old_sequence_id = ntohs(pm->sequence_id); - pm->sequence_id = htons(p->sequence_id); - spa_list_append(&acmp->pending[type], &p->link); - - return p->ptr; -} - - - struct stream *find_stream(struct server *server, enum spa_direction direction, uint16_t index) { @@ -75,13 +21,13 @@ struct stream *find_stream(struct server *server, enum spa_direction direction, type = AVB_AEM_DESC_STREAM_OUTPUT; break; default: - pw_log_error("Unkown direction\n"); + pw_log_error("Unkown direction"); return NULL; } desc = server_find_descriptor(server, type, index); if (!desc) { - pw_log_error("Could not find stream type %u index %u\n", + pw_log_error("Could not find stream type %u index %u", type, index); return NULL; } @@ -90,19 +36,19 @@ struct stream *find_stream(struct server *server, enum spa_direction direction, case SPA_DIRECTION_INPUT: struct aecp_aem_stream_input_state *stream_in; stream_in = desc->ptr; - stream = &stream_in->stream; + stream = &stream_in->common.stream; break; case SPA_DIRECTION_OUTPUT: struct aecp_aem_stream_output_state *stream_out; stream_out = desc->ptr; - stream = &stream_out->stream; + stream = &stream_out->common.stream; break; } return stream; } -int reply_not_supported(struct acmp *acmp, uint8_t type, const void *m, int len) +int acmp_reply_not_supported(struct acmp *acmp, uint8_t type, const void *m, int len) { struct server *server = acmp->server; uint8_t buf[len]; @@ -115,12 +61,3 @@ int reply_not_supported(struct acmp *acmp, uint8_t type, const void *m, int len) return avb_server_send_packet(server, h->src, AVB_TSN_ETH, buf, len); } - -int retry_pending(struct acmp *acmp, uint64_t now, struct pending *p) -{ - struct server *server = acmp->server; - struct avb_ethernet_header *h = p->ptr; - p->retry++; - p->last_time = now; - return avb_server_send_packet(server, h->dest, AVB_TSN_ETH, p->ptr, p->size); -} diff --git a/src/modules/module-avb/acmp-cmds-resps/acmp-common.h b/src/modules/module-avb/acmp-cmds-resps/acmp-common.h index 06a928a57..e3e0c09ec 100644 --- a/src/modules/module-avb/acmp-cmds-resps/acmp-common.h +++ b/src/modules/module-avb/acmp-cmds-resps/acmp-common.h @@ -9,20 +9,14 @@ #include #include "../acmp.h" -struct pending { - struct spa_list link; - uint64_t last_time; - uint64_t timeout; - uint16_t old_sequence_id; - uint16_t sequence_id; - uint16_t retry; - size_t size; - void *ptr; -}; - struct acmp { struct server *server; struct spa_hook server_listener; +}; + + +struct acmp_legacy_avb { + struct acmp acmp; #define PENDING_TALKER 0 #define PENDING_LISTENER 1 @@ -31,20 +25,18 @@ struct acmp { uint16_t sequence_id[3]; }; -struct pending *pending_find(struct acmp *acmp, uint32_t type, uint16_t sequence_id); +struct acmp_milan_v12 { + struct acmp acmp; -void pending_free(struct acmp *acmp, struct pending *p); + struct spa_list timers_lt; + struct spa_list pending_tk; + uint16_t sequence_id[2]; +}; -void pending_destroy(struct acmp *acmp); - -void *pending_new(struct acmp *acmp, uint32_t type, uint64_t now, - uint32_t timeout_ms, const void *m, size_t size); - -int retry_pending(struct acmp *acmp, uint64_t now, struct pending *p); struct stream *find_stream(struct server *server, enum spa_direction direction, uint16_t index); -int reply_not_supported(struct acmp *acmp, uint8_t type, const void *m, int len); +int acmp_reply_not_supported(struct acmp *acmp, uint8_t type, const void *m, int len); #endif //AVB_ACMP_COMMON_H diff --git a/src/modules/module-avb/acmp-cmds-resps/acmp-legacy-avb.c b/src/modules/module-avb/acmp-cmds-resps/acmp-legacy-avb.c index c4d1b3da8..1656174d9 100644 --- a/src/modules/module-avb/acmp-cmds-resps/acmp-legacy-avb.c +++ b/src/modules/module-avb/acmp-cmds-resps/acmp-legacy-avb.c @@ -5,6 +5,78 @@ #include "acmp-common.h" #include "acmp-legacy-avb.h" +struct pending { + struct spa_list link; + uint64_t last_time; + uint64_t timeout; + uint16_t old_sequence_id; + uint16_t sequence_id; + uint16_t retry; + size_t size; + void *ptr; +}; + +static struct pending *pending_find(struct acmp_legacy_avb *acmp_legacy, uint32_t type, uint16_t sequence_id) +{ + struct pending *p; + spa_list_for_each(p, &acmp_legacy->pending[type], link) + if (p->sequence_id == sequence_id) + return p; + return NULL; +} + +static void pending_free(struct acmp_legacy_avb *acmp_legacy, struct pending *p) +{ + spa_list_remove(&p->link); + free(p); +} + +static void pending_destroy(struct acmp_legacy_avb *acmp_legacy) +{ + struct pending *p, *t; + for (uint32_t list_id = 0; list_id < PENDING_CONTROLLER; list_id++) { + spa_list_for_each_safe(p, t, &acmp_legacy->pending[list_id], link) { + pending_free(acmp_legacy, p); + } + } +} + +static void *pending_new(struct acmp_legacy_avb *acmp_legacy, uint32_t type, uint64_t now, uint32_t timeout_ms, + const void *m, size_t size) +{ + struct pending *p; + struct avb_ethernet_header *h; + struct avb_packet_acmp *pm; + + p = calloc(1, sizeof(*p) + size); + if (p == NULL) + return NULL; + p->last_time = now; + p->timeout = timeout_ms * SPA_NSEC_PER_MSEC; + p->sequence_id = acmp_legacy->sequence_id[type]++; + p->size = size; + p->ptr = SPA_PTROFF(p, sizeof(*p), void); + memcpy(p->ptr, m, size); + + h = p->ptr; + pm = SPA_PTROFF(h, sizeof(*h), void); + p->old_sequence_id = ntohs(pm->sequence_id); + pm->sequence_id = htons(p->sequence_id); + spa_list_append(&acmp_legacy->pending[type], &p->link); + + return p->ptr; +} + +int retry_pending(struct acmp_legacy_avb *acmp_legacy, uint64_t now, struct pending *p) +{ + struct acmp *acmp = (struct acmp*)acmp_legacy; + struct server *server = acmp->server; + struct avb_ethernet_header *h = p->ptr; + p->retry++; + p->last_time = now; + return avb_server_send_packet(server, h->dest, AVB_TSN_ETH, p->ptr, p->size); +} + int handle_connect_tx_command_legacy_avb(struct acmp *acmp, uint64_t now, const void *m, int len) { @@ -43,6 +115,7 @@ done: int handle_connect_tx_response_legacy_avb(struct acmp *acmp, uint64_t now, const void *m, int len) { + struct acmp_legacy_avb *acmp_legacy = (struct acmp_legacy_avb *) acmp; struct server *server = acmp->server; struct avb_ethernet_header *h; const struct avb_packet_acmp *resp = SPA_PTROFF(m, sizeof(*h), void); @@ -57,7 +130,7 @@ int handle_connect_tx_response_legacy_avb(struct acmp *acmp, uint64_t now, sequence_id = ntohs(resp->sequence_id); - pending = pending_find(acmp, PENDING_TALKER, sequence_id); + pending = pending_find(acmp_legacy, PENDING_TALKER, sequence_id); if (pending == NULL) return 0; @@ -79,7 +152,7 @@ int handle_connect_tx_response_legacy_avb(struct acmp *acmp, uint64_t now, res = avb_server_send_packet(server, h->dest, AVB_TSN_ETH, h, pending->size); - pending_free(acmp, pending); + pending_free(acmp_legacy, pending); return res; } @@ -117,6 +190,7 @@ done: int handle_disconnect_tx_response_legacy_avb(struct acmp *acmp, uint64_t now, const void *m, int len) { + struct acmp_legacy_avb *acmp_legacy = (struct acmp_legacy_avb *) acmp; struct server *server = acmp->server; struct avb_ethernet_header *h; struct avb_packet_acmp *reply; @@ -131,7 +205,7 @@ int handle_disconnect_tx_response_legacy_avb(struct acmp *acmp, uint64_t now, sequence_id = ntohs(resp->sequence_id); - pending = pending_find(acmp, PENDING_TALKER, sequence_id); + pending = pending_find(acmp_legacy, PENDING_TALKER, sequence_id); if (pending == NULL) return 0; @@ -151,7 +225,7 @@ int handle_disconnect_tx_response_legacy_avb(struct acmp *acmp, uint64_t now, res = avb_server_send_packet(server, h->dest, AVB_TSN_ETH, h, pending->size); - pending_free(acmp, pending); + pending_free(acmp_legacy, pending); return res; } @@ -159,6 +233,7 @@ int handle_disconnect_tx_response_legacy_avb(struct acmp *acmp, uint64_t now, int handle_connect_rx_command_legacy_avb(struct acmp *acmp, uint64_t now, const void *m, int len) { + struct acmp_legacy_avb *acmp_legacy = (struct acmp_legacy_avb *) acmp; struct server *server = acmp->server; struct avb_ethernet_header *h; const struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); @@ -167,7 +242,7 @@ int handle_connect_rx_command_legacy_avb(struct acmp *acmp, uint64_t now, if (be64toh(p->listener_guid) != server->entity_id) return 0; - h = pending_new(acmp, PENDING_TALKER, now, + h = pending_new(acmp_legacy, PENDING_TALKER, now, AVB_ACMP_TIMEOUT_CONNECT_TX_COMMAND_MS, m, len); if (h == NULL) return -errno; @@ -182,6 +257,7 @@ int handle_connect_rx_command_legacy_avb(struct acmp *acmp, uint64_t now, int handle_disconnect_rx_command_legacy_avb(struct acmp *acmp, uint64_t now, const void *m, int len) { + struct acmp_legacy_avb *acmp_legacy = (struct acmp_legacy_avb *) acmp; struct server *server = acmp->server; struct avb_ethernet_header *h; const struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); @@ -190,7 +266,7 @@ int handle_disconnect_rx_command_legacy_avb(struct acmp *acmp, uint64_t now, if (be64toh(p->listener_guid) != server->entity_id) return 0; - h = pending_new(acmp, PENDING_TALKER, now, + h = pending_new(acmp_legacy, PENDING_TALKER, now, AVB_ACMP_TIMEOUT_DISCONNECT_TX_COMMAND_MS, m, len); if (h == NULL) return -errno; @@ -201,3 +277,54 @@ int handle_disconnect_rx_command_legacy_avb(struct acmp *acmp, uint64_t now, return avb_server_send_packet(server, h->dest, AVB_TSN_ETH, h, len); } + +static void check_timeout(struct acmp *acmp, uint64_t now, uint16_t type) +{ + struct acmp_legacy_avb *acmp_avb = (struct acmp_legacy_avb *)acmp; + struct pending *p, *t; + + spa_list_for_each_safe(p, t, &acmp_avb->pending[type], link) { + if (p->last_time + p->timeout > now) + continue; + + if (p->retry == 0) { + pw_log_info("%p: pending timeout, retry", p); + retry_pending(acmp_avb, now, p); + } else { + pw_log_info("%p: pending timeout, fail", p); + pending_free(acmp_avb, p); + } + } +} + +void acmp_periodic_avb_legacy(void *data, uint64_t now) +{ + struct acmp *acmp = data; + + check_timeout(acmp, now, PENDING_TALKER); + check_timeout(acmp, now, PENDING_LISTENER); + check_timeout(acmp, now, PENDING_CONTROLLER); +} + +struct acmp* acmp_server_init_legacy_avb(void) +{ + struct acmp_legacy_avb *acmp_avb; + + acmp_avb = calloc(1, sizeof(*acmp_avb)); + if (acmp_avb == NULL) + return NULL; + + spa_list_init(&acmp_avb->pending[PENDING_TALKER]); + spa_list_init(&acmp_avb->pending[PENDING_LISTENER]); + spa_list_init(&acmp_avb->pending[PENDING_CONTROLLER]); + + return (struct acmp *)acmp_avb; +} + +void acmp_server_destroy_legacy_avb(struct acmp *acmp) +{ + struct acmp_legacy_avb *acmp_legacy = (struct acmp_legacy_avb *)acmp; + pending_destroy(acmp_legacy); + + free(acmp_legacy); +} diff --git a/src/modules/module-avb/acmp-cmds-resps/acmp-legacy-avb.h b/src/modules/module-avb/acmp-cmds-resps/acmp-legacy-avb.h index 2a4618d5b..89e280fbb 100644 --- a/src/modules/module-avb/acmp-cmds-resps/acmp-legacy-avb.h +++ b/src/modules/module-avb/acmp-cmds-resps/acmp-legacy-avb.h @@ -7,6 +7,13 @@ #include #include "../acmp.h" + +struct acmp* acmp_server_init_legacy_avb(void); + +void acmp_periodic_avb_legacy(void *data, uint64_t now); + +void acmp_server_destroy_legacy_avb(struct acmp *acmp); + int handle_connect_tx_command_legacy_avb(struct acmp *acmp, uint64_t now, const void *m, int len); diff --git a/src/modules/module-avb/acmp-cmds-resps/acmp-milan-v12.c b/src/modules/module-avb/acmp-cmds-resps/acmp-milan-v12.c index ed7d67703..27e6f1a18 100644 --- a/src/modules/module-avb/acmp-cmds-resps/acmp-milan-v12.c +++ b/src/modules/module-avb/acmp-cmds-resps/acmp-milan-v12.c @@ -1,3 +1,2164 @@ -#include "../acmp.h" +/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */ +/* SPDX-FileCopyrightText: Copyright © 2026 Alexandre Malki */ +/* SPDX-License-Identifier: MIT */ + + + +#include "acmp-common.h" #include "acmp-milan-v12.h" +#include "../adp.h" +#include "../aecp-aem.h" +#include "../aecp-aem-state.h" + + +#define ACMP_MILAN_V12_PBSTA_DISABLED (0U) +#define ACMP_MILAN_V12_PBSTA_PASSIVE (1U) +#define ACMP_MILAN_V12_PBSTA_ACTIVE (2U) +#define ACMP_MILAN_V12_PBSTA_COMPLETED (3U) + +#define ACMP_MILAN_TMR_DELAY(now) (now + (pw_rand32() % SPA_NSEC_PER_SEC)) +#define ACMP_MILAN_TMR_RETRY(now) (now + (4U * SPA_NSEC_PER_SEC)) +#define ACMP_MILAN_TMR_NO_TK(now) (now + (10U * SPA_NSEC_PER_SEC)) +#define ACMP_MILAN_TMR_NO_RESP(now) (now + (200U * SPA_NSEC_PER_MSEC)) + +struct listener_fsm_cmd { + int (*state_handler) (struct acmp *, + struct aecp_aem_stream_input_state_milan_v12*, + const void *, size_t, uint64_t); +}; + + +/** Milan V1.2 Section 5.5.3.5 */ +enum fsm_acmp_evt_milan_v12 { + FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP, + FSM_ACMP_EVT_MILAN_V12_TMR_RETRY, + FSM_ACMP_EVT_MILAN_V12_TMR_DELAY, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_TK, + FSM_ACMP_EVT_MILAN_V12_RCV_BIND_RX_CMD, + FSM_ACMP_EVT_MILAN_V12_RCV_PROBE_TX_RESP, + FSM_ACMP_EVT_MILAN_V12_RCV_GET_RX_STATE, + FSM_ACMP_EVT_MILAN_V12_RCV_UNBIND_RX_CMD, + FSM_ACMP_EVT_MILAN_V12_TK_DISCOVERED, + FSM_ACMP_EVT_MILAN_V12_TK_DEPARTED, + FSM_ACMP_EVT_MILAN_V12_TK_REGISTERED, + FSM_ACMP_EVT_MILAN_V12_TK_UNREGISTERED, + + FSM_ACMP_EVT_MILAN_V12_MAX, +}; + +struct acmp_lt_timers { + struct spa_list link; + + uint64_t timeout; + struct aecp_aem_stream_input_state_milan_v12 *stream; + enum fsm_acmp_evt_milan_v12 event; + + uint8_t saved_packet[512]; + size_t saved_packet_len; +}; + +static const char *fsm_acmp_evt_milan_v12_str[] = { + "FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP", + "FSM_ACMP_EVT_MILAN_V12_TMR_RETRY", + "FSM_ACMP_EVT_MILAN_V12_TMR_DELAY", + "FSM_ACMP_EVT_MILAN_V12_TMR_NO_TK", + "FSM_ACMP_EVT_MILAN_V12_RCV_BIND_RX_CMD", + "FSM_ACMP_EVT_MILAN_V12_RCV_PROBE_TX_RESP", + "FSM_ACMP_EVT_MILAN_V12_RCV_GET_RX_STATE", + "FSM_ACMP_EVT_MILAN_V12_RCV_UNBIND_RX_CMD", + "FSM_ACMP_EVT_MILAN_V12_TK_DISCOVERED", + "FSM_ACMP_EVT_MILAN_V12_TK_DEPARTED", + "FSM_ACMP_EVT_MILAN_V12_TK_REGISTERED", + "FSM_ACMP_EVT_MILAN_V12_TK_UNREGISTERED", +}; + +static const char *fsm_acmp_state_milan_v12_str[] = { + "FSM_ACMP_STATE_MILAN_V12_UNBOUND", + "FSM_ACMP_STATE_MILAN_V12_PRB_W_AVAIL", + "FSM_ACMP_STATE_MILAN_V12_PRB_W_DELAY", + "FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP", + "FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP2", + "FSM_ACMP_STATE_MILAN_V12_PRB_W_RETRY", + "FSM_ACMP_STATE_MILAN_V12_SETTLED_NO_RSV", + "FSM_ACMP_STATE_MILAN_V12_SETTLED_RSV_OK", +}; + +static struct acmp_lt_timers *acmp_lt_add_timer_milan_v12(struct acmp_milan_v12 *acmp_m, + struct aecp_aem_stream_input_state_milan_v12 *stream, + enum fsm_acmp_evt_milan_v12 event, uint64_t timeout, const void *m, size_t len) +{ + struct acmp_lt_timers *tmr; + + tmr = calloc(1, sizeof(*tmr)); + if (tmr == NULL) + return NULL; + if (m) { + memcpy(tmr->saved_packet, m, len); + tmr->saved_packet_len = len; + } + + tmr->timeout = timeout; + tmr->stream = stream; + tmr->event = event; + + spa_list_append(&acmp_m->timers_lt, &tmr->link); + + return tmr; +} + +static void acmp_list_free_element_milan_v12(struct spa_list *link, void *ptr) { + spa_list_remove(link); + free(ptr); +} + +static struct acmp_lt_timers* acmp_timer_lt_find_milan_v12(struct acmp_milan_v12 *acmp_m, + struct aecp_aem_stream_input_state_milan_v12 *stream, + enum fsm_acmp_evt_milan_v12 event) +{ + struct acmp_lt_timers *tmr; + spa_list_for_each(tmr, &acmp_m->timers_lt, link) { + if ((tmr->stream == stream) && (tmr->event == event)) { + return tmr; + } + } + + pw_log_warn("Stream %p, no timer %s", stream, fsm_acmp_evt_milan_v12_str[event]); + + return NULL; +} + +static void acmp_timer_lt_find_remove_milan_v12(struct acmp_milan_v12 *acmp_m, + struct aecp_aem_stream_input_state_milan_v12 *stream, + enum fsm_acmp_evt_milan_v12 event) +{ + struct acmp_lt_timers *tmr = + acmp_timer_lt_find_milan_v12(acmp_m, stream, event); + + if (tmr) { + acmp_list_free_element_milan_v12(&tmr->link, tmr); + } +} + +static struct acmp_lt_timers* acmp_timer_lt_register(struct acmp_milan_v12 *acmp_m, + struct aecp_aem_stream_input_state_milan_v12 *stream, + enum fsm_acmp_evt_milan_v12 event, const void *m, size_t len, + uint64_t now) +{ + struct acmp_lt_timers *tmr; + uint64_t timeout; + + switch (event) { + case FSM_ACMP_EVT_MILAN_V12_TMR_NO_TK: + timeout = ACMP_MILAN_TMR_NO_TK(now); + break; + case FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP: + timeout = ACMP_MILAN_TMR_NO_RESP(now); + break; + case FSM_ACMP_EVT_MILAN_V12_TMR_DELAY: + timeout = ACMP_MILAN_TMR_DELAY(now); + break; + case FSM_ACMP_EVT_MILAN_V12_TMR_RETRY: + timeout = ACMP_MILAN_TMR_RETRY(now); + break; + default: + pw_log_error("Invalid timer %d\n", event); + return NULL; + } + + tmr = acmp_lt_add_timer_milan_v12(acmp_m, stream, event, + timeout, m, len); + + if (!tmr) { + pw_log_error("Invalid timer creation"); + return NULL; + } + + return tmr; +} + +static void update_aem_streaming_flags(struct aecp_aem_stream_input_state_milan_v12 *stream, + uint32_t flag) +{ + uint32_t *flags = &stream->acmp_status.common.saved_bindings.aem_flags; + SPA_FLAG_UPDATE(*flags, flag, flag); +} + +/** + * \brief Helpers to avoid copy/pasting the same code again and again + * \sa Milan v1.2 Table 5.31 BIND_RX_RESPONSE + */ +static void prepare_bind_rx_response_no_match(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint8_t *outbuf) +{ + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)outbuf; + struct avb_packet_acmp *reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + + memcpy(outbuf, m, len); + AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, + AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_RESPONSE); + + AVB_PACKET_ACMP_SET_STATUS(reply, + AVB_ACMP_STATUS_CONTROLLER_NOT_AUTHORIZED); +} + +/** + * \brief Helpers to avoid copy/pasting the same code again and again + * \sa Milan v1.2 Table 5.32 BIND_RX_RESPONSE success + */ +static void prepare_bind_rx_response_success(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint8_t *outbuf) +{ + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)outbuf; + struct avb_packet_acmp *reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + uint16_t flags; + + memcpy(outbuf, m, len); + + AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, + AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_RESPONSE); + + AVB_PACKET_ACMP_SET_STATUS(reply, AVB_ACMP_STATUS_SUCCESS); + reply->connection_count = htons(1); + + flags = ntohs(reply->flags); + SPA_FLAG_CLEAR(flags, AVB_ACMP_FLAG_FAST_CONNECT); + SPA_FLAG_CLEAR(flags, AVB_ACMP_FLAG_SRP_REGISTRATION_FAILED); + + reply->flags = htons(flags); + reply->stream_id = 0; + memset(reply->stream_dest_mac, 0, sizeof(reply->stream_dest_mac)); + reply->stream_vlan_id = 0; + AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, + AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_RESPONSE); +} + +/** + * \brief Helpers to avoid copy/pasting the same code again and again + * \sa Milan v1.2 Table 5.33 BIND_RX_RESPONSE success + */ +static void prepare_probe_tx_command_success(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint8_t *outbuf) +{ + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12*) acmp; + struct server *server = acmp->server; + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)outbuf; + struct avb_packet_acmp *reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + uint16_t flags; + + memcpy(outbuf, m, len); + + AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, + AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_COMMAND); + + AVB_PACKET_ACMP_SET_STATUS(reply, AVB_ACMP_STATUS_SUCCESS); + + reply->connection_count = htons(0); + reply->controller_guid = htobe64(stream->acmp_status.common.saved_bindings.controller_guid); + reply->talker_guid = htobe64(stream->acmp_status.common.saved_bindings.talker_guid); + reply->listener_guid = htobe64(server->entity_id); + reply->talker_unique_id = htons(stream->acmp_status.common.saved_bindings.talker_unique_id); + reply->sequence_id = htons(acmp_m->sequence_id[0]); + acmp_m->sequence_id[0]++; + + flags = ntohs(reply->flags); + flags &= ~AVB_ACMP_FLAG_STREAMING_WAIT; + flags |= AVB_ACMP_FLAG_FAST_CONNECT; + flags &= ~AVB_ACMP_FLAG_SRP_REGISTRATION_FAILED; + reply->flags = htons(flags); + + reply->stream_id = 0; + memset(reply->stream_dest_mac, 0, sizeof(reply->stream_dest_mac)); + reply->stream_vlan_id = 0; +} + +/** + * \brief Helpers to avoid copy/pasting the same code again and again + * \sa Milan v1.2 Table 5.35 UNBIND_RX_RESPONSE controller_not_authorized + */ +static void prepare_unbind_rx_controller_not_authorized(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint8_t *outbuf) +{ + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)outbuf; + struct avb_packet_acmp *reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + + memcpy(outbuf, m, len); + AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, + AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_RESPONSE); + AVB_PACKET_ACMP_SET_STATUS(reply, + AVB_ACMP_STATUS_CONTROLLER_NOT_AUTHORIZED); +} + +/** + * \brief Helpers to avoid copy/pasting the same code again and again + * \sa Milan v1.2 Table 5.36 BIND_RX_RESPONSE success + */ +static void prepare_unbind_rx_response_success(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint8_t *outbuf) +{ + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)outbuf; + struct avb_packet_acmp *reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + uint16_t flags; + memcpy(outbuf, m, len); + + AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, + AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_RESPONSE); + + AVB_PACKET_ACMP_SET_STATUS(reply, AVB_ACMP_STATUS_SUCCESS); + + reply->talker_guid = 0; + reply->talker_unique_id = htons(stream->acmp_status.common.saved_bindings.talker_unique_id); + + reply->connection_count = htons(0); + + flags = ntohs(reply->flags); + flags &= ~AVB_ACMP_FLAG_STREAMING_WAIT; + flags &= ~AVB_ACMP_FLAG_FAST_CONNECT; + flags &= ~AVB_ACMP_FLAG_SRP_REGISTRATION_FAILED; + + reply->flags = htons(flags); + reply->stream_id = 0; + memset(reply->stream_dest_mac, 0, sizeof(reply->stream_dest_mac)); + reply->stream_vlan_id = 0; +} + +/** + * \brief Helpers to avoid copy/pasting the same code again and again + * \sa Milan v1.2 Table 5.37 GET_RX_STATE success + */ +static void prepare_get_rx_response_success(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint8_t *outbuf) +{ + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)outbuf; + struct avb_packet_acmp *reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + + memcpy(outbuf, m, len); + /** Prepare packet according to Milan v1.2 Table 5.34 */ + AVB_PACKET_ACMP_SET_STATUS(reply, AVB_ACMP_STATUS_SUCCESS); + AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, + AVB_ACMP_MESSAGE_TYPE_GET_RX_STATE_RESPONSE); + + memset(reply->stream_dest_mac, 0, sizeof(reply->stream_dest_mac)); + reply->talker_guid = htobe64(stream->acmp_status.common.saved_bindings.talker_guid); + reply->talker_unique_id = htons(stream->acmp_status.common.saved_bindings.talker_unique_id); + reply->connection_count = htons(1); + reply->flags |= htons(AVB_ACMP_FLAG_SRP_REGISTRATION_FAILED + | AVB_ACMP_FLAG_STREAMING_WAIT); + +} + +static bool bindings_match_message_talker(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m) +{ + const struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + const struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + bool bindings_matches; + + bindings_matches = stream->acmp_status.common.saved_bindings.talker_guid == be64toh(p->talker_guid); + bindings_matches &= stream->acmp_status.common.saved_bindings.talker_unique_id == ntohs(p->talker_unique_id); + + return bindings_matches; +} + +static void binding_save_parameters(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m) +{ + + const struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + const struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + stream->acmp_status.common.saved_bindings.controller_guid = be64toh(p->controller_guid); + stream->acmp_status.common.saved_bindings.talker_guid = be64toh(p->talker_guid); + stream->acmp_status.common.saved_bindings.listener_guid = be64toh(p->listener_guid); + stream->acmp_status.common.saved_bindings.talker_unique_id = ntohs(p->talker_unique_id); + stream->acmp_status.common.saved_bindings.listener_unique_id = ntohs(p->listener_unique_id); + stream->acmp_status.common.saved_bindings.aem_flags = ntohs(p->flags); +} + +static bool is_accessible_entity_id(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, size_t len, uint64_t now) +{ + struct server *server = acmp->server; + const struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + const struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + if (be64toh(p->listener_guid) != server->entity_id) { + pw_log_warn("entity is no accessible, either locked or does not correspond"); + return false; + } + return true; +} + +/** Milan v1.2 5.5.3.5.3 */ +int handle_fsm_unbound_rcv_bind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + const struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + const struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + uint8_t res; + + memcpy(buf, m, len); + if (!is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_bind_rx_response_no_match(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + binding_save_parameters(acmp, stream, m); + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res != 0){ + pw_log_error("tx: bind_rx_resp"); + return -1; + } + + if (adp_start_discovery_entity(server, be64toh(p->talker_guid))) { + pw_log_error("starting adp state machine"); + return -1; + } + + prepare_probe_tx_command_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: probe_tx command"); + return res; + } + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP, m, len, now)) { + spa_assert(0); + } + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP; + + return 0; +} + +/** Milan v1.2 5.5.3.5.4 */ +int handle_fsm_unbound_rcv_get_rx_state_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + prepare_get_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: get_tx resp"); + } + pw_log_info("Responding to a get_rx command %zu", len); + + return res; +} + +/** Milan v1.2 5.5.3.5.5 */ +int handle_fsm_unbound_rcv_unbind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + uint8_t res; + + memcpy(buf, m, len); + if (!is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_unbind_rx_controller_not_authorized(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + prepare_unbind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: unbind_rx resp"); + return -1; + } + + return 0; +} + +/** Milan v1.2 5.5.3.5.6 */ +int handle_fsm_prb_w_avail_rcv_bind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + uint8_t res; + + if (is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_bind_rx_response_no_match(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + if(bindings_match_message_talker(acmp, stream, m) && + (stream->acmp_status.common.saved_bindings.aem_flags & ntohs(p->flags))) { + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: bind_rx"); + } + return res; + } + + adp_stop_discovery_entity(server, be64toh(p->talker_guid)); + + + stream->acmp_status.common.saved_bindings.controller_guid = be64toh(p->controller_guid); + stream->acmp_status.common.saved_bindings.talker_guid = be64toh(p->talker_guid); + stream->acmp_status.common.saved_bindings.talker_unique_id = ntohs(p->talker_unique_id); + + stream->acmp_status.common.saved_bindings.aem_flags = + (ntohs(p->flags) & AVB_ACMP_FLAG_STREAMING_WAIT) + | stream->acmp_status.common.saved_bindings.aem_flags; + + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + + if (adp_start_discovery_entity(server, be64toh(p->talker_guid))) { + pw_log_warn("Starting the ADP discovery FSM for 0x%"PRIx64, + be64toh(p->talker_guid)); + } + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP, m, len, now)) { + spa_assert(0); + } + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP; + + return 0; +} + +/** Milan v1.2 5.5.3.5.7 */ +int handle_fsm_prb_w_avail_rcv_get_rx_state_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + prepare_get_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: get_tx resp"); + } + pw_log_info("Responding to a get_rx command %zu", len); + + return res; +} + +/** Milan v1.2 5.5.3.5.8 */ +int handle_fsm_prb_w_avail_rcv_unbind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + if (is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_bind_rx_response_no_match(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + adp_stop_discovery_entity(server, be64toh(p->talker_guid)); + + memset(&stream->acmp_status.common.saved_bindings, 0, + sizeof(stream->acmp_status.common.saved_bindings)); + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_DISABLED; + stream->acmp_status.common.acmp_status = 0; + + prepare_unbind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_UNBOUND; + + return res; +} + +/** Milan v1.2 5.5.3.5.9 */ +int handle_fsm_prb_w_avail_evt_tk_discovered_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_DELAY, m, len, now)) { + spa_assert(0); + } + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + stream->acmp_status.common.acmp_status = 0; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP; + + return 0; +} + +/** Milan v1.2 5.5.3.5.10 */ +int handle_fsm_prb_w_delay_tmr_delay_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + prepare_probe_tx_command_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, + h_reply, len); + if (res) { + pw_log_error("tx: probe_tx command"); + return res; + } + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP, m, len, now)) { + spa_assert(0); + } + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP; + + return 0; +} + +/** Milan v1.2 5.5.3.5.11 */ +int handle_fsm_prb_w_delay_rcv_bind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + if (is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_bind_rx_response_no_match(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + if(bindings_match_message_talker(acmp, stream, m) && + (stream->acmp_status.common.saved_bindings.aem_flags & ntohs(p->flags))) { + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + + return res; + } + + adp_stop_discovery_entity(server, be64toh(p->talker_guid)); + + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, FSM_ACMP_EVT_MILAN_V12_TMR_DELAY); + + stream->acmp_status.common.saved_bindings.controller_guid = be64toh(p->controller_guid); + stream->acmp_status.common.saved_bindings.talker_guid = be64toh(p->talker_guid); + stream->acmp_status.common.saved_bindings.talker_unique_id = ntohs(p->talker_unique_id); + + update_aem_streaming_flags(stream, + ntohs(p->flags) & AVB_ACMP_FLAG_STREAMING_WAIT); + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + + if (adp_start_discovery_entity(server, be64toh(p->talker_guid))) { + pw_log_warn("Starting the ADP discovery FSM for 0x%"PRIx64, + be64toh(p->talker_guid)); + } + + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP, m, len, now)) { + spa_assert(0); + } + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + stream->acmp_status.common.acmp_status = 0; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP; + + return 0; +} + +/** Milan v1.2 5.5.3.5.12 */ +int handle_fsm_prb_w_delay_rcv_get_rx_state_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + prepare_get_rx_response_success(acmp, stream, m, len, buf); + //FIXME the packet shall look like Table 5.37 + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: get_tx resp"); + } + pw_log_info("Responding to a get_rx command %zu", len); + + return res; +} + +/** Milan v1.2 5.5.3.5.13 */ +int handle_fsm_prb_w_delay_rcv_unbind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + if (is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_bind_rx_response_no_match(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + adp_stop_discovery_entity(server, be64toh(p->talker_guid)); + + memset(&stream->acmp_status.common.saved_bindings, 0, + sizeof(stream->acmp_status.common.saved_bindings)); + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_DISABLED; + stream->acmp_status.common.acmp_status = 0; + + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, FSM_ACMP_EVT_MILAN_V12_TMR_DELAY); + + prepare_unbind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_UNBOUND; + + return 0; +} + +/** Milan v1.2 5.5.3.5.14 */ +int handle_fsm_prb_w_delay_evt_tk_discovered_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + pw_log_info("rx: tk_disscovered event"); + return 0; +} + +/** Milan v1.2 5.5.3.5.15 */ +int handle_fsm_prb_w_delay_evt_tk_departed_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + pw_log_info("rx: tk_departed event"); + + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_DELAY); + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_PASSIVE; + stream->acmp_status.common.acmp_status = 0; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_AVAIL; + + return 0; +} + +/** Milan v1.2 5.5.3.5.16 */ +int handle_fsm_prb_w_resp_tmr_no_resp_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct server *server = acmp->server; + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + prepare_probe_tx_command_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP, m, len, now)) { + spa_assert(0); + } + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP2; + + return res; +} + +/** Milan v1.2 5.5.3.5.17 */ +int handle_fsm_prb_w_resp_rcv_bind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct server *server = acmp->server; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + if (!is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_unbind_rx_controller_not_authorized(acmp, stream, m, + len, buf); + res = avb_server_send_packet(server, h_reply->dest, + AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + if(bindings_match_message_talker(acmp, stream, m) && + (stream->acmp_status.common.saved_bindings.aem_flags & ntohs(p->flags))) { + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, + AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: bind_rx"); + } + return res; + } + + adp_stop_discovery_entity(server, be64toh(p->talker_guid)); + + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP); + + stream->acmp_status.common.saved_bindings.controller_guid = be64toh(p->controller_guid); + stream->acmp_status.common.saved_bindings.talker_guid = be64toh(p->talker_guid); + stream->acmp_status.common.saved_bindings.talker_unique_id = ntohs(p->talker_unique_id); + + update_aem_streaming_flags(stream, + ntohs(p->flags) & AVB_ACMP_FLAG_STREAMING_WAIT); + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, + h_reply, len); + + if (res) { + pw_log_error("Sending no accessible entity"); + } + + if (adp_start_discovery_entity(server, be64toh(p->talker_guid))) { + pw_log_error("starting adp state machine"); + return -1; + } + + prepare_probe_tx_command_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: probe_tx command"); + return res; + } + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP, m, len, now)) { + spa_assert(0); + } + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + stream->acmp_status.common.acmp_status = 0; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP; + + return 0; +} + +/** Milan v1.2, Sec. 5.5.3.5.18 */ +int handle_fsm_prb_w_resp_rcv_probe_tx_resp_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct server *server = acmp->server; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + if (!is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_unbind_rx_controller_not_authorized(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP); + + if (AVB_PACKET_ACMP_GET_STATUS(p) != AVB_ACMP_STATUS_SUCCESS) { + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_RETRY, + m, len, now)){ + spa_assert(0); + } + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_RETRY; + stream->acmp_status.common.acmp_status = AVB_PACKET_ACMP_GET_STATUS(p); + + return 0; + } + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_TK, m, len, now)){ + spa_assert(0); + } + + stream->acmp_status.common.saved_bindings.controller_guid = be64toh(p->controller_guid); + stream->acmp_status.common.saved_bindings.talker_guid = be64toh(p->talker_guid); + stream->acmp_status.common.saved_bindings.talker_unique_id = ntohs(p->talker_unique_id); + stream->acmp_status.common.saved_bindings.talker_unique_id = ntohs(p->talker_unique_id); + + memcpy(stream->acmp_status.common.stream_dest_mac, p->stream_dest_mac, + sizeof(p->stream_dest_mac)); + stream->acmp_status.common.stream_vlanid = ntohs(p->stream_vlan_id); + + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_COMPLETED; + stream->acmp_status.common.acmp_status = 0; + + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_SETTLED_NO_RSV; + + return 0; +} + +/** Milan v1.2 5.5.3.5.19 */ +int handle_fsm_prb_w_resp_rcv_get_rx_state_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + prepare_get_rx_response_success(acmp, stream, m, len, buf); + //FIXME the packet shall look like Table 5.37 + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: get_tx resp"); + } + pw_log_info("Responding to a get_rx command %zu", len); + + return res; +} + +/** Milan v1.2 5.5.3.5.20 */ +int handle_fsm_prb_w_resp_rcv_unbind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct server *server = acmp->server; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + if (!is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_unbind_rx_controller_not_authorized(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + adp_stop_discovery_entity(server, be64toh(p->talker_guid)); + memset(&stream->acmp_status.common.saved_bindings, 0, + sizeof(stream->acmp_status.common.saved_bindings)); + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_DISABLED; + stream->acmp_status.common.acmp_status = 0; + + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP); + + prepare_unbind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, + h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_UNBOUND; + + return res; +} + +/** Milan v1.2 5.5.3.5.21 */ +int handle_fsm_prb_w_resp_evt_tk_discovered_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + pw_log_info("rx: tk_discovered event"); + return 0; +} + +/** Milan v1.2 5.5.3.5.22 */ +int handle_fsm_prb_w_resp_evt_tk_departed_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + pw_log_info("rx: tk_departed event"); + + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP); + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_PASSIVE; + stream->acmp_status.common.acmp_status = 0; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_AVAIL; + + return 0; +} + +/** Milan v1.2 5.5.3.5.23 */ +int handle_fsm_prb_w_resp2_tmr_no_resp_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_RETRY, m, len, now)){ + spa_assert(0); + } + + stream->acmp_status.common.acmp_status = AVB_ACMP_STATUS_LISTENER_TALKER_TIMEOUT; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_RETRY; + + return 0; +} + +/** Milan v1.2 5.5.3.5.24 */ +int handle_fsm_prb_w_resp2_rcv_bind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + return handle_fsm_prb_w_resp_rcv_bind_rx_cmd_evt(acmp, stream, m, len, now); +} + +/** Milan v1.2 5.5.3.5.25 */ +int handle_fsm_prb_w_resp2_rcv_probe_tx_resp_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + return handle_fsm_prb_w_resp_rcv_probe_tx_resp_evt(acmp, stream, m, len, now); +} + +/** Milan v1.2 5.5.3.5.26 */ +int handle_fsm_prb_w_resp2_rcv_get_rx_state_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + return handle_fsm_prb_w_resp_rcv_get_rx_state_evt(acmp, stream, m, len, now); +} + +/** Milan v1.2 5.5.3.5.27 */ +int handle_fsm_prb_w_resp2_rcv_unbind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + return handle_fsm_prb_w_resp_rcv_unbind_rx_cmd_evt(acmp, stream, m, len, now); +} + +/** Milan v1.2 5.5.3.5.28 */ +int handle_fsm_prb_w_resp2_evt_tk_discovered_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + return handle_fsm_prb_w_resp_evt_tk_discovered_evt(acmp, stream, m, len, now); +} + +/** Milan v1.2 5.5.3.5.29 */ +int handle_fsm_prb_w_resp2_evt_tk_departed_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + return handle_fsm_prb_w_resp_evt_tk_departed_evt(acmp, stream, m, len, now); +} + +/** Milan v1.2 5.5.3.5.30 */ +int handle_fsm_prb_w_retry_tmr_retry_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + struct server *server = acmp->server; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + if (!adp_is_discovered_entity(server, be64toh(p->talker_guid))) { + + stream->acmp_status.common.probing_status = + ACMP_MILAN_V12_PBSTA_PASSIVE; + + stream->acmp_status.common.acmp_status = 0; + stream->acmp_status.fsm_acmp_state = + FSM_ACMP_STATE_MILAN_V12_PRB_W_DELAY; + + } else { + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_DELAY, m, len, now)){ + spa_assert(0); + } + } + + return 0; +} + +/** Milan v1.2 5.5.3.5.31 */ +int handle_fsm_prb_w_retry_rcv_bind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct server *server = acmp->server; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + if (!is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_unbind_rx_controller_not_authorized(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + if(bindings_match_message_talker(acmp, stream, m) && + ((stream->acmp_status.common.saved_bindings.aem_flags & ntohs(p->flags) + & AVB_ACMP_FLAG_STREAMING_WAIT))) { + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: bind_rx"); + } + return res; + } + + adp_stop_discovery_entity(server, be64toh(p->talker_guid)); + + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, FSM_ACMP_EVT_MILAN_V12_TMR_RETRY); + + stream->acmp_status.common.saved_bindings.controller_guid = be64toh(p->controller_guid); + stream->acmp_status.common.saved_bindings.talker_guid = be64toh(p->talker_guid); + stream->acmp_status.common.saved_bindings.talker_unique_id = ntohs(p->talker_unique_id); + update_aem_streaming_flags(stream, + ntohs(p->flags) & AVB_ACMP_FLAG_STREAMING_WAIT); + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res != 0){ + pw_log_error("tx: bind_rx_resp"); + return -1; + } + + if (adp_start_discovery_entity(server, be64toh(p->talker_guid))) { + pw_log_error("starting adp state machine"); + return -1; + } + + prepare_probe_tx_command_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: probe_tx command"); + return res; + } + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP, m, len, now)) { + spa_assert(0); + } + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + stream->acmp_status.common.acmp_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP; + + return 0; +} + +/** Milan v1.2 5.5.3.5.32 */ +int handle_fsm_prb_w_retry_rcv_get_rx_state_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + prepare_get_rx_response_success(acmp, stream, m, len, buf); + //FIXME the packet shall look like Table 5.37 + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: get_tx resp"); + } + pw_log_info("Responding to a get_rx_command %zu", len); + + return res; +} + +/** Milan v1.2 5.5.3.5.33 */ +int handle_fsm_prb_w_retry_rcv_unbind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + if (!is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_unbind_rx_controller_not_authorized(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + adp_stop_discovery_entity(server, be64toh(p->talker_guid)); + + memset(&stream->acmp_status.common.saved_bindings, 0, + sizeof(stream->acmp_status.common.saved_bindings)); + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + stream->acmp_status.common.acmp_status = 0; + + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, FSM_ACMP_EVT_MILAN_V12_TMR_RETRY); + + prepare_unbind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: unbind_rx resp"); + return -1; + } + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_UNBOUND; + + return 0; +} + +/** Milan v1.2 5.5.3.5.34 */ +int handle_fsm_prb_w_retry_evt_tk_discovered_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + pw_log_info("rx: tk_discovered event"); + + return 0; +} + +/** Milan v1.2 5.5.3.5.35 */ +int handle_fsm_prb_w_retry_evt_tk_departed_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + pw_log_info("rx: tk_discovered event"); + + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, FSM_ACMP_EVT_MILAN_V12_TMR_RETRY); + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + stream->acmp_status.common.acmp_status = 0; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_AVAIL; + + return 0; +} + +/** Milan v1.2 5.5.3.5.36 */ +int handle_fsm_settled_no_rsv_tmr_no_tk_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + if (!adp_is_discovered_entity(acmp->server, be64toh(p->talker_guid))) { + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_PASSIVE; + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_AVAIL; + } else { + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_DELAY; + } + + stream->acmp_status.common.acmp_status = 0; + + return 0; +} + +/** Milan v1.2 5.5.3.5.37 */ +int handle_fsm_settled_no_rsv_rcv_bind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct server *server = acmp->server; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + uint8_t res; + + if (is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_bind_rx_response_no_match(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + if(bindings_match_message_talker(acmp, stream, m) && + ((stream->acmp_status.common.saved_bindings.aem_flags & ntohs(p->flags) + & AVB_ACMP_FLAG_STREAMING_WAIT))) { + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: bind_rx"); + } + return res; + } + + adp_stop_discovery_entity(server, be64toh(p->talker_guid)); + + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, FSM_ACMP_EVT_MILAN_V12_TMR_NO_TK); + + stream->acmp_status.common.saved_bindings.controller_guid = be64toh(p->controller_guid); + stream->acmp_status.common.saved_bindings.talker_guid = be64toh(p->talker_guid); + stream->acmp_status.common.saved_bindings.talker_unique_id = ntohs(p->talker_unique_id); + update_aem_streaming_flags(stream, + ntohs(p->flags) & AVB_ACMP_FLAG_STREAMING_WAIT); + + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + + if (adp_start_discovery_entity(server, be64toh(p->talker_guid))) { + pw_log_error("starting adp state machine"); + return -1; + } + + prepare_probe_tx_command_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: probe_tx command"); + return res; + } + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP, m, len, now)) { + spa_assert(0); + } + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + stream->acmp_status.common.acmp_status = 0; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP; + return 0; +} + +/** Milan v1.2 5.5.3.5.38 */ +int handle_fsm_settled_no_rsv_rcv_get_rx_state_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + struct avb_packet_acmp *reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + int res; + + prepare_get_rx_response_success(acmp, stream, m, len, buf); + reply->flags &= htons(~(AVB_ACMP_FLAG_SRP_REGISTRATION_FAILED)); + reply->flags |= htons(AVB_ACMP_FLAG_FAST_CONNECT); + + reply->flags |= htons(stream->acmp_status.common.saved_bindings.aem_flags + & AVB_ACMP_FLAG_STREAMING_WAIT); + + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: get_tx resp"); + } + pw_log_info("Responding to a get_rx_command %zu", len); + + // TODO take the SRP information of the stream TABLE 5.38 for hte packet + return res; +} + +/** Milan v1.2 5.5.3.5.39 */ +int handle_fsm_settled_no_rsv_rcv_unbind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct server *server = acmp->server; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + if (!is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_unbind_rx_controller_not_authorized(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + + adp_stop_discovery_entity(server, be64toh(p->talker_guid)); + + memset(&stream->acmp_status.common.saved_bindings, 0, + sizeof(stream->acmp_status.common.saved_bindings)); + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_DISABLED; + stream->acmp_status.common.acmp_status = 0; + + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_TK); + + prepare_unbind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: unbind_rx resp"); + return -1; + } + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_UNBOUND; + + return 0; +} + +/** Milan v1.2 5.5.3.5.40 */ +int handle_fsm_settled_no_rsv_evt_tk_discovered_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + pw_log_info("discovered entity\n"); + return 0; +} + +/** Milan v1.2 5.5.3.5.41 */ +int handle_fsm_settled_no_rsv_evt_tk_departed_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + pw_log_info("departed entity\n"); + return 0; +} + +/** Milan v1.2, Sec. 5.5.3.5.42 */ +int handle_fsm_settled_no_rsv_evt_tk_registered_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + acmp_timer_lt_find_remove_milan_v12(acmp_m, stream, FSM_ACMP_EVT_MILAN_V12_TMR_NO_TK); + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_SETTLED_RSV_OK; + + return 0; +} + +/** Milan v1.2 5.5.3.5.43 */ +int handle_fsm_settled_rsv_ok_rcv_bind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct server *server = acmp->server; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + if (!is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_unbind_rx_controller_not_authorized(acmp, stream, m, + len, buf); + res = avb_server_send_packet(server, h_reply->dest, + AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + if(bindings_match_message_talker(acmp, stream, m) && + ((stream->acmp_status.common.saved_bindings.aem_flags + & ntohs(p->flags) & AVB_ACMP_FLAG_STREAMING_WAIT))) { + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: bind_rx"); + } + return res; + } + + + adp_stop_discovery_entity(server, be64toh(p->talker_guid)); + + stream->acmp_status.common.saved_bindings.controller_guid = be64toh(p->controller_guid); + stream->acmp_status.common.saved_bindings.talker_guid = be64toh(p->talker_guid); + stream->acmp_status.common.saved_bindings.talker_unique_id = ntohs(p->talker_unique_id); + + + prepare_bind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res != 0){ + pw_log_error("tx: bind_rx_resp"); + return -1; + } + + if (adp_start_discovery_entity(server, be64toh(p->talker_guid))) { + pw_log_error("starting adp state machine"); + return -1; + } + + prepare_probe_tx_command_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: probe_tx command"); + return res; + } + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP, m, len, now)) { + spa_assert(0); + } + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + stream->acmp_status.common.acmp_status = 0; + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP; + + return 0; +} + +/** Milan v1.2 5.5.3.5.44 */ +int handle_fsm_settled_rsv_ok_rcv_get_rx_state_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + struct avb_packet_acmp *reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void); + int res; + + prepare_get_rx_response_success(acmp, stream, m, len, buf); + reply->flags &= htons(~(AVB_ACMP_FLAG_SRP_REGISTRATION_FAILED)); + reply->flags |= htons(AVB_ACMP_FLAG_FAST_CONNECT); + reply->flags |= htons(stream->acmp_status.common.saved_bindings.aem_flags + & AVB_ACMP_FLAG_STREAMING_WAIT); + + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("tx: get_tx resp"); + } + pw_log_info("Responding to a get_rx_command %zu", len); + + // TODO take the SRP information of the stream TABLE 5.40 for the packet + + return res; +} + +/** Milan v1.2, Sec. 5.5.3.5.45 */ +int handle_fsm_settled_rsv_ok_rcv_unbind_rx_cmd_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + uint8_t buf[512]; + struct server *server = acmp->server; + struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + struct avb_ethernet_header *h_reply = (struct avb_ethernet_header *)buf; + int res; + + if (!is_accessible_entity_id(acmp, stream, m, len, now)) { + prepare_unbind_rx_controller_not_authorized(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + return res; + } + + adp_stop_discovery_entity(acmp->server, be64toh(p->talker_guid)); + + memset(&stream->acmp_status.common.saved_bindings, 0, + sizeof(stream->acmp_status.common.saved_bindings)); + + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_DISABLED; + stream->acmp_status.common.acmp_status = 0; + + prepare_unbind_rx_response_success(acmp, stream, m, len, buf); + res = avb_server_send_packet(server, h_reply->dest, AVB_TSN_ETH, h_reply, len); + if (res) { + pw_log_error("Sending no accessible entity"); + } + + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_UNBOUND; + + return 0; +} + +/** Milan v1.2 5.5.3.5.46 */ +int handle_fsm_settled_rsv_ok_evt_tk_discovered_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + pw_log_info("rx: tk_discovered"); + return 0; +} + +/** Milan v1.2 5.5.3.5.47 */ +int handle_fsm_settled_rsv_ok_evt_tk_departed_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + pw_log_info("rx: tk_departed"); + return 0; +} + +/** Milan v1.2 5.5.3.5.48 */ +int handle_fsm_settled_rsv_ok_evt_tk_unregistered_evt(struct acmp *acmp, + struct aecp_aem_stream_input_state_milan_v12 *stream, const void *m, + size_t len, uint64_t now) +{ + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + uint64_t talker_guid; + + talker_guid = stream->acmp_status.common.saved_bindings.talker_guid; + if (!adp_is_discovered_entity(acmp->server, talker_guid)) { + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_PASSIVE; + stream->acmp_status.common.acmp_status = 0; + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_AVAIL; + } else { + stream->acmp_status.common.probing_status = ACMP_MILAN_V12_PBSTA_ACTIVE; + stream->acmp_status.common.acmp_status = 0; + stream->acmp_status.fsm_acmp_state = FSM_ACMP_STATE_MILAN_V12_PRB_W_DELAY; + + if (!acmp_timer_lt_register(acmp_m, stream, + FSM_ACMP_EVT_MILAN_V12_TMR_DELAY, m, len, now)) { + spa_assert(0); + } + } + + return 0; +} + +static const struct listener_fsm_cmd listener_unbound[FSM_ACMP_EVT_MILAN_V12_MAX] = { + /* Milan v1.2, Sec. 5.5.3.5.3 */ + [FSM_ACMP_EVT_MILAN_V12_RCV_BIND_RX_CMD] = { + .state_handler = handle_fsm_unbound_rcv_bind_rx_cmd_evt }, + + [FSM_ACMP_EVT_MILAN_V12_RCV_GET_RX_STATE] = { + .state_handler = handle_fsm_unbound_rcv_get_rx_state_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_UNBIND_RX_CMD] = { + .state_handler = handle_fsm_unbound_rcv_unbind_rx_cmd_evt}, +}; + +static const struct listener_fsm_cmd listener_prb_w_avail[FSM_ACMP_EVT_MILAN_V12_MAX] = { + [FSM_ACMP_EVT_MILAN_V12_RCV_BIND_RX_CMD] = { + .state_handler = handle_fsm_prb_w_avail_rcv_bind_rx_cmd_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_GET_RX_STATE] = { + .state_handler = handle_fsm_prb_w_avail_rcv_get_rx_state_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_UNBIND_RX_CMD] = { + .state_handler = handle_fsm_prb_w_avail_rcv_unbind_rx_cmd_evt }, + + [FSM_ACMP_EVT_MILAN_V12_TK_DISCOVERED] = { + .state_handler = handle_fsm_prb_w_avail_evt_tk_discovered_evt }, +}; + +static const struct listener_fsm_cmd listener_prb_w_delay[FSM_ACMP_EVT_MILAN_V12_MAX] = { + [FSM_ACMP_EVT_MILAN_V12_TMR_DELAY] = { + .state_handler = handle_fsm_prb_w_delay_tmr_delay_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_BIND_RX_CMD] = { + .state_handler = handle_fsm_prb_w_delay_rcv_bind_rx_cmd_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_GET_RX_STATE] = { + .state_handler = handle_fsm_prb_w_delay_rcv_get_rx_state_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_UNBIND_RX_CMD]{ + .state_handler = handle_fsm_prb_w_delay_rcv_unbind_rx_cmd_evt}, + [FSM_ACMP_EVT_MILAN_V12_TK_DISCOVERED]{ + .state_handler = handle_fsm_prb_w_delay_evt_tk_discovered_evt}, + [FSM_ACMP_EVT_MILAN_V12_TK_DEPARTED]{ + .state_handler = handle_fsm_prb_w_delay_evt_tk_departed_evt}, +}; + +static const struct listener_fsm_cmd listener_prb_w_resp[FSM_ACMP_EVT_MILAN_V12_MAX] = { + [FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP]{ + .state_handler = handle_fsm_prb_w_resp_tmr_no_resp_evt}, + [FSM_ACMP_EVT_MILAN_V12_RCV_BIND_RX_CMD]{ + .state_handler = handle_fsm_prb_w_resp_rcv_bind_rx_cmd_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_PROBE_TX_RESP]{ + .state_handler = handle_fsm_prb_w_resp_rcv_probe_tx_resp_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_GET_RX_STATE]{ + .state_handler = handle_fsm_prb_w_resp_rcv_get_rx_state_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_UNBIND_RX_CMD]{ + .state_handler = handle_fsm_prb_w_resp_rcv_unbind_rx_cmd_evt}, + + [FSM_ACMP_EVT_MILAN_V12_TK_DISCOVERED]{ + .state_handler = handle_fsm_prb_w_resp_evt_tk_discovered_evt}, + + [FSM_ACMP_EVT_MILAN_V12_TK_DEPARTED]{ + .state_handler = handle_fsm_prb_w_resp_evt_tk_departed_evt}, +}; + +static const struct listener_fsm_cmd listener_prb_w_resp2[FSM_ACMP_EVT_MILAN_V12_MAX] = { + [FSM_ACMP_EVT_MILAN_V12_TMR_NO_RESP]{ + .state_handler = handle_fsm_prb_w_resp2_tmr_no_resp_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_BIND_RX_CMD]{ + .state_handler = handle_fsm_prb_w_resp2_rcv_bind_rx_cmd_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_PROBE_TX_RESP]{ + .state_handler = handle_fsm_prb_w_resp2_rcv_probe_tx_resp_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_GET_RX_STATE]{ + .state_handler = handle_fsm_prb_w_resp2_rcv_get_rx_state_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_UNBIND_RX_CMD]{ + .state_handler = handle_fsm_prb_w_resp2_rcv_unbind_rx_cmd_evt}, + + [FSM_ACMP_EVT_MILAN_V12_TK_DISCOVERED]{ + .state_handler = handle_fsm_prb_w_resp2_evt_tk_discovered_evt}, + + [FSM_ACMP_EVT_MILAN_V12_TK_DEPARTED]{ + .state_handler = handle_fsm_prb_w_resp2_evt_tk_departed_evt}, +}; + +static const struct listener_fsm_cmd listener_prb_w_retry[FSM_ACMP_EVT_MILAN_V12_MAX] = { + [FSM_ACMP_EVT_MILAN_V12_TMR_RETRY]{ + .state_handler = handle_fsm_prb_w_retry_tmr_retry_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_BIND_RX_CMD]{ + .state_handler = handle_fsm_prb_w_retry_rcv_bind_rx_cmd_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_GET_RX_STATE]{ + .state_handler = handle_fsm_prb_w_retry_rcv_get_rx_state_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_UNBIND_RX_CMD]{ + .state_handler = handle_fsm_prb_w_retry_rcv_unbind_rx_cmd_evt}, + + [FSM_ACMP_EVT_MILAN_V12_TK_DISCOVERED]{ + .state_handler = handle_fsm_prb_w_retry_evt_tk_discovered_evt}, + + [FSM_ACMP_EVT_MILAN_V12_TK_DEPARTED]{ + .state_handler = handle_fsm_prb_w_retry_evt_tk_departed_evt}, +}; + +static const struct listener_fsm_cmd listener_settled_no_rsv[FSM_ACMP_EVT_MILAN_V12_MAX] = { + [FSM_ACMP_EVT_MILAN_V12_TMR_NO_TK]{ + .state_handler = handle_fsm_settled_no_rsv_tmr_no_tk_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_BIND_RX_CMD]{ + .state_handler = handle_fsm_settled_no_rsv_rcv_bind_rx_cmd_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_GET_RX_STATE]{ + .state_handler = handle_fsm_settled_no_rsv_rcv_get_rx_state_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_UNBIND_RX_CMD]{ + .state_handler = handle_fsm_settled_no_rsv_rcv_unbind_rx_cmd_evt}, + + [FSM_ACMP_EVT_MILAN_V12_TK_DISCOVERED]{ + .state_handler = handle_fsm_settled_no_rsv_evt_tk_discovered_evt}, + + [FSM_ACMP_EVT_MILAN_V12_TK_DEPARTED]{ + .state_handler = handle_fsm_settled_no_rsv_evt_tk_departed_evt}, + + [FSM_ACMP_EVT_MILAN_V12_TK_REGISTERED]{ + .state_handler = handle_fsm_settled_no_rsv_evt_tk_registered_evt}, + +}; + +static const struct listener_fsm_cmd listener_settled_rsv_ok[FSM_ACMP_EVT_MILAN_V12_MAX] = { + [FSM_ACMP_EVT_MILAN_V12_RCV_BIND_RX_CMD]{ + .state_handler = handle_fsm_settled_rsv_ok_rcv_bind_rx_cmd_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_GET_RX_STATE]{ + .state_handler = handle_fsm_settled_rsv_ok_rcv_get_rx_state_evt}, + + [FSM_ACMP_EVT_MILAN_V12_RCV_UNBIND_RX_CMD]{ + .state_handler = handle_fsm_settled_rsv_ok_rcv_unbind_rx_cmd_evt}, + + [FSM_ACMP_EVT_MILAN_V12_TK_DISCOVERED]{ + .state_handler = handle_fsm_settled_rsv_ok_evt_tk_discovered_evt}, + + [FSM_ACMP_EVT_MILAN_V12_TK_DEPARTED]{ + .state_handler = handle_fsm_settled_rsv_ok_evt_tk_departed_evt}, + + [FSM_ACMP_EVT_MILAN_V12_TK_UNREGISTERED]{ + .state_handler = handle_fsm_settled_rsv_ok_evt_tk_unregistered_evt}, +}; + +static const struct listener_fsm_cmd *cmd_listeners_states[FSM_ACMP_STATE_MILAN_V12_MAX] = { + [FSM_ACMP_STATE_MILAN_V12_UNBOUND] = listener_unbound, + [FSM_ACMP_STATE_MILAN_V12_PRB_W_AVAIL] = listener_prb_w_avail, + [FSM_ACMP_STATE_MILAN_V12_PRB_W_DELAY] = listener_prb_w_delay, + [FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP] = listener_prb_w_resp, + [FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP2] = listener_prb_w_resp2, + [FSM_ACMP_STATE_MILAN_V12_PRB_W_RETRY] = listener_prb_w_retry, + [FSM_ACMP_STATE_MILAN_V12_SETTLED_NO_RSV] = listener_settled_no_rsv, + [FSM_ACMP_STATE_MILAN_V12_SETTLED_RSV_OK] = listener_settled_rsv_ok, +}; + +static int acmp_generic_command_lt_handler_milan_v12(struct acmp *acmp, + uint64_t now, const void *m, int len, enum fsm_acmp_evt_milan_v12 event) +{ + const struct avb_ethernet_header *h = (struct avb_ethernet_header *)m; + const struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + + const struct listener_fsm_cmd *cmd; + struct aecp_aem_stream_input_state_milan_v12 *si_state; + struct descriptor *desc; + uint16_t desc_type = AVB_AEM_DESC_STREAM_INPUT; + uint16_t desc_index = ntohs(p->listener_unique_id); + + desc = server_find_descriptor(acmp->server, desc_type, desc_index); + if (desc == NULL) + return -1; + + si_state = (struct aecp_aem_stream_input_state_milan_v12 *)desc->ptr; + cmd = &cmd_listeners_states[si_state->acmp_status.fsm_acmp_state][event]; + if (!cmd) { + pw_log_error("transition STATE:%s EVT:%s", + fsm_acmp_state_milan_v12_str[si_state->acmp_status.fsm_acmp_state], + fsm_acmp_evt_milan_v12_str[event]); + return -1; + } + + return cmd->state_handler(acmp, si_state, m, len, now); +} + +static int acmp_generic_timer_handler_milan_v12(struct acmp *acmp, uint64_t now, + struct acmp_lt_timers *tmr) +{ + const struct listener_fsm_cmd *cmd; + struct aecp_aem_stream_input_state_milan_v12 *si_state; + enum fsm_acmp_evt_milan_v12 event = tmr->event; + + + si_state = (struct aecp_aem_stream_input_state_milan_v12 *)tmr->stream; + cmd = &cmd_listeners_states[si_state->acmp_status.fsm_acmp_state][event]; + if (!cmd) { + pw_log_error("transition STATE:%s EVT:%s", + fsm_acmp_state_milan_v12_str[si_state->acmp_status.fsm_acmp_state], + fsm_acmp_evt_milan_v12_str[event]); + return -1; + } + + return cmd->state_handler(acmp, si_state, tmr->saved_packet, tmr->saved_packet_len, now); +} + +static int acmp_generic_adp_srp_evt_lt_handler_milan_v12(struct acmp *acmp, + uint64_t entity_id, enum fsm_acmp_evt_milan_v12 event) +{ + const struct listener_fsm_cmd *cmd; + struct aecp_aem_stream_input_state_milan_v12 *si_state; + struct descriptor *desc; + enum fsm_acmp_state_milan_v12 fsm_state; + uint16_t desc_type = AVB_AEM_DESC_STREAM_INPUT; + int rc; + + for (uint16_t desc_index = 0; desc_index < UINT16_MAX; desc_index++) { + desc = server_find_descriptor(acmp->server, desc_type, desc_index); + if (desc == NULL) + break; + + si_state = (struct aecp_aem_stream_input_state_milan_v12 *)desc->ptr; + fsm_state = si_state->acmp_status.fsm_acmp_state; + cmd = &cmd_listeners_states[fsm_state][event]; + + if (!cmd) { + pw_log_warn("Invalid transition STATE:%s EVT:%s - ignoring", + fsm_acmp_state_milan_v12_str[fsm_state], + fsm_acmp_evt_milan_v12_str[event]); + continue; + } + + rc = cmd->state_handler(acmp, si_state, NULL, 0, 0); + if (rc){ + pw_log_error("cmd failed for stream %p", si_state); + } + } + + return 0; +} + +#if 0 +static int acmp_generic_srp_evt_handler_milan_v12(struct acmp *acmp, uint64_t now, + uint64_t entity_id, enum fsm_acmp_evt_milan_v12 event) +{ + return 0; +} +#endif + +int acmp_init_talker_stream_milan_v12(struct acmp *acmp, void *acmp_status) +{ + return 0; +} + +int acmp_init_listener_stream_milan_v12(struct acmp *acmp, void *acmp_status) +{ + return 0; +} + + + +int handle_probe_tx_response_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len) +{ + return acmp_generic_command_lt_handler_milan_v12(acmp, now, m, len, + FSM_ACMP_EVT_MILAN_V12_RCV_PROBE_TX_RESP); +} + + +int handle_bind_rx_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len) +{ + return acmp_generic_command_lt_handler_milan_v12(acmp, now, m, len, + FSM_ACMP_EVT_MILAN_V12_RCV_BIND_RX_CMD); +} + +int handle_unbind_rx_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len) +{ + return acmp_generic_command_lt_handler_milan_v12(acmp, now, m, len, + FSM_ACMP_EVT_MILAN_V12_RCV_UNBIND_RX_CMD); +} + +int handle_get_rx_state_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len) +{ + return acmp_generic_command_lt_handler_milan_v12(acmp, now, m, len, + FSM_ACMP_EVT_MILAN_V12_RCV_GET_RX_STATE); +} + +int handle_evt_tk_discovered_milan_v12(struct acmp *acmp, uint64_t talker_guid) +{ + return acmp_generic_adp_srp_evt_lt_handler_milan_v12(acmp, talker_guid, + FSM_ACMP_EVT_MILAN_V12_TK_DISCOVERED); +} + +int handle_evt_tk_departed_milan_v12(struct acmp *acmp, uint64_t talker_guid) +{ + return acmp_generic_adp_srp_evt_lt_handler_milan_v12(acmp, talker_guid, + FSM_ACMP_EVT_MILAN_V12_TK_DEPARTED); +} + +int handle_evt_tk_registered_milan_v12(struct acmp *acmp, uint64_t talker_guid) +{ + return acmp_generic_adp_srp_evt_lt_handler_milan_v12(acmp, talker_guid, + FSM_ACMP_EVT_MILAN_V12_TK_REGISTERED); +} + +int handle_evt_tk_unregistered_milan_v12(struct acmp *acmp, uint64_t talker_guid) +{ + return acmp_generic_adp_srp_evt_lt_handler_milan_v12(acmp, talker_guid, + FSM_ACMP_EVT_MILAN_V12_TK_UNREGISTERED); +} + +int handle_probe_tx_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len) +{ + return 0; +} + +int handle_get_tx_state_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len) +{ + return 0; +} + +int handle_get_tx_connection_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len) +{ + return 0; +} + +int handle_disconnect_tx_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len) +{ + return 0; +} + +void acmp_periodic_milan_v12(struct acmp *acmp, uint64_t now) +{ + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12 *)acmp; + struct acmp_lt_timers *p, *t; + + spa_list_for_each_safe(p, t, &acmp_m->timers_lt, link) { + if (p->timeout > now) + continue; + + if (acmp_generic_timer_handler_milan_v12(acmp, now, p)) { + pw_log_error("while executing timer handler"); + } + free(p); + } +} + +struct acmp* acmp_server_init_milan_v12(void) +{ + struct acmp_milan_v12 *acmp_m; + + acmp_m = calloc(1, sizeof(*acmp_m)); + if (acmp_m == NULL) + return NULL; + + spa_list_init(&acmp_m->timers_lt); + spa_list_init(&acmp_m->pending_tk); + + return (struct acmp *)acmp_m; +} + +void acmp_destroy_milan_v12(struct acmp *acmp) +{ + struct acmp_milan_v12 *acmp_m = (struct acmp_milan_v12*) acmp; + struct acmp_lt_timers *tmr, *t; + + spa_list_for_each_safe(tmr, t, &acmp_m->timers_lt, link) { + acmp_list_free_element_milan_v12(&tmr->link, tmr); + } + + + free(acmp_m); +} + + +int handle_acmp_cli_cmd_milan_v12(struct acmp *acmp, const char *args, FILE *out) +{ +#if 0 + fprintf(out, "{ \"type\": \"help\"," + "\"text\": \"" + "/acmp/milan/help: this help \\n" + "/acmp/milan/cmds:\n" + "\" }"); +#endif + return 0; +} diff --git a/src/modules/module-avb/acmp-cmds-resps/acmp-milan-v12.h b/src/modules/module-avb/acmp-cmds-resps/acmp-milan-v12.h index e0612a497..63a26eb02 100644 --- a/src/modules/module-avb/acmp-cmds-resps/acmp-milan-v12.h +++ b/src/modules/module-avb/acmp-cmds-resps/acmp-milan-v12.h @@ -1,11 +1,67 @@ -/* SPDX-FileCopyrightText: Copyright © 2027 Alexandre Malki */ +/* SPDX-FileCopyrightText: Copyright © 2026 Alexandre Malki */ /* SPDX-License-Identifier: MIT */ #ifndef AVB_ACMP_MILAN_V12_H #define AVB_ACMP_MILAN_V12_H #include +#include "acmp-common.h" +/** Milan v1.2 ACMP */ +enum fsm_acmp_state_milan_v12 { + FSM_ACMP_STATE_MILAN_V12_UNBOUND, + FSM_ACMP_STATE_MILAN_V12_PRB_W_AVAIL, + FSM_ACMP_STATE_MILAN_V12_PRB_W_DELAY, + FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP, + FSM_ACMP_STATE_MILAN_V12_PRB_W_RESP2, + FSM_ACMP_STATE_MILAN_V12_PRB_W_RETRY, + FSM_ACMP_STATE_MILAN_V12_SETTLED_NO_RSV, + FSM_ACMP_STATE_MILAN_V12_SETTLED_RSV_OK, + FSM_ACMP_STATE_MILAN_V12_MAX, +}; + +struct acmp* acmp_server_init_milan_v12(void); + +void acmp_destroy_milan_v12(struct acmp *acmp); + +void acmp_periodic_milan_v12(struct acmp *acmp, uint64_t now); + +int handle_probe_tx_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len); + +int handle_disconnect_tx_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len); + +int handle_get_tx_state_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len); + +int handle_get_tx_connection_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len); + +int handle_unbind_rx_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len); + +int handle_bind_rx_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len); + +int handle_probe_tx_response_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len); + +int handle_get_rx_state_command_milan_v12(struct acmp *acmp, uint64_t now, + const void *m, int len); + +int handle_evt_tk_discovered_milan_v12(struct acmp *acmp, uint64_t entity); +int handle_evt_tk_departed_milan_v12(struct acmp *acmp, uint64_t entity); + +int handle_evt_tk_registered_milan_v12(struct acmp *acmp, uint64_t talker_guid); +int handle_evt_tk_unregistered_milan_v12(struct acmp *acmp, uint64_t talker_guid); + +int acmp_tmr_no_resp_milan_v12(struct acmp *acmp, uint64_t now); +int acmp_tmr_retry_milan_v12(struct acmp *acmp, uint64_t now); +int acmp_tmr_delay_milan_v12(struct acmp *acmp, uint64_t now); +int acmp_tmr_no_tk_milan_v12(struct acmp *acmp, uint64_t now); + +int handle_acmp_cli_cmd_milan_v12(struct acmp *acmp, const char *args, FILE *out); #endif //AVB_ACMP_MILAN_V12_H diff --git a/src/modules/module-avb/acmp.c b/src/modules/module-avb/acmp.c index c156da524..5f0d75d32 100644 --- a/src/modules/module-avb/acmp.c +++ b/src/modules/module-avb/acmp.c @@ -13,7 +13,6 @@ #include "internal.h" #include "stream.h" #include "aecp-aem-descriptors.h" -#include "aecp-aem-state.h" #include "acmp-cmds-resps/acmp-common.h" #include "acmp-cmds-resps/acmp-legacy-avb.h" @@ -27,16 +26,35 @@ static int handle_ignore(struct acmp *acmp, uint64_t now, const void *m, int len } static const char * const acmp_cmd_names[] = { - [AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_COMMAND] = "connect-tx-command", - [AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE] = "connect-tx-response", + /** Milan V1.2 Section 5.5.2.2 (PDU) */ + [AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_COMMAND] = + "connect-tx-command/probe-tx-command", + + /** Milan V1.2 Section 5.5.2.2 (PDU) */ + [AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE] = + "connect-tx-response/probe-tx-response", + [AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_COMMAND] = "disconnect-tx-command", [AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_RESPONSE] = "disconnect-tx-response", [AVB_ACMP_MESSAGE_TYPE_GET_TX_STATE_COMMAND] = "get-tx-state-command", [AVB_ACMP_MESSAGE_TYPE_GET_TX_STATE_RESPONSE] = "get-tx-state-response", - [AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_COMMAND] = "connect-rx-command", - [AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_RESPONSE] = "connect-rx-response", - [AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_COMMAND] = "disconnect-rx-command", - [AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_RESPONSE] = "disconnect-rx-response", + + /** Milan V1.2 Section 5.5.2.2 (PDU) */ + [AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_COMMAND] = + "connect-rx-command/bind-rx-command", + + /** Milan V1.2 Section 5.5.2.2 (PDU) */ + [AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_RESPONSE] = + "connect-rx-response/bind-rx-response", + + /** Milan V1.2 Section 5.5.2.2 (PDU) */ + [AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_COMMAND] = + "disconnect-rx-command/unbind-rx-command", + + /** Milan V1.2 Section 5.5.2.2 (PDU) */ + [AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_RESPONSE] = + "disconnect-rx-response/unbind-rx-response", + [AVB_ACMP_MESSAGE_TYPE_GET_RX_STATE_COMMAND] = "get-rx-state-command", [AVB_ACMP_MESSAGE_TYPE_GET_RX_STATE_RESPONSE] = "get-rx-state-response", [AVB_ACMP_MESSAGE_TYPE_GET_TX_CONNECTION_COMMAND] = "get-tx-connection-command", @@ -51,15 +69,32 @@ struct acmp_cmds { [mtype] = { .handle = handler } static const struct acmp_cmds acmp_cmds_legacy_avb[] = { - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_COMMAND, handle_connect_tx_command_legacy_avb), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE, handle_connect_tx_response_legacy_avb), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_COMMAND, handle_disconnect_tx_command_legacy_avb), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_RESPONSE, handle_disconnect_tx_response_legacy_avb), + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_COMMAND, + handle_connect_tx_command_legacy_avb), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE, + handle_connect_tx_response_legacy_avb), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_COMMAND, + handle_disconnect_tx_command_legacy_avb), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_RESPONSE, + handle_disconnect_tx_response_legacy_avb), + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_TX_STATE_COMMAND, NULL), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_TX_STATE_RESPONSE, handle_ignore), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_COMMAND, handle_connect_rx_command_legacy_avb), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_RESPONSE, handle_ignore), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_COMMAND, handle_disconnect_rx_command_legacy_avb), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_TX_STATE_RESPONSE, + handle_ignore), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_COMMAND, + handle_connect_rx_command_legacy_avb), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_RESPONSE, + handle_ignore), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_COMMAND, + handle_disconnect_rx_command_legacy_avb), + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_RESPONSE, NULL), AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_RX_STATE_COMMAND, handle_ignore), AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_RX_STATE_RESPONSE, handle_ignore), @@ -68,19 +103,37 @@ static const struct acmp_cmds acmp_cmds_legacy_avb[] = { }; static const struct acmp_cmds acmp_cmds_milan_v12[] = { - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_COMMAND, NULL), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE, NULL), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_COMMAND, NULL), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_RESPONSE, NULL), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_TX_STATE_COMMAND, NULL), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_TX_STATE_RESPONSE, NULL), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_COMMAND, NULL), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_COMMAND, + handle_probe_tx_command_milan_v12), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE, + handle_probe_tx_response_milan_v12), + + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_COMMAND, + handle_bind_rx_command_milan_v12), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_COMMAND, + handle_unbind_rx_command_milan_v12), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_COMMAND, + handle_unbind_rx_command_milan_v12), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_RESPONSE, + handle_ignore), + + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_RX_STATE_COMMAND, + handle_get_rx_state_command_milan_v12), + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_RESPONSE, NULL), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_COMMAND, NULL), AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_RESPONSE, NULL), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_RX_STATE_COMMAND, NULL), - AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_RX_STATE_RESPONSE, NULL), AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_TX_CONNECTION_COMMAND, NULL), + + /** Milan V1.2 Section 5.5.2.2 (PDU) , Milan does not requires them */ + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_TX_STATE_COMMAND, handle_ignore), + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_TX_STATE_RESPONSE, handle_ignore), + AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_RX_STATE_RESPONSE, NULL), AVB_ACMP_CMD_HANDLER(AVB_ACMP_MESSAGE_TYPE_GET_TX_CONNECTION_RESPONSE, NULL), }; @@ -115,7 +168,7 @@ static int acmp_message(void *data, uint64_t now, const void *message, int len) return 0; if (memcmp(h->dest, mac, 6) != 0 && - memcmp(h->dest, server->mac_addr, 6) != 0) + memcmp(h->dest, server->mac_addr, 6) != 0) return 0; if (AVB_PACKET_GET_SUBTYPE(&p->hdr) != AVB_SUBTYPE_ACMP) @@ -126,11 +179,11 @@ static int acmp_message(void *data, uint64_t now, const void *message, int len) pw_log_info("got ACMP message %s", acmp_cmd_names[mtype]); if (mtype < 0 || (size_t)mtype >= acmp_cmds_modes[server->avb_mode].count) { - return reply_not_supported(acmp, mtype | 1, message, len); + return acmp_reply_not_supported(acmp, mtype | 1, message, len); } if (acmp_cmds_modes[server->avb_mode].cmds[mtype].handle == NULL) - return reply_not_supported(acmp, mtype | 1, message, len); + return acmp_reply_not_supported(acmp, mtype | 1, message, len); return acmp_cmds_modes[server->avb_mode].cmds[mtype].handle(acmp, now, message, len); } @@ -138,41 +191,27 @@ static int acmp_message(void *data, uint64_t now, const void *message, int len) static void acmp_destroy(void *data) { struct acmp *acmp = data; - spa_hook_remove(&acmp->server_listener); - pending_destroy(acmp); - free(acmp); -} - -static void check_timeout(struct acmp *acmp, uint64_t now, uint16_t type) -{ - struct pending *p, *t; - - spa_list_for_each_safe(p, t, &acmp->pending[type], link) { - if (p->last_time + p->timeout > now) - continue; - - if (p->retry == 0) { - pw_log_info("%p: pending timeout, retry", p); - retry_pending(acmp, now, p); - } else { - pw_log_info("%p: pending timeout, fail", p); - pending_free(acmp, p); - } + switch (acmp->server->avb_mode) { + case AVB_MODE_MILAN_V12: + acmp_destroy_milan_v12(acmp); + break; + case AVB_MODE_LEGACY: + acmp_server_destroy_legacy_avb(acmp); + break; + default: + pw_log_warn("Unknown avb_mode"); + break; } + + spa_hook_remove(&acmp->server_listener); } -static void acmp_periodic(void *data, uint64_t now) -{ - struct acmp *acmp = data; - check_timeout(acmp, now, PENDING_TALKER); - check_timeout(acmp, now, PENDING_LISTENER); - check_timeout(acmp, now, PENDING_CONTROLLER); -} + static int do_help(struct acmp *acmp, const char *args, FILE *out) { fprintf(out, "{ \"type\": \"help\"," "\"text\": \"" - "/adp/help: this help \\n" + " /acmp/help: this help \\n" "\" }"); return 0; } @@ -189,12 +228,108 @@ static int acmp_command(void *data, uint64_t now, const char *command, const cha if (spa_streq(command, "help")) res = do_help(acmp, args, out); + else if (spa_streq(command, "milan_v12")) + res = handle_acmp_cli_cmd_milan_v12(acmp, args, out); else res = -ENOTSUP; return res; } +static void acmp_periodic(void *data, uint64_t now) +{ + struct acmp *acmp = (struct acmp*)data; + + switch (acmp->server->avb_mode) { + case AVB_MODE_MILAN_V12: + acmp_periodic_milan_v12(acmp, now); + break; + case AVB_MODE_LEGACY: + acmp_periodic_avb_legacy(acmp, now); + break; + default: + pw_log_warn("Unknown avb_mode"); + break; + } + +} + +int handle_evt_tk_discovered(struct avb_acmp *avb_acmp, uint64_t entity) +{ + struct acmp *acmp = (struct acmp*)avb_acmp; + + switch (acmp->server->avb_mode) { + case AVB_MODE_MILAN_V12: + return handle_evt_tk_discovered_milan_v12(acmp, entity); + break; + case AVB_MODE_LEGACY: + pw_log_warn("not implemented for legacy avb"); + break; + default: + pw_log_warn("Unknown avb_mode"); + break; + } + + return -1; +} + +int handle_evt_tk_departed(struct avb_acmp *avb_acmp, uint64_t entity) +{ + struct acmp *acmp = (struct acmp*)avb_acmp; + + switch (acmp->server->avb_mode) { + case AVB_MODE_MILAN_V12: + return handle_evt_tk_departed_milan_v12(acmp, entity); + break; + case AVB_MODE_LEGACY: + pw_log_warn("not implemented for legacy avb"); + break; + default: + pw_log_warn("Unknown avb_mode"); + break; + } + + return 0; +} + +int handle_evt_tk_registered(struct avb_acmp *avb_acmp, uint64_t entity) +{ + struct acmp *acmp = (struct acmp*)avb_acmp; + + switch (acmp->server->avb_mode) { + case AVB_MODE_MILAN_V12: + return handle_evt_tk_registered_milan_v12(acmp, entity); + break; + case AVB_MODE_LEGACY: + pw_log_warn("not implemented for legacy avb"); + break; + default: + pw_log_warn("Unknown avb_mode"); + break; + } + + return -1; +} + +int handle_evt_tk_unregistered(struct avb_acmp *avb_acmp, uint64_t entity) +{ + struct acmp *acmp = (struct acmp*)avb_acmp; + + switch (acmp->server->avb_mode) { + case AVB_MODE_MILAN_V12: + return handle_evt_tk_unregistered_milan_v12(acmp, entity); + break; + case AVB_MODE_LEGACY: + pw_log_warn("not implemented for legacy avb"); + break; + default: + pw_log_warn("Unknown avb_mode"); + break; + } + + return -1; +} + static const struct server_events server_events = { AVB_VERSION_SERVER_EVENTS, .destroy = acmp_destroy, @@ -203,19 +338,81 @@ static const struct server_events server_events = { .command = acmp_command }; +int acmp_init_listener_stream_output(struct avb_acmp *avb_acmp, + struct aecp_aem_stream_output_state *stream_st) +{ + int rc = 0; + pw_log_warn("Not implemented"); +#if 0 + switch (acmp->server->avb_mode) { + case AVB_MODE_MILAN_V12: + rc = acmp_init_talker_stream_milan_v12(acmp, stream_st); + break; + case AVB_MODE_LEGACY: + default: + break; + } + +#endif + return rc; +} + +int acmp_init_listener_stream_input(struct avb_acmp *avb_acmp, + struct aecp_aem_stream_input_state *stream_st) +{ + int rc = 0; + + pw_log_warn("Not implemented"); +#if 0 + switch (acmp->server->avb_mode) { + case AVB_MODE_MILAN_V12: + break; + case AVB_MODE_LEGACY: + default: + break; + } +#endif + return rc; +} + +int acmp_fini_stream(struct acmp *acmp, struct stream *stream) +{ + int rc = 0; + +#if 0 + switch (acmp->server->avb_mode) { + case AVB_MODE_MILAN_V12: + rc = acmp_fini_stream_milan_v12(acmp, stream); + break; + case AVB_MODE_LEGACY: + default: + break; + } +#endif + + return rc; +} + struct avb_acmp *avb_acmp_register(struct server *server) { struct acmp *acmp; - acmp = calloc(1, sizeof(*acmp)); + switch (server->avb_mode) { + case AVB_MODE_MILAN_V12: + acmp = acmp_server_init_milan_v12(); + break; + case AVB_MODE_LEGACY: + acmp = acmp_server_init_legacy_avb(); + break; + default: + acmp = NULL; + break; + } + if (acmp == NULL) return NULL; acmp->server = server; - spa_list_init(&acmp->pending[PENDING_TALKER]); - spa_list_init(&acmp->pending[PENDING_LISTENER]); - spa_list_init(&acmp->pending[PENDING_CONTROLLER]); - avdecc_server_add_listener(server, &acmp->server_listener, &server_events, acmp); return (struct avb_acmp*)acmp; diff --git a/src/modules/module-avb/acmp.h b/src/modules/module-avb/acmp.h index bdd0d7e89..7f5495af1 100644 --- a/src/modules/module-avb/acmp.h +++ b/src/modules/module-avb/acmp.h @@ -8,6 +8,22 @@ #include "packets.h" #include "internal.h" +#include "acmp-cmds-resps/acmp-common.h" +#include "aecp-aem-state.h" + + +/** 1722.1 defines this for the ACMP and the AEM FSMs */ +#define AVB_ACMP_FLAG_CLASS_B (1<<0) +#define AVB_ACMP_FLAG_FAST_CONNECT (1<<1) +#define AVB_ACMP_FLAG_SAVED_STATES (1<<2) +#define AVB_ACMP_FLAG_STREAMING_WAIT (1<<3) +#define AVB_ACMP_FLAG_SUPPORTS_ENCRYPTED (1<<4) +#define AVB_ACMP_FLAG_ENCRYPTED_PDU (1<<5) +#define AVB_ACMP_FLAG_SRP_REGISTRATION_FAILED (1<<6) +#define AVB_ACMP_FLAG_CL_ENTRIES_VALID (1<<7) +#define AVB_ACMP_FLAG_NO_SRP (1<<8) +#define AVB_ACMP_FLAG_UDP (1<<9) + #define AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_COMMAND 0 #define AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE 1 #define AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_COMMAND 2 @@ -52,6 +68,9 @@ #define AVB_ACMP_TIMEOUT_GET_RX_STATE_COMMAND_MS 200 #define AVB_ACMP_TIMEOUT_GET_TX_CONNECTION_COMMAND 200 + +struct avb_acmp; + struct avb_packet_acmp { struct avb_packet_header hdr; uint64_t stream_id; @@ -74,7 +93,22 @@ struct avb_packet_acmp { #define AVB_PACKET_ACMP_GET_MESSAGE_TYPE(p) AVB_PACKET_GET_SUB1(&(p)->hdr) #define AVB_PACKET_ACMP_GET_STATUS(p) AVB_PACKET_GET_SUB2(&(p)->hdr) +int acmp_init_listener_stream_output(struct avb_acmp *avb_acmp, + struct aecp_aem_stream_output_state *stream_st); + +int acmp_init_listener_stream_input(struct avb_acmp *avb_acmp, + struct aecp_aem_stream_input_state *stream_st); + +int acmp_fini_listener_stream(struct avb_acmp *avb_acmp, + struct aecp_aem_stream_input_state *stream_st); + struct avb_acmp *avb_acmp_register(struct server *server); void avb_acmp_unregister(struct avb_acmp *acmp); +int handle_evt_tk_discovered(struct avb_acmp *avb_acmp, uint64_t entity); +int handle_evt_tk_departed(struct avb_acmp *avb_acmp, uint64_t entity); + +int handle_evt_tk_registered(struct avb_acmp *avb_acmp, uint64_t entity); +int handle_evt_tk_unregistered(struct avb_acmp *avb_acmp, uint64_t entity); + #endif /* AVB_ACMP_H */ diff --git a/src/modules/module-avb/adp.c b/src/modules/module-avb/adp.c index 9a8190b1d..95ea491db 100644 --- a/src/modules/module-avb/adp.c +++ b/src/modules/module-avb/adp.c @@ -7,6 +7,7 @@ #include #include "adp.h" +#include "acmp.h" #include "aecp-aem-descriptors.h" #include "internal.h" #include "utils.h" @@ -80,7 +81,7 @@ static int send_discover(struct adp *adp, uint64_t entity_id) AVB_PACKET_SET_SUBTYPE(&p->hdr, AVB_SUBTYPE_ADP); AVB_PACKET_SET_LENGTH(&p->hdr, AVB_ADP_CONTROL_DATA_LENGTH); AVB_PACKET_ADP_SET_MESSAGE_TYPE(p, AVB_ADP_MESSAGE_TYPE_ENTITY_DISCOVER); - p->entity_id = htonl(entity_id); + p->entity_id = htobe64(entity_id); avb_server_send_packet(adp->server, mac, AVB_TSN_ETH, buf, len); return 0; } @@ -128,15 +129,63 @@ static int adp_message(void *data, uint64_t now, const void *message, int len) spa_list_append(&adp->entities, &e->link); pw_log_info("entity %s available", avb_utils_format_id(buf, sizeof(buf), entity_id)); + + if (server->avb_mode == AVB_MODE_MILAN_V12) { + //Milan V1.2 Section 5.6.4.5.1 + if (handle_evt_tk_discovered(server->acmp, entity_id)) { + pw_log_info("handling available event"); + return -1; + } + } + } else { + if (server->avb_mode == AVB_MODE_MILAN_V12) { + //Milan V1.2 + //Milan V1.2 Section 5.6.4.5.2 + struct avb_ethernet_header *h_saved = (struct avb_ethernet_header *) e->buf; + struct avb_packet_adp *p_saved = + SPA_PTROFF(h_saved, sizeof(*h_saved), void); + + if (p_saved->available_index != p->available_index) { + if (handle_evt_tk_departed(server->acmp, entity_id)) { + pw_log_info("handling departing event"); + return -1; + } + + bool has_gptp_domain_changed = + (p_saved->gptp_domain_number != p->gptp_domain_number) || + (p_saved->gptp_grandmaster_id != p->gptp_grandmaster_id); + + if (has_gptp_domain_changed) { + e->last_time = INT64_MAX; + spa_list_remove(&e->link); + pw_log_info("Removing from the adp list \n"); + return 0; + } + + if (handle_evt_tk_discovered(server->acmp, entity_id)) { + pw_log_warn("handling available event"); + return -1; + } + } + + memcpy(e->buf, message, len); + } } e->last_time = now; + break; case AVB_ADP_MESSAGE_TYPE_ENTITY_DEPARTING: if (e != NULL) { + if (server->avb_mode == AVB_MODE_MILAN_V12) { + // Milan v1.2 Section 5.6.4.5.3 + handle_evt_tk_departed(server->acmp, entity_id); + } + pw_log_info("entity %s departing", avb_utils_format_id(buf, sizeof(buf), entity_id)); entity_free(e); } + break; case AVB_ADP_MESSAGE_TYPE_ENTITY_DISCOVER: pw_log_info("entity %s advertise", @@ -174,6 +223,7 @@ static void check_timeout(struct adp *adp, uint64_t now) { struct entity *e, *t; char buf[128]; + struct avb_acmp *avb_acmp = adp->server->acmp; spa_list_for_each_safe(e, t, &adp->entities, link) { if (e->last_time + (e->valid_time + 2) * SPA_NSEC_PER_SEC > now) @@ -182,6 +232,8 @@ static void check_timeout(struct adp *adp, uint64_t now) pw_log_info("entity %s timeout", avb_utils_format_id(buf, sizeof(buf), e->entity_id)); + handle_evt_tk_departed(avb_acmp, e->entity_id); + if (e->advertise) send_departing(adp, now, e); @@ -218,7 +270,7 @@ static int check_advertise(struct adp *adp, uint64_t now) d = server_find_descriptor(server, AVB_AEM_DESC_ENTITY, 0); if (d == NULL) - return 0; + return -1; entity = d->ptr; entity_id = be64toh(entity->entity_id); @@ -345,6 +397,40 @@ static const struct server_events server_events = { .command = adp_command }; +bool adp_is_discovered_entity(struct server *server, uint64_t entity_id) +{ + struct adp *adp = (struct adp*)server->adp; + struct entity *entity = find_entity_by_id(adp, entity_id); + + if (entity == NULL) { + return false; + } + + return true; +} + +int adp_start_discovery_entity(struct server *server, uint64_t entity_id) +{ + pw_log_info("ADP: start discovery of entity 0x%"PRIx64, entity_id); + return send_discover((struct adp*) server->adp, entity_id); +} + +void adp_stop_discovery_entity(struct server *server, uint64_t entity_id) +{ + struct entity *e, *t; + struct adp *adp = (struct adp*)server->adp; + pw_log_info("ADP: stop discovery of entity 0x%" PRIx64, entity_id); + + spa_list_for_each_safe(e, t, &adp->entities, link) { + if (e->entity_id == entity_id) { + entity_free(e); + return; + } + } + + pw_log_warn("Could not find entity 0x%"PRIx64, entity_id); +} + struct avb_adp *avb_adp_register(struct server *server) { struct adp *adp; diff --git a/src/modules/module-avb/adp.h b/src/modules/module-avb/adp.h index 408a48805..37c303103 100644 --- a/src/modules/module-avb/adp.h +++ b/src/modules/module-avb/adp.h @@ -54,6 +54,8 @@ #define AVB_ADP_CONTROL_DATA_LENGTH 56 +struct avb_adp; + struct avb_packet_adp { struct avb_packet_header hdr; uint64_t entity_id; @@ -80,6 +82,9 @@ struct avb_packet_adp { #define AVB_PACKET_ADP_GET_MESSAGE_TYPE(p) AVB_PACKET_GET_SUB1(&(p)->hdr) #define AVB_PACKET_ADP_GET_VALID_TIME(p) AVB_PACKET_GET_SUB2(&(p)->hdr) +bool adp_is_discovered_entity(struct server *server, uint64_t entity_id); +int adp_start_discovery_entity(struct server *server, uint64_t entity_id); +void adp_stop_discovery_entity(struct server *server, uint64_t entity_id); struct avb_adp *avb_adp_register(struct server *server); void avb_adp_unregister(struct avb_adp *adp); diff --git a/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-stream-format.c b/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-stream-format.c index 7e9841f3b..9d2b490b8 100644 --- a/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-stream-format.c +++ b/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-stream-format.c @@ -100,7 +100,6 @@ int handle_cmd_set_stream_format_milan_v12(struct aecp *aecp, int64_t now, int i; int rc; bool found = false; - void *stream; set_cmd = (const struct avb_packet_aecp_aem_setget_stream_format *)p->payload; desc_type = ntohs(set_cmd->descriptor_type); @@ -113,21 +112,14 @@ int handle_cmd_set_stream_format_milan_v12(struct aecp *aecp, int64_t now, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len); if (desc_type == AVB_AEM_DESC_STREAM_INPUT) { - struct aecp_aem_stream_input_state *state = - (struct aecp_aem_stream_input_state *)desc->ptr; - stream = &state->stream; // TODO check if the stream is bound } else if (desc_type == AVB_AEM_DESC_STREAM_OUTPUT) { - struct aecp_aem_stream_output_state *state = - (struct aecp_aem_stream_output_state *)desc->ptr; - stream = &state->stream; // TODO check if the stream is STREAM_RUNNING } else { return reply_status(aecp, AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, m, len); } - (void)stream; stream_desc = (struct avb_aem_desc_stream *)desc->ptr; for (i = 0; i < ntohs(stream_desc->number_of_formats); i++) { if (stream_desc->stream_formats[i] == new_format) { diff --git a/src/modules/module-avb/aecp-aem-state.h b/src/modules/module-avb/aecp-aem-state.h index d887da1a0..26baa4d3c 100644 --- a/src/modules/module-avb/aecp-aem-state.h +++ b/src/modules/module-avb/aecp-aem-state.h @@ -33,68 +33,37 @@ * notificaction. */ struct aecp_aem_state_base { - /** - * Originator of the control - * This is needed so the unsoolictied notification does not send back SUCCESS - * to the originator of of the unsolicited notification - */ - uint64_t controller_entity_id; + uint64_t controller_entity_id; - /** - * To avoid sending on every change for unsol notifications, only once a - * second - */ - int64_t last_update; + int64_t last_update; - /** timeout absolute time*/ - int64_t expire_timeout; + /** timeout absolute time*/ + int64_t expire_timeout; }; /** * \brief the structure keeps track of the registered controller entities */ struct aecp_aem_unsol_notification_state { - /** - * The controller is that is locking this system - */ - uint64_t ctrler_entity_id; + uint64_t ctrler_entity_id; - /** - * mac Address of the controller - */ - uint8_t ctrler_mac_addr[6]; + uint8_t ctrler_mac_addr[6]; - /** - * Port where the registeration originated from - */ - uint8_t port_id; + uint8_t port_id; - /*** - * The sequence ID of the next unsolicited notification - */ - uint16_t next_seq_id; - /** - * Actual value of the lock, get removed when unregistere or expired. - */ - bool is_registered; + uint16_t next_seq_id; + bool is_registered; }; struct aecp_aem_base_info { - /** Originator of the control - * This is needed so the unsoolictied notification does not send back SUCCESS - * to the originator of of the unsolicited notification */ - uint64_t controller_entity_id; + uint64_t controller_entity_id; - /** - * To avoid sending on every change for unsol notifications, only once a - * a second - * */ - int64_t last_update; + int64_t last_update; - /** timeout absolute time*/ - int64_t expire_timeout; + /** timeout absolute time*/ + int64_t expire_timeout; }; struct aecp_aem_lock_state { @@ -138,43 +107,79 @@ struct aecp_aem_entity_legacy_avb_state { * Table 7-156 */ struct aecp_aem_stream_input_counters { - struct aecp_aem_state_base base_state; + struct aecp_aem_state_base base_state; - uint32_t media_locked; - uint32_t media_unlocked; - uint32_t stream_interrupted; - uint32_t seq_mistmatch; - uint32_t media_reset; - /** Timestamp Uncertain */ - uint32_t tu; - uint32_t unsupported_format; - uint32_t late_timestamp; - uint32_t early_timestamp; - uint32_t frame_rx; + uint32_t media_locked; + uint32_t media_unlocked; + uint32_t stream_interrupted; + uint32_t seq_mistmatch; + uint32_t media_reset; + /** Timestamp Uncertain */ + uint32_t tu; + uint32_t unsupported_format; + uint32_t late_timestamp; + uint32_t early_timestamp; + uint32_t frame_rx; +}; + +struct stream_common { + struct stream stream; + struct avb_msrp_attribute *msrp_attr; }; struct aecp_aem_stream_input_state { - struct avb_aem_desc_stream desc; + struct avb_aem_desc_stream desc; - struct aecp_aem_stream_input_counters counters; - struct stream stream; + struct aecp_aem_stream_input_counters counters; + struct stream_common common; +}; + +struct stream_input_saved_binding_param { + uint64_t controller_guid; + uint64_t talker_guid; + uint64_t listener_guid; + uint16_t talker_unique_id; + uint16_t listener_unique_id; + /** 1722.1-2021 Table 7.145 use the same as in the aecp packet*/ + uint32_t aem_flags; +}; + +struct acmp_stream_status_common { + struct stream_input_saved_binding_param saved_bindings; + uint8_t probing_status; + uint8_t acmp_status; + uint8_t stream_dest_mac[6]; + uint16_t stream_vlanid; +}; + +struct acmp_stream_status_milan_v12 { + struct acmp_stream_status_common common; + uint32_t fsm_acmp_state; +}; + +/** + * \brief The Milan v1.2 stream structure needs more information + * about the different protocol*/ +struct aecp_aem_stream_input_state_milan_v12 { + struct aecp_aem_stream_input_state stream_in_sta; + struct acmp_stream_status_milan_v12 acmp_status; }; struct aecp_aem_stream_output_counters { - struct aecp_aem_state_base base_state; + struct aecp_aem_state_base base_state; - uint32_t stream_start; - uint32_t stream_stop; - uint32_t media_reset; - uint32_t tu; - uint32_t frame_tx; + uint32_t stream_start; + uint32_t stream_stop; + uint32_t media_reset; + uint32_t tu; + uint32_t frame_tx; }; struct aecp_aem_stream_output_state { - struct avb_aem_desc_stream desc; + struct avb_aem_desc_stream desc; - struct aecp_aem_stream_output_counters counters; - struct stream stream; + struct aecp_aem_stream_output_counters counters; + struct stream_common common; }; #endif // AVB_AECP_AEM_STATE_H diff --git a/src/modules/module-avb/aecp-aem.h b/src/modules/module-avb/aecp-aem.h index 1eefd413b..22c6369c0 100644 --- a/src/modules/module-avb/aecp-aem.h +++ b/src/modules/module-avb/aecp-aem.h @@ -63,11 +63,17 @@ struct avb_packet_aecp_aem_setget_sensor_format { } __attribute__ ((__packed__)); -#define AVB_AEM_STREAM_INFO_FLAG_CLASS_B (1u<<0) -#define AVB_AEM_STREAM_INFO_FLAG_FAST_CONNECT (1u<<1) -#define AVB_AEM_STREAM_INFO_FLAG_SAVED_STATE (1u<<2) -#define AVB_AEM_STREAM_INFO_FLAG_STREAMING_WAIT (1u<<3) -#define AVB_AEM_STREAM_INFO_FLAG_ENCRYPTED_PDU (1u<<4) + +#define AVB_AEM_STREAM_INFO_FLAG_CLASS_B (1u<<0) +#define AVB_AEM_STREAM_INFO_FLAG_FAST_CONNECT (1u<<1) +#define AVB_AEM_STREAM_INFO_FLAG_SAVED_STATE (1u<<2) +#define AVB_AEM_STREAM_INFO_FLAG_STREAMING_WAIT (1u<<3) +#define AVB_AEM_STREAM_INFO_FLAG_SUPPORTS_ENCRYPTED (1u<<4) +#define AVB_AEM_STREAM_INFO_FLAG_ENCRYPTED_PDU (1u<<5) +#define AVB_AEM_STREAM_INFO_FLAG_SRP_REGISTERING_FAILED (1u<<6) +#define AVB_AEM_STREAM_INFO_FLAG_CL_ENTRIES_VALID (1u<<7) +#define AVB_AEM_STREAM_INFO_FLAG_NO_SRP (1u<<8) +#define AVB_AEM_STREAM_INFO_FLAG_UDP (1u<<9) #define AVB_AEM_STREAM_INFO_FLAG_STREAM_VLAN_ID_VALID (1u<<25) #define AVB_AEM_STREAM_INFO_FLAG_CONNECTED (1u<<26) #define AVB_AEM_STREAM_INFO_FLAG_MSRP_FAILURE_VALID (1u<<27) @@ -76,10 +82,59 @@ struct avb_packet_aecp_aem_setget_sensor_format { #define AVB_AEM_STREAM_INFO_FLAG_STREAM_ID_VALID (1u<<30) #define AVB_AEM_STREAM_INFO_FLAG_STREAM_FORMAT_VALID (1u<<31) +union aem_stream_info_flags { + struct { + uint32_t class_b:1; + uint32_t fast_connect:1; + uint32_t saved_state:1; + uint32_t streaming_wait:1; + uint32_t supports_encrypted:1; + uint32_t encrypted_pdu:1; + union { + uint32_t talker_failed:1; + // Milan V1.2 + uint32_t registering_failed:1; + }; + uint32_t rsvd_0:1; + uint32_t no_srp:1; + uint32_t rsvd_1:10; + uint32_t ip_flags_valid:1; + uint32_t ip_src_port_valid:1; + uint32_t ip_dst_port_valid:1; + uint32_t ip_src_addr_valid:1; + uint32_t ip_dst_addr_valid:1; + uint32_t not_registering_srp:1; + uint32_t stream_vlan_id_valid:1; + uint32_t connected:1; + uint32_t msrp_failure_valid:1; + uint32_t stream_dst_valid:1; + uint32_t msrp_acc_lat_valid:1; + uint32_t stream_id_valid:1; + uint32_t stream_format_valid:1; + } ; + uint32_t flags; +} __attribute__ ((__packed__)); + +union aem_stream_info_flag_extended { + struct { + uint16_t ip_flags; + uint16_t source_port; + uint16_t destination_port; + } legacy_avb; + struct { + uint16_t rsvd_0; + uint32_t flags_ex_registering:1; + uint32_t rsvd_1:31; + uint32_t pbsta:3; + uint32_t acmpsta:5; + uint32_t rsvd_2:24; + } milan_v12; +} __attribute__ ((__packed__)); + struct avb_packet_aecp_aem_setget_stream_info { uint16_t descriptor_type; uint16_t descriptor_index; - uint32_t aem_stream_info_flags; + union aem_stream_info_flags flags; uint64_t stream_format; uint64_t stream_id; uint32_t msrp_accumulated_latency; @@ -88,7 +143,7 @@ struct avb_packet_aecp_aem_setget_stream_info { uint8_t reserved; uint64_t msrp_failure_bridge_id; uint16_t stream_vlan_id; - uint16_t reserved2; + union aem_stream_info_flag_extended flags_ex; } __attribute__ ((__packed__)); struct avb_packet_aecp_aem_setget_name { diff --git a/src/modules/module-avb/avdecc.c b/src/modules/module-avb/avdecc.c index 8729d6421..0a0e8870e 100644 --- a/src/modules/module-avb/avdecc.c +++ b/src/modules/module-avb/avdecc.c @@ -412,8 +412,8 @@ struct server *avdecc_server_new(struct impl *impl, struct spa_dict *props) server->mmrp = avb_mmrp_register(server); server->msrp = avb_msrp_register(server); server->mvrp = avb_mvrp_register(server); - avb_adp_register(server); - avb_acmp_register(server); + server->adp = avb_adp_register(server); + server->acmp = avb_acmp_register(server); server->domain_attr = avb_msrp_attribute_new(server->msrp, AVB_MSRP_ATTRIBUTE_TYPE_DOMAIN); diff --git a/src/modules/module-avb/es-builder.c b/src/modules/module-avb/es-builder.c index 2611e73e6..cb6225454 100644 --- a/src/modules/module-avb/es-builder.c +++ b/src/modules/module-avb/es-builder.c @@ -80,7 +80,7 @@ static struct descriptor *es_buidler_desc_stream_general_prepare(struct server * } pstream_input = desc->ptr; - stream = &pstream_input->stream; + stream = &pstream_input->common.stream; direction = SPA_DIRECTION_INPUT; break; case AVB_AEM_DESC_STREAM_OUTPUT: @@ -96,7 +96,7 @@ static struct descriptor *es_buidler_desc_stream_general_prepare(struct server * } pstream_output = desc->ptr; - stream = &pstream_output->stream; + stream = &pstream_output->common.stream; direction = SPA_DIRECTION_OUTPUT; break; diff --git a/src/modules/module-avb/internal.h b/src/modules/module-avb/internal.h index bb4961674..125546ee8 100644 --- a/src/modules/module-avb/internal.h +++ b/src/modules/module-avb/internal.h @@ -113,6 +113,8 @@ struct server { struct avb_mvrp *mvrp; struct avb_msrp *msrp; struct avb_maap *maap; + struct avb_adp *adp; + struct avb_acmp *acmp; struct avb_msrp_attribute *domain_attr; }; diff --git a/src/modules/module-avb/stream.c b/src/modules/module-avb/stream.c index ddc5ab635..7587194bf 100644 --- a/src/modules/module-avb/stream.c +++ b/src/modules/module-avb/stream.c @@ -16,6 +16,7 @@ #include "iec61883.h" #include "stream.h" #include "aecp-aem-state.h" +#include "acmp-cmds-resps/acmp-common.h" #include "utils.h" static void on_stream_destroy(void *d) @@ -245,6 +246,12 @@ struct stream *server_create_stream(struct server *server, struct stream *stream struct spa_pod_builder b; int res; + struct stream_common *common; + struct avb_msrp_attribute *msrp_attr; + + + common = SPA_CONTAINER_OF(stream, struct stream_common, stream); + stream->server = server; stream->direction = direction; stream->prio = AVB_MSRP_PRIORITY_DEFAULT; @@ -258,10 +265,6 @@ struct stream *server_create_stream(struct server *server, struct stream *stream (uint64_t)server->mac_addr[5] << 16 | htons(index); - stream->vlan_attr = avb_mvrp_attribute_new(server->mvrp, - AVB_MVRP_ATTRIBUTE_TYPE_VID); - stream->vlan_attr->attr.vid.vlan = htons(stream->vlan_id); - stream->buffer_data = calloc(1, BUFFER_SIZE); stream->buffer_size = BUFFER_SIZE; spa_ringbuffer_init(&stream->ring); @@ -283,14 +286,20 @@ struct stream *server_create_stream(struct server *server, struct stream *stream PW_KEY_NODE_WANT_DRIVER, "true", NULL)); } + +#if 0 + mvrp_attr = &stream_in_state->mvrp_attr + *mvrp_attr = avb_mvrp_attribute_new(mvrp_attr, AVB_MVRP_ATTRIBUTE_TYPE_VID); + (*mvrp_attr)->attr.vid.vlan = htons(stream->vlan_id); +#endif if (stream->stream == NULL) goto error_free; pw_stream_add_listener(stream->stream, &stream->stream_listener, direction == SPA_DIRECTION_INPUT ? - &source_stream_events : - &sink_stream_events, + &source_stream_events : + &sink_stream_events, stream); stream->info.info.raw.format = SPA_AUDIO_FORMAT_S24_32_BE; @@ -319,23 +328,34 @@ struct stream *server_create_stream(struct server *server, struct stream *stream setup_pdu(stream); setup_msg(stream); + + if (stream->direction == SPA_DIRECTION_OUTPUT) { + msrp_attr = avb_msrp_attribute_new(server->msrp, + AVB_MSRP_ATTRIBUTE_TYPE_LISTENER); + } else { - stream->listener_attr = avb_msrp_attribute_new(server->msrp, - AVB_MSRP_ATTRIBUTE_TYPE_LISTENER); - stream->talker_attr = avb_msrp_attribute_new(server->msrp, - AVB_MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE); - stream->talker_attr->attr.talker.vlan_id = htons(stream->vlan_id); - stream->talker_attr->attr.talker.tspec_max_frame_size = htons(32 + stream->frames_per_pdu * stream->stride); - stream->talker_attr->attr.talker.tspec_max_interval_frames = - htons(AVB_MSRP_TSPEC_MAX_INTERVAL_FRAMES_DEFAULT); - stream->talker_attr->attr.talker.priority = stream->prio; - stream->talker_attr->attr.talker.rank = AVB_MSRP_RANK_DEFAULT; - stream->talker_attr->attr.talker.accumulated_latency = htonl(95); + msrp_attr = avb_msrp_attribute_new(server->msrp, + AVB_MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE); + + msrp_attr->attr.talker.vlan_id = htons(stream->vlan_id); + + msrp_attr->attr.talker.tspec_max_frame_size = + htons(32 + stream->frames_per_pdu * stream->stride); + + msrp_attr->attr.talker.tspec_max_interval_frames = + htons(AVB_MSRP_TSPEC_MAX_INTERVAL_FRAMES_DEFAULT); + + msrp_attr->attr.talker.priority = stream->prio; + msrp_attr->attr.talker.rank = AVB_MSRP_RANK_DEFAULT; + msrp_attr->attr.talker.accumulated_latency = htonl(95); + } + + + common->msrp_attr = msrp_attr; spa_list_append(&server->streams, &stream->link); return stream; - error_free_stream: pw_stream_destroy(stream->stream); errno = -res; @@ -346,7 +366,10 @@ error_free: void stream_destroy(struct stream *stream) { - avb_mrp_attribute_destroy(stream->listener_attr->mrp); + struct stream_common *common; + common = SPA_CONTAINER_OF(stream, struct stream_common, stream); + + avb_mrp_attribute_destroy(common->msrp_attr->mrp); } static int setup_socket(struct stream *stream) @@ -418,6 +441,8 @@ int stream_activate(struct stream *stream, uint16_t index, uint64_t now) struct server *server = stream->server; struct avb_frame_header *h = (void*)stream->pdu; int fd, res; + struct stream_common *common; + common = SPA_CONTAINER_OF(stream, struct stream_common, stream); if (stream->source == NULL) { if ((fd = setup_socket(stream)) < 0) @@ -433,55 +458,63 @@ int stream_activate(struct stream *stream, uint16_t index, uint64_t now) } } +#if 0 + FIXME!!! This should be used only one and activated once and for all.. avb_mrp_attribute_begin(stream->vlan_attr->mrp, now); avb_mrp_attribute_join(stream->vlan_attr->mrp, now, true); +#endif if (stream->direction == SPA_DIRECTION_INPUT) { - stream->listener_attr->attr.listener.stream_id = htobe64(stream->peer_id); - stream->listener_attr->param = AVB_MSRP_LISTENER_PARAM_READY; - avb_mrp_attribute_begin(stream->listener_attr->mrp, now); - avb_mrp_attribute_join(stream->listener_attr->mrp, now, true); - + common->msrp_attr->attr.listener.stream_id = htobe64(stream->peer_id); + common->msrp_attr->param = AVB_MSRP_LISTENER_PARAM_READY; + avb_mrp_attribute_begin(common->msrp_attr->mrp, now); + avb_mrp_attribute_join(common->msrp_attr->mrp, now, true); +#if 0 stream->talker_attr->attr.talker.stream_id = htobe64(stream->peer_id); avb_mrp_attribute_begin(stream->talker_attr->mrp, now); +#endif } else { if ((res = avb_maap_get_address(server->maap, stream->addr, index)) < 0) return res; +#if 0 stream->listener_attr->attr.listener.stream_id = htobe64(stream->id); stream->listener_attr->param = AVB_MSRP_LISTENER_PARAM_IGNORE; avb_mrp_attribute_begin(stream->listener_attr->mrp, now); - - stream->talker_attr->attr.talker.stream_id = htobe64(stream->id); - memcpy(stream->talker_attr->attr.talker.dest_addr, stream->addr, 6); +#endif + common->msrp_attr->attr.talker.stream_id = htobe64(stream->id); + memcpy(common->msrp_attr->attr.talker.dest_addr, stream->addr, 6); stream->sock_addr.sll_halen = ETH_ALEN; memcpy(&stream->sock_addr.sll_addr, stream->addr, ETH_ALEN); memcpy(h->dest, stream->addr, 6); memcpy(h->src, server->mac_addr, 6); - avb_mrp_attribute_begin(stream->talker_attr->mrp, now); - avb_mrp_attribute_join(stream->talker_attr->mrp, now, true); + avb_mrp_attribute_begin(common->msrp_attr->mrp, now); + avb_mrp_attribute_join(common->msrp_attr->mrp, now, true); } + pw_stream_set_active(stream->stream, true); + return 0; } int stream_deactivate(struct stream *stream, uint64_t now) { + struct stream_common *common; + common = SPA_CONTAINER_OF(stream, struct stream_common, stream); + pw_stream_set_active(stream->stream, false); if (stream->source != NULL) { pw_loop_destroy_source(stream->server->impl->loop, stream->source); stream->source = NULL; } - +#if 0 avb_mrp_attribute_leave(stream->vlan_attr->mrp, now); +#endif // + + avb_mrp_attribute_leave(common->msrp_attr->mrp, now); - if (stream->direction == SPA_DIRECTION_INPUT) { - avb_mrp_attribute_leave(stream->listener_attr->mrp, now); - } else { - avb_mrp_attribute_leave(stream->talker_attr->mrp, now); - } return 0; } diff --git a/src/modules/module-avb/stream.h b/src/modules/module-avb/stream.h index 4cc02ddd3..f15837140 100644 --- a/src/modules/module-avb/stream.h +++ b/src/modules/module-avb/stream.h @@ -61,10 +61,6 @@ struct stream { uint64_t format; uint32_t stride; struct spa_audio_info info; - - struct avb_msrp_attribute *talker_attr; - struct avb_msrp_attribute *listener_attr; - struct avb_mvrp_attribute *vlan_attr; }; #include "msrp.h"