Skip to content

Commit 381fb70

Browse files
committed
protocol(p25,audio,tests): keep P25P2 ENC audio gating aligned with media policy
1 parent a8e4465 commit 381fb70

9 files changed

Lines changed: 305 additions & 81 deletions

File tree

include/dsd-neo/core/audio.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ void audio_mix_mono_from_slots_f32(const float* left, const float* right, size_t
180180

181181
/** @brief Simple P25 P2 per-slot mixer gate used by tests (maps p25_p2_audio_allowed -> enc flags). */
182182
int dsd_p25p2_mixer_gate(const dsd_state* state, int* encL, int* encR);
183+
/** @brief Return 1 when P25p2 decode should queue audio for the slot under decrypt and media policy. */
184+
int dsd_p25p2_decode_audio_allowed(const dsd_opts* opts, const dsd_state* state, int slot, int alg);
183185

184186
/** @brief Flush partially buffered P25p2 SS18 audio on call end/release. */
185187
void dsd_p25p2_flush_partial_audio(dsd_opts* opts, dsd_state* state);

src/core/audio/dsd_audio_gate.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,76 @@ dsd_p25p2_mixer_gate(const dsd_state* state, int* encL, int* encR) {
7272
return 0;
7373
}
7474

75+
static int
76+
dsd_p25p2_slot_can_decrypt(const dsd_state* state, int slot, int alg) {
77+
unsigned long long key = 0;
78+
if (!state || slot < 0 || slot > 1) {
79+
return 0;
80+
}
81+
if (alg == 0 || alg == 0x80) {
82+
return 1;
83+
}
84+
key = (slot == 0) ? state->R : state->RR;
85+
if ((alg == 0xAA || alg == 0x81 || alg == 0x9F) && key != 0ULL) {
86+
return 1;
87+
}
88+
if ((alg == 0x84 || alg == 0x89) && state->aes_key_loaded[slot] == 1) {
89+
return 1;
90+
}
91+
return 0;
92+
}
93+
94+
static int
95+
dsd_p25p2_media_decision_allows_audio(const dsd_tg_policy_decision* decision) {
96+
if (!decision) {
97+
return 1;
98+
}
99+
if (decision->tg_hold_active && decision->tg_hold_match) {
100+
return 1;
101+
}
102+
if (!decision->audio_allowed || (decision->block_reasons & DSD_TG_POLICY_BLOCK_ALLOWLIST) != 0u) {
103+
return 0;
104+
}
105+
return 1;
106+
}
107+
108+
int
109+
dsd_p25p2_decode_audio_allowed(const dsd_opts* opts, const dsd_state* state, int slot, int alg) {
110+
dsd_tg_policy_decision decision;
111+
int raw_target = 0;
112+
int raw_source = 0;
113+
uint32_t target = 0;
114+
uint32_t source = 0;
115+
116+
if (!state || slot < 0 || slot > 1) {
117+
return 0;
118+
}
119+
if (!dsd_p25p2_slot_can_decrypt(state, slot, alg)) {
120+
return 0;
121+
}
122+
123+
raw_target = (slot == 0) ? state->lasttg : state->lasttgR;
124+
raw_source = (slot == 0) ? state->lastsrc : state->lastsrcR;
125+
target = (raw_target > 0) ? (uint32_t)raw_target : 0u;
126+
source = (raw_source > 0) ? (uint32_t)raw_source : 0u;
127+
if (state->gi[slot] == 1) {
128+
if (dsd_tg_policy_evaluate_private_call(opts, state, source, target, 0, 0,
129+
DSD_TG_POLICY_PRIVATE_ALLOWLIST_UNKNOWN_BLOCK,
130+
DSD_TG_POLICY_HOLD_FORCE_MEDIA_ONLY, &decision)
131+
== 0) {
132+
return dsd_p25p2_media_decision_allows_audio(&decision);
133+
}
134+
} else {
135+
if (dsd_tg_policy_evaluate_group_call(opts, state, target, source, 0, 0, DSD_TG_POLICY_HOLD_FORCE_MEDIA_ONLY,
136+
&decision)
137+
== 0) {
138+
return dsd_p25p2_media_decision_allows_audio(&decision);
139+
}
140+
}
141+
142+
return 0;
143+
}
144+
75145
int
76146
dsd_audio_group_gate_mono(const dsd_opts* opts, const dsd_state* state, unsigned long tg, int enc_in, int* enc_out) {
77147
dsd_tg_policy_decision decision;

src/protocol/p25/p25_trunk_sm.c

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Unified P25 trunking state machine.
66
*/
77

8+
#include <dsd-neo/core/audio.h>
89
#include <dsd-neo/core/dsd_time.h>
910
#include <dsd-neo/core/opts.h>
1011
#include <dsd-neo/core/state.h>
@@ -570,16 +571,21 @@ handle_enc(p25_sm_ctx_t* ctx, dsd_opts* opts, dsd_state* state, const p25_sm_eve
570571
int slot = (ev->slot >= 0 && ev->slot <= 1) ? ev->slot : 0;
571572
int algid = ev->algid;
572573
int tg = ev->tg;
574+
int can_decrypt = 0;
575+
int allow_audio = 0;
573576

574577
// Store encryption params in slot context
575578
ctx->slots[slot].algid = algid;
576579
ctx->slots[slot].keyid = ev->keyid;
577580
ctx->slots[slot].tg = tg;
581+
can_decrypt = slot_can_decrypt(state, slot, algid);
582+
allow_audio = dsd_p25p2_decode_audio_allowed(opts, state, slot, algid);
578583

579584
// Skip lockout processing if encryption lockout is disabled
580585
if (opts->trunk_tune_enc_calls != 0) {
581-
// Still update audio gating based on decryptability
582-
state->p25_p2_audio_allowed[slot] = slot_can_decrypt(state, slot, algid) ? 1 : 0;
586+
// Even when encrypted calls are permitted, keep the gate aligned with
587+
// the current media policy so allow-list/TG-hold blocks are not reopened.
588+
state->p25_p2_audio_allowed[slot] = allow_audio;
583589
return;
584590
}
585591

@@ -588,9 +594,10 @@ handle_enc(p25_sm_ctx_t* ctx, dsd_opts* opts, dsd_state* state, const p25_sm_eve
588594
return;
589595
}
590596

591-
// Skip if stream is clear or we have a key
592-
if (slot_can_decrypt(state, slot, algid)) {
593-
state->p25_p2_audio_allowed[slot] = 1;
597+
// Decryptable encrypted calls still need to respect the current media
598+
// policy; do not reopen slots that the caller already blocked.
599+
if (can_decrypt) {
600+
state->p25_p2_audio_allowed[slot] = allow_audio;
594601
return;
595602
}
596603

@@ -1234,21 +1241,14 @@ p25_emit_enc_lockout_once(dsd_opts* opts, dsd_state* state, uint8_t slot, int tg
12341241
return; // already marked; event previously emitted
12351242
}
12361243

1237-
// Prepare per-slot context and clear encryption display variables for this slot
1244+
// Prepare per-slot context. Keep live encryption fields intact: callers may
1245+
// still need ALG/KID/MI after this helper returns to keep audio gates closed.
12381246
if ((slot & 1) == 0) {
12391247
state->lasttg = (uint32_t)tg;
12401248
state->dmr_so = (uint16_t)svc_bits;
1241-
// Clear slot 0 encryption display variables to prevent stale UI
1242-
state->payload_algid = 0;
1243-
state->payload_keyid = 0;
1244-
state->payload_miP = 0;
12451249
} else {
12461250
state->lasttgR = (uint32_t)tg;
12471251
state->dmr_soR = (uint16_t)svc_bits;
1248-
// Clear slot 1 encryption display variables to prevent stale UI
1249-
state->payload_algidR = 0;
1250-
state->payload_keyidR = 0;
1251-
state->payload_miN = 0;
12521252
}
12531253
state->gi[slot & 1] = 0;
12541254

src/protocol/p25/phase2/p25p2_frame.c

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,31 @@ p25_p2_s16_frames_have_audio(short frames[18][160]) {
6161
return 0;
6262
}
6363

64+
static int
65+
p25p2_frame_slot_audio_allowed(const dsd_opts* opts, const dsd_state* state, int slot, int alg) {
66+
#if defined(DSD_NEO_P25P2_TEST_STUB)
67+
unsigned long long key = 0;
68+
(void)opts;
69+
70+
if (!state || slot < 0 || slot > 1) {
71+
return 0;
72+
}
73+
if (alg == 0 || alg == 0x80) {
74+
return 1;
75+
}
76+
key = (slot == 0) ? state->R : state->RR;
77+
if ((alg == 0xAA || alg == 0x81 || alg == 0x9F) && key != 0ULL) {
78+
return 1;
79+
}
80+
if ((alg == 0x84 || alg == 0x89) && state->aes_key_loaded[slot] == 1) {
81+
return 1;
82+
}
83+
return 0;
84+
#else
85+
return dsd_p25p2_decode_audio_allowed(opts, state, slot, alg);
86+
#endif
87+
}
88+
6489
// Clear per-slot audio gates, small audio rings, encryption indicators, and
6590
// UI call banners for both logical slots. Intended for use on call teardown
6691
// before returning to the control channel.
@@ -840,12 +865,7 @@ process_ESS(dsd_opts* opts, dsd_state* state) {
840865
// protecting against stale re-enables after teardown.
841866
int in_call = ((state->dmrburstL >= 20 && state->dmrburstL <= 22) || voice);
842867
int alg = state->payload_algid;
843-
int allow = (in_call
844-
&& ((alg == 0 || alg == 0x80)
845-
|| (((alg == 0xAA || alg == 0x81 || alg == 0x9F) && state->R != 0)
846-
|| ((alg == 0x84 || alg == 0x89) && state->aes_key_loaded[0] == 1))))
847-
? 1
848-
: 0;
868+
int allow = in_call && p25p2_frame_slot_audio_allowed(opts, state, 0, alg);
849869
if (allow) {
850870
state->p25_p2_audio_allowed[0] = 1;
851871
}
@@ -891,12 +911,7 @@ process_ESS(dsd_opts* opts, dsd_state* state) {
891911
// context for the right slot, mirroring left-slot handling.
892912
int in_call = ((state->dmrburstR >= 20 && state->dmrburstR <= 22) || voice);
893913
int alg = state->payload_algidR;
894-
int allow = (in_call
895-
&& ((alg == 0 || alg == 0x80)
896-
|| (((alg == 0xAA || alg == 0x81 || alg == 0x9F) && state->RR != 0)
897-
|| ((alg == 0x84 || alg == 0x89) && state->aes_key_loaded[1] == 1))))
898-
? 1
899-
: 0;
914+
int allow = in_call && p25p2_frame_slot_audio_allowed(opts, state, 1, alg);
900915
if (allow) {
901916
state->p25_p2_audio_allowed[1] = 1;
902917
}

src/protocol/p25/phase2/p25p2_vpdu.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2486,8 +2486,9 @@ process_MAC_VPDU(dsd_opts* opts, dsd_state* state, int type, unsigned long long
24862486
// Mark and emit once via centralized helper
24872487
p25_emit_enc_lockout_once(opts, state, (uint8_t)slot, ttg, /*svc_bits*/ 0);
24882488
}
2489-
// Gate this slot only
2489+
// Gate this slot only and flush any queued audio to avoid residue
24902490
state->p25_p2_audio_allowed[slot] = 0;
2491+
p25_p2_audio_ring_reset(state, slot);
24912492
int other = slot ^ 1;
24922493
int other_audio = state->p25_p2_audio_allowed[other] || state->p25_p2_audio_ring_count[other] > 0;
24932494
if (!other_audio) {

src/protocol/p25/phase2/p25p2_xcch.c

Lines changed: 24 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* 2022-09 DSD-FME Florida Man Edition
1111
*-----------------------------------------------------------------------------*/
1212

13+
#include <dsd-neo/core/audio.h>
1314
#include <dsd-neo/core/constants.h>
1415
#include <dsd-neo/core/dsd_time.h>
1516
#include <dsd-neo/core/file_io.h>
@@ -28,6 +29,23 @@
2829
#include "dsd-neo/core/opts_fwd.h"
2930
#include "dsd-neo/core/state_fwd.h"
3031

32+
static int
33+
p25p2_xcch_slot_audio_allowed(const dsd_opts* opts, const dsd_state* state, int slot) {
34+
int alg = 0;
35+
36+
if (!state || slot < 0 || slot > 1) {
37+
return 0;
38+
}
39+
40+
if (slot == 0) {
41+
alg = state->payload_algid;
42+
} else {
43+
alg = state->payload_algidR;
44+
}
45+
46+
return dsd_p25p2_decode_audio_allowed(opts, state, slot, alg);
47+
}
48+
3149
void
3250
process_SACCH_MAC_PDU(dsd_opts* opts, dsd_state* state, int payload[180]) {
3351
//Figure out which PDU we are looking at, see above info on 8.4.1
@@ -216,14 +234,7 @@ process_SACCH_MAC_PDU(dsd_opts* opts, dsd_state* state, int payload[180]) {
216234
}
217235
// Conditionally enable audio only if clear or decryptable
218236
{
219-
int allow_audio = 0;
220-
int alg = (slot == 0) ? state->payload_algid : state->payload_algidR;
221-
unsigned long long key = (slot == 0) ? state->R : state->RR;
222-
int aes_loaded = state->aes_key_loaded[slot];
223-
if (alg == 0 || alg == 0x80 || ((alg == 0xAA || alg == 0x81 || alg == 0x9F) && key != 0)
224-
|| ((alg == 0x84 || alg == 0x89) && aes_loaded == 1)) {
225-
allow_audio = 1; // clear or decryptable with key
226-
}
237+
int allow_audio = p25p2_xcch_slot_audio_allowed(opts, state, slot);
227238
state->p25_p2_audio_allowed[slot] = allow_audio;
228239
// Only set PTT burst indicator when audio is allowed; encrypted/locked-out
229240
// calls should not influence audio routing decisions
@@ -295,14 +306,7 @@ process_SACCH_MAC_PDU(dsd_opts* opts, dsd_state* state, int payload[180]) {
295306
}
296307
// Conditionally enable audio only if clear or decryptable
297308
{
298-
int allow_audio = 0;
299-
int alg = (slot == 0) ? state->payload_algid : state->payload_algidR;
300-
unsigned long long key = (slot == 0) ? state->R : state->RR;
301-
int aes_loaded = state->aes_key_loaded[slot];
302-
if (alg == 0 || alg == 0x80 || ((alg == 0xAA || alg == 0x81 || alg == 0x9F) && key != 0)
303-
|| ((alg == 0x84 || alg == 0x89) && aes_loaded == 1)) {
304-
allow_audio = 1;
305-
}
309+
int allow_audio = p25p2_xcch_slot_audio_allowed(opts, state, slot);
306310
state->p25_p2_audio_allowed[slot] = allow_audio;
307311
// Only set PTT burst indicator when audio is allowed; encrypted/locked-out
308312
// calls should not influence audio routing decisions
@@ -502,18 +506,7 @@ process_SACCH_MAC_PDU(dsd_opts* opts, dsd_state* state, int payload[180]) {
502506
fprintf(stderr, "%s", KNRM);
503507
// Enable audio per policy (respect encryption, key presence, and ignore stale packet bit when clear)
504508
{
505-
int allow_audio = 0;
506-
int alg = (slot == 0) ? state->payload_algid : state->payload_algidR;
507-
unsigned long long key = (slot == 0) ? state->R : state->RR;
508-
int aes_loaded = state->aes_key_loaded[slot];
509-
// If stream is clear or decryptable, enable regardless of a stale Packet/Data flag
510-
if (alg == 0 || alg == 0x80 || ((alg == 0xAA || alg == 0x81 || alg == 0x9F) && key != 0)
511-
|| ((alg == 0x84 || alg == 0x89) && aes_loaded == 1)) {
512-
allow_audio = 1;
513-
} else {
514-
// Otherwise, suppress audio for Packet/Data sessions
515-
allow_audio = state->p25_call_is_packet[slot] ? 0 : allow_audio;
516-
}
509+
int allow_audio = p25p2_xcch_slot_audio_allowed(opts, state, slot);
517510
state->p25_p2_audio_allowed[slot] = allow_audio;
518511

519512
// Only set voice-active burst indicator when audio is allowed;
@@ -687,14 +680,7 @@ process_FACCH_MAC_PDU(dsd_opts* opts, dsd_state* state, int payload[156]) {
687680
}
688681
// Conditionally enable audio only if clear or decryptable
689682
{
690-
int allow_audio = 0;
691-
int alg = (slot == 0) ? state->payload_algid : state->payload_algidR;
692-
unsigned long long key = (slot == 0) ? state->R : state->RR;
693-
int aes_loaded = state->aes_key_loaded[slot];
694-
if (alg == 0 || alg == 0x80 || ((alg == 0xAA || alg == 0x81 || alg == 0x9F) && key != 0)
695-
|| ((alg == 0x84 || alg == 0x89) && aes_loaded == 1)) {
696-
allow_audio = 1; // clear or decryptable with key
697-
}
683+
int allow_audio = p25p2_xcch_slot_audio_allowed(opts, state, slot);
698684
state->p25_p2_audio_allowed[slot] = allow_audio;
699685
}
700686
}
@@ -761,14 +747,7 @@ process_FACCH_MAC_PDU(dsd_opts* opts, dsd_state* state, int payload[156]) {
761747
}
762748
// Conditionally enable audio only if clear or decryptable
763749
{
764-
int allow_audio = 0;
765-
int alg = (slot == 0) ? state->payload_algid : state->payload_algidR;
766-
unsigned long long key = (slot == 0) ? state->R : state->RR;
767-
int aes_loaded = state->aes_key_loaded[slot];
768-
if (alg == 0 || alg == 0x80 || ((alg == 0xAA || alg == 0x81 || alg == 0x9F) && key != 0)
769-
|| ((alg == 0x84 || alg == 0x89) && aes_loaded == 1)) {
770-
allow_audio = 1;
771-
}
750+
int allow_audio = p25p2_xcch_slot_audio_allowed(opts, state, slot);
772751
state->p25_p2_audio_allowed[slot] = allow_audio;
773752
}
774753
}
@@ -961,16 +940,7 @@ process_FACCH_MAC_PDU(dsd_opts* opts, dsd_state* state, int payload[156]) {
961940
p25_sm_emit_active(opts, state, slot);
962941
// Enable audio per policy (respect encryption, key presence, and ignore stale packet bit when clear)
963942
{
964-
int allow_audio = 0;
965-
int alg = (slot == 0) ? state->payload_algid : state->payload_algidR;
966-
unsigned long long key = (slot == 0) ? state->R : state->RR;
967-
int aes_loaded = state->aes_key_loaded[slot];
968-
if (alg == 0 || alg == 0x80 || ((alg == 0xAA || alg == 0x81 || alg == 0x9F) && key != 0)
969-
|| ((alg == 0x84 || alg == 0x89) && aes_loaded == 1)) {
970-
allow_audio = 1; // clear or decryptable with key
971-
} else {
972-
allow_audio = state->p25_call_is_packet[slot] ? 0 : allow_audio;
973-
}
943+
int allow_audio = p25p2_xcch_slot_audio_allowed(opts, state, slot);
974944
state->p25_p2_audio_allowed[slot] = allow_audio;
975945
}
976946

0 commit comments

Comments
 (0)