view src/cs/layer1/cfile/l1_tch_tap.c @ 300:edcb8364d45b

L1: resurrect TCH tap feature In this new incarnation of our TCH tap feature, we support DL sniffing in all 3 of FR1, HR1 and EFR, and the new implementation will capture every 20 ms frame where the old one silently skipped a frame (sent nothing) during FACCH stealing. The wire interface on RVTMUX changed slightly, and fc-shell tch record will need to be updated to support the new version. TCH UL play or substitution is supported for FR1 and EFR only; support for HR1 can be added later if needed.
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 13 Dec 2022 02:44:01 +0000
parents
children
line wrap: on
line source

/*
 * This module is a FreeCalypso addition; it contains code implementing
 * our TCH tap functional additions: TCH downlink capture and TCH uplink
 * play.
 */

#include "l1_macro.h"
#include "l1_confg.h"
#include "l1_types.h"
#include "sys_types.h"
#include <string.h>
#include "l1_const.h"

#if TESTMODE
  #include "l1tm_defty.h"
#endif
#if (AUDIO_TASK == 1)
  #include "l1audio_const.h"
  #include "l1audio_cust.h"
  #include "l1audio_defty.h"
#endif
#if (L1_GTT == 1)
  #include "l1gtt_const.h"
  #include "l1gtt_defty.h"
#endif
#if (L1_MIDI == 1)
  #include "l1midi_defty.h"
#endif
#include "l1_defty.h"

#include "l1_varex.h"
#include "l1_trace.h"
#include "tch_tap_proto.h"

T_RVT_USER_ID tch_tap_rvt_id;
BOOL tch_dl_sniff_mode, tch_ul_play_mode;

void tch_send_downlink_bits(API *dsp_buffer, UWORD8 nwords, UWORD8 chan_mode,
			    UWORD8 fn_mod_104)
{
	UWORD16 size;
	T_RVT_BUFFER buf;
	T_RVT_RET rc;
	UINT8 *dp;
	UWORD16 apiword;
	int i;

	size = nwords * 2 + 3;
	rc = rvt_mem_alloc(tch_tap_rvt_id, size, &buf);
	if (rc != RVT_OK)
		return;
	dp = buf;
	*dp++ = TCH_DLBITS_NEW_IND;
	*dp++ = chan_mode;
	*dp++ = fn_mod_104;
	for (i = 0; i < nwords; i++) {
		apiword = dsp_buffer[i];
		*dp++ = apiword >> 8;
		*dp++ = apiword;
	}
	rvt_send_trace_no_cpy(buf, tch_tap_rvt_id, size, RVT_BINARY_FORMAT);
}

static void send_tch_ulbits_conf(void)
{
	T_RVT_BUFFER buf;
	T_RVT_RET rc;

	rc = rvt_mem_alloc(tch_tap_rvt_id, 1, &buf);
	if (rc == RVT_OK) {
		buf[0] = TCH_ULBITS_CONF;
		rvt_send_trace_no_cpy(buf, tch_tap_rvt_id, 1,
					RVT_BINARY_FORMAT);
	}
}

#define	UPLINK_QUEUE_SIZE	5
#define	WORDS_PER_ENTRY		17

static UWORD16 uplink_data[UPLINK_QUEUE_SIZE][WORDS_PER_ENTRY];
static volatile int ul_read_ptr, ul_write_ptr;

void tchf_substitute_uplink(API *dsp_buffer)
{
	int read_ptr;
	int i;

	read_ptr = ul_read_ptr;
	if (read_ptr == ul_write_ptr) {
		/* no uplink substitution */
		l1s_dsp_com.dsp_ndb_ptr->d_tch_mode &= ~B_PLAY_UL;
		tch_ul_play_mode = FALSE;
		return;
	}
	for (i = 0; i < WORDS_PER_ENTRY; i++)
		dsp_buffer[i+3] = uplink_data[read_ptr][i];
	// Fill data block Header...
	dsp_buffer[0] = (1 << B_BLUD);		// 1st word: Set B_BLU bit.
	dsp_buffer[1] = 0;			// 2nd word: cleared.
	dsp_buffer[2] = 0;			// 3rd word: cleared.
	l1s_dsp_com.dsp_ndb_ptr->d_tch_mode |= B_PLAY_UL;
	/* advance the read pointer and send TCH_ULBITS_CONF */
	read_ptr++;
	if (read_ptr >= UPLINK_QUEUE_SIZE)
		read_ptr = 0;
	ul_read_ptr = read_ptr;
	send_tch_ulbits_conf();
}

static void handle_tch_ulbits_req(T_RVT_BUFFER pkt)
{
	int write_ptr, write_next, i;
	UINT8 *sp;

	write_ptr = ul_write_ptr;
	write_next = write_ptr + 1;
	if (write_next >= UPLINK_QUEUE_SIZE)
		write_next = 0;
	if (write_next == ul_read_ptr)	/* queue full */
		return;
	sp = pkt + 1;
	for (i = 0; i < WORDS_PER_ENTRY; i++) {
		uplink_data[write_ptr][i] = (sp[0] << 8) | sp[1];
		sp += 2;
	}
	ul_write_ptr = write_next;
	tch_ul_play_mode = TRUE;
}

static void handle_tch_config_req(T_RVT_BUFFER pkt)
{
	UWORD8 config;
	T_RVT_BUFFER buf;
	T_RVT_RET rc;

	config = pkt[1] & 0x01;
	tch_dl_sniff_mode = config;

	/* send TCH_CONFIG_CONF response */
	rc = rvt_mem_alloc(tch_tap_rvt_id, 2, &buf);
	if (rc == RVT_OK) {
		buf[0] = TCH_CONFIG_CONF;
		buf[1] = config;
		rvt_send_trace_no_cpy(buf, tch_tap_rvt_id, 2,
					RVT_BINARY_FORMAT);
	}
}

/*
 * The following function is the callback registered with RVT; it gets
 * called in RVT HISR context.
 */
static void tch_rvt_input_callback(T_RVT_BUFFER pkt, UINT16 pktlen)
{
	if (pktlen < 1)
		return;
	switch (pkt[0]) {
	case TCH_CONFIG_REQ:
		if (pktlen != 2)
			return;
		handle_tch_config_req(pkt);
		break;
	case TCH_ULBITS_REQ:
		if (pktlen < 34)
			return;
		handle_tch_ulbits_req(pkt);
		break;
	}
}

void tch_tap_init(void)
{
	rvt_register_id("TCH", &tch_tap_rvt_id, tch_rvt_input_callback);
	tch_dl_sniff_mode = FALSE;
	tch_ul_play_mode = FALSE;
	ul_read_ptr = 0;
	ul_write_ptr = 0;
}