changeset 2:5c18cd38c8ad

ft16: import from ice1-trau-tester/abis
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 29 Aug 2024 13:07:16 +0000
parents e5527fc2050b
children 19047c3302a9
files ft16/dl_frames.c ft16/dl_frames.h ft16/globals.h ft16/main.c ft16/read_ts.c ft16/record_ctrl.c ft16/submux.h ft16/subslot_rx.c ft16/tx_func.c ft16/user_cmd.c
diffstat 10 files changed, 516 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ft16/dl_frames.c	Thu Aug 29 13:07:16 2024 +0000
@@ -0,0 +1,72 @@
+/*
+ * In this module we generate canned TRAU-DL frames for FR and EFR,
+ * which will later be transmitted on Abis when individual subslots
+ * come alive.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/trau/trau_frame.h>
+#include <osmocom/trau/trau_rtp.h>
+
+#include "dl_frames.h"
+
+ubit_t dl_frame_fr[DL_OUTPUT_BUFLEN];
+ubit_t dl_frame_efr[DL_OUTPUT_BUFLEN];
+
+static const uint8_t gsmfr_silence_frame[33] = {
+	0xDA, 0xA7, 0xAA, 0xA5, 0x1A,
+	0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
+	0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
+	0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
+	0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
+};
+
+static const uint8_t efr_dhf_rtp[31] = {
+	0xC0, 0x85, 0xEB, 0x49, 0x0F, 0xAA, 0xD6, 0x03,
+	0xE3, 0xA1, 0x86, 0x07, 0xB0, 0xC4, 0x2C, 0x08,
+	0x04, 0x80, 0x55, 0x80, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static void gen_dl_fr(void)
+{
+	struct osmo_trau_frame tf;
+	struct osmo_trau2rtp_state st;
+	int rc;
+
+	tf.dir = OSMO_TRAU_DIR_DL;
+	st.type = OSMO_TRAU16_FT_FR;
+	rc = osmo_rtp2trau(&tf, gsmfr_silence_frame, 33, &st);
+	OSMO_ASSERT(rc == 0);
+	tf.dl_ta_usec = 0;
+	rc = osmo_trau_frame_encode(dl_frame_fr, DL_OUTPUT_BUFLEN, &tf);
+	OSMO_ASSERT(rc == DL_OUTPUT_LEN);
+}
+
+static void gen_dl_efr(void)
+{
+	struct osmo_trau_frame tf;
+	struct osmo_trau2rtp_state st;
+	int rc;
+
+	tf.dir = OSMO_TRAU_DIR_DL;
+	st.type = OSMO_TRAU16_FT_EFR;
+	rc = osmo_rtp2trau(&tf, efr_dhf_rtp, 31, &st);
+	OSMO_ASSERT(rc == 0);
+	tf.dl_ta_usec = 0;
+	rc = osmo_trau_frame_encode(dl_frame_efr, DL_OUTPUT_BUFLEN, &tf);
+	OSMO_ASSERT(rc == DL_OUTPUT_LEN);
+}
+
+void init_canned_dl_frames(void)
+{
+	gen_dl_fr();
+	gen_dl_efr();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ft16/dl_frames.h	Thu Aug 29 13:07:16 2024 +0000
@@ -0,0 +1,16 @@
+/*
+ * This header file defines the interface to the module that provides
+ * canned TRAU-DL frames to be transmitted on Abis as needed.
+ */
+
+#pragma once
+
+#include <osmocom/core/bits.h>
+
+#define	DL_OUTPUT_LEN		320
+#define	DL_OUTPUT_BUFLEN	640
+
+extern ubit_t dl_frame_fr[DL_OUTPUT_BUFLEN];
+extern ubit_t dl_frame_efr[DL_OUTPUT_BUFLEN];
+
+void init_canned_dl_frames(void);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ft16/globals.h	Thu Aug 29 13:07:16 2024 +0000
@@ -0,0 +1,18 @@
+/* global vars and intermodule-linkage functions in itt-abis-16 */
+
+#pragma once
+
+extern void *g_ctx;
+extern struct osmo_e1dp_client *g_client;
+extern int ts_fd;
+extern struct osmo_i460_timeslot i460_ts;
+extern uint8_t readbuf[160];
+extern FILE *record_file;
+
+int ts_fd_cb(struct osmo_fd *ofd, unsigned int what);
+void transmit_e1_ts(void);
+
+void handle_user_cmd(int argc, char **argv);
+void cmd_record_start(int argc, char **argv);
+void cmd_record_stop(int argc, char **argv);
+void cmd_print_rx(int argc, char **argv);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ft16/main.c	Thu Aug 29 13:07:16 2024 +0000
@@ -0,0 +1,127 @@
+/*
+ * This C module is the main for itt-abis-16, a test program for collecting
+ * TRAU-UL captures from an E1 BTS.  This program operates on a single E1
+ * timeslot on Abis and treats it as consisting of four 16 kbit/s subslots.
+ *
+ * This code is based on osmo-e1d-pipe,
+ * (C) 2020-2022 by Harald Welte <laforge@osmocom.org>,
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/application.h>
+#include <osmocom/e1d/proto_clnt.h>
+#include <osmocom/isdn/i460_mux.h>
+#include <osmocom/trau/trau_sync.h>
+
+#include "../libutil/open_ts.h"
+#include "../libutil/stdin_handler.h"
+#include "globals.h"
+#include "submux.h"
+#include "dl_frames.h"
+
+void *g_ctx;
+struct osmo_e1dp_client *g_client;
+int ts_fd;
+struct osmo_i460_timeslot i460_ts;
+struct abis_subslot subslots[ABIS_SUBSLOTS];
+
+static const char *e1d_socket_path = E1DP_DEFAULT_SOCKET;
+static const char *timeslot_spec;
+static struct osmo_fd ts_ofd, stdin_ofd;
+
+static void process_cmdline(int argc, char **argv)
+{
+	extern int optind;
+	extern char *optarg;
+	int c;
+
+	while ((c = getopt(argc, argv, "p:")) != EOF) {
+		switch (c) {
+		case 'p':
+			e1d_socket_path = optarg;
+			continue;
+		default:
+		usage:
+			fprintf(stderr, "usage: %s [-p socket] intf:line:ts\n",
+				argv[0]);
+			exit(1);
+		}
+	}
+	if (argc != optind + 1)
+		goto usage;
+	timeslot_spec = argv[optind];
+}
+
+static void register_subslots(void)
+{
+	int nr;
+	struct osmo_i460_schan_desc chd;
+
+	memset(&chd, 0, sizeof chd);
+	chd.rate = OSMO_I460_RATE_16k;
+	chd.demux.num_bits = 320;
+	chd.demux.out_cb_bits = i460_rx_func;
+
+	for (nr = 0; nr < ABIS_SUBSLOTS; nr++) {
+		subslots[nr].nr = nr;
+		chd.demux.user_data = subslots + nr;
+		chd.mux.user_data = subslots + nr;
+		subslots[nr].schan =
+				osmo_i460_subchan_add(g_ctx, &i460_ts, &chd);
+		OSMO_ASSERT(subslots[nr].schan);
+		chd.bit_offset += 2;
+	}
+}
+
+static void setup_rx_sync(void)
+{
+	int nr;
+
+	for (nr = 0; nr < ABIS_SUBSLOTS; nr++) {
+		subslots[nr].sync = osmo_trau_sync_alloc(g_ctx, "TRAU-UL-sync",
+					sync_rx_func, OSMO_TRAU_SYNCP_16_FR_EFR,
+					subslots + nr);
+		OSMO_ASSERT(subslots[nr].sync);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	process_cmdline(argc, argv);
+	g_ctx = talloc_named_const(NULL, 0, "g_ctx");
+	OSMO_ASSERT(g_ctx);
+	osmo_init_logging2(g_ctx, NULL);
+
+	g_client = osmo_e1dp_client_create(g_ctx, e1d_socket_path);
+	if (!g_client) {
+		fprintf(stderr, "error: cannot connect to osmo-e1d at %s\n",
+			e1d_socket_path);
+		exit(1);
+	}
+	ts_fd = open_e1d_ts(g_client, timeslot_spec);
+
+	osmo_i460_ts_init(&i460_ts);
+	register_subslots();
+	setup_rx_sync();
+	init_canned_dl_frames();
+
+	osmo_fd_setup(&ts_ofd, ts_fd, OSMO_FD_READ, ts_fd_cb, NULL, 0);
+	OSMO_ASSERT(osmo_fd_register(&ts_ofd) == 0);
+
+	osmo_fd_setup(&stdin_ofd, 0, OSMO_FD_READ, stdin_select_cb,
+			handle_user_cmd, 0);
+	OSMO_ASSERT(osmo_fd_register(&stdin_ofd) == 0);
+
+	while (1) {
+		osmo_select_main(0);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ft16/read_ts.c	Thu Aug 29 13:07:16 2024 +0000
@@ -0,0 +1,36 @@
+/*
+ * The function in this module gets called from Osmocom select loop
+ * whenever the data socket to osmo-e1d is ready for reading.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/isdn/i460_mux.h>
+
+#include "globals.h"
+
+uint8_t readbuf[160];
+FILE *record_file;
+
+int ts_fd_cb(struct osmo_fd *ofd, unsigned int what)
+{
+	int rc;
+
+	rc = read(ts_fd, readbuf, 160);
+	if (rc != 160) {
+		fprintf(stderr,
+			"error: read from ts returned %d instead of 160\n",
+			rc);
+		exit(1);
+	}
+	if (record_file)
+		fwrite(readbuf, 1, 160, record_file);
+	osmo_i460_demux_in(&i460_ts, readbuf, 160);
+	transmit_e1_ts();
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ft16/record_ctrl.c	Thu Aug 29 13:07:16 2024 +0000
@@ -0,0 +1,50 @@
+/*
+ * Here we implement stdin commands that control recording of E1 timeslot
+ * read stream.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <osmocom/core/select.h>
+
+#include "globals.h"
+
+void cmd_record_start(int argc, char **argv)
+{
+	if (argc != 2) {
+		printf("error: record command needs 1 argument\n");
+		return;
+	}
+	if (record_file) {
+		printf("error: recording already in progress\n");
+		return;
+	}
+	record_file = fopen(argv[1], "w");
+	if (!record_file)
+		perror(argv[1]);
+}
+
+void cmd_record_stop(int argc, char **argv)
+{
+	if (!record_file) {
+		printf("error: no recording in progress\n");
+		return;
+	}
+	fclose(record_file);
+	record_file = NULL;
+}
+
+void cmd_print_rx(int argc, char **argv)
+{
+	int i, j, off;
+
+	off = 0;
+	for (i = 0; i < 10; i++) {
+		for (j = 0; j < 16; j++)
+			printf(" %02X", readbuf[off++]);
+		putchar('\n');
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ft16/submux.h	Thu Aug 29 13:07:16 2024 +0000
@@ -0,0 +1,29 @@
+/*
+ * The structures and functions defined in this header file deal with
+ * submultiplexing on the Abis interface.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/isdn/i460_mux.h>
+
+#define	ABIS_SUBSLOTS	4
+
+struct abis_subslot {
+	struct osmo_i460_subchan *schan;
+	struct osmo_fsm_inst *sync;
+	int nr;
+	bool got_sync;
+	uint8_t frame_type;
+};
+
+extern struct abis_subslot subslots[ABIS_SUBSLOTS];
+
+void i460_rx_func(struct osmo_i460_subchan *schan, void *user_data,
+		  const ubit_t *bits, unsigned int num_bits);
+void sync_rx_func(void *user_data, const ubit_t *bits, unsigned int num_bits);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ft16/subslot_rx.c	Thu Aug 29 13:07:16 2024 +0000
@@ -0,0 +1,72 @@
+/*
+ * Here we are going to implement Abis subslot Rx.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/isdn/i460_mux.h>
+#include <osmocom/trau/trau_sync.h>
+
+#include "globals.h"
+#include "submux.h"
+
+void i460_rx_func(struct osmo_i460_subchan *schan, void *user_data,
+		  const ubit_t *bits, unsigned int num_bits)
+{
+	struct abis_subslot *ab = user_data;
+
+	osmo_trau_sync_rx_ubits(ab->sync, bits, num_bits);
+}
+
+static void sync_lost(struct abis_subslot *ab)
+{
+	if (!ab->got_sync)
+		return;
+	printf("Subslot %d lost frame sync\n", ab->nr);
+	ab->got_sync = false;
+}
+
+/* function copied from libosmo-abis/src/trau/trau_frame.c */
+static uint32_t get_bits(const ubit_t *bitbuf, int offset, int num)
+{
+	int i;
+	uint32_t ret = 0;
+
+	for (i = offset; i < offset + num; i++) {
+		ret = ret << 1;
+		if (bitbuf[i])
+			ret |= 1;
+	}
+	return ret;
+}
+
+void sync_rx_func(void *user_data, const ubit_t *bits, unsigned int num_bits)
+{
+	struct abis_subslot *ab = user_data;
+	uint8_t ft;
+
+	if (!bits) {
+		sync_lost(ab);
+		return;
+	}
+	OSMO_ASSERT(num_bits == 320);
+	ft = get_bits(bits, 17, 5);
+	if (!ab->got_sync) {
+		printf("Subslot %d got frame sync with FT=0x%02X\n",
+			ab->nr, ft);
+		ab->got_sync = true;
+		ab->frame_type = ft;
+		return;
+	}
+	if (ft == ab->frame_type)
+		return;
+	printf("Subslot %d changed frame type from 0x%02X to 0x%02X\n", ab->nr,
+		ab->frame_type, ft);
+	ab->frame_type = ft;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ft16/tx_func.c	Thu Aug 29 13:07:16 2024 +0000
@@ -0,0 +1,57 @@
+/*
+ * Here we are going to implement Tx on Abis toward the BTS.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/isdn/i460_mux.h>
+#include <osmocom/trau/trau_frame.h>
+
+#include "globals.h"
+#include "submux.h"
+#include "dl_frames.h"
+
+static void tx_service_subslot(int nr)
+{
+	struct abis_subslot *ab = &subslots[nr];
+	const uint8_t *srcbuf;
+	struct msgb *msg;
+	uint8_t *outbuf;
+
+	if (!ab->got_sync)
+		return;
+	switch (ab->frame_type) {
+	case TRAU_FT_FR_UP:
+		srcbuf = dl_frame_fr;
+		break;
+	case TRAU_FT_EFR:
+		srcbuf = dl_frame_efr;
+		break;
+	default:
+		return;
+	}
+	msg = msgb_alloc_c(g_ctx, DL_OUTPUT_LEN, "TRAU-DL-frame");
+	if (!msg)
+		return;
+	outbuf = msgb_put(msg, DL_OUTPUT_LEN);
+	memcpy(outbuf, srcbuf, DL_OUTPUT_LEN);
+	osmo_i460_mux_enqueue(ab->schan, msg);
+}
+
+void transmit_e1_ts(void)
+{
+	uint8_t buf[160];
+	int nr;
+
+	for (nr = 0; nr < ABIS_SUBSLOTS; nr++)
+		tx_service_subslot(nr);
+	osmo_i460_mux_out(&i460_ts, buf, 160);
+	write(ts_fd, buf, 160);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ft16/user_cmd.c	Thu Aug 29 13:07:16 2024 +0000
@@ -0,0 +1,39 @@
+/*
+ * In this module we handle user-issued stdin commands during
+ * itt-abis-16 running session.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osmocom/core/select.h>
+
+#include "globals.h"
+
+static struct cmdtab {
+	char	*cmd;
+	void	(*func)(int argc, char **argv);
+} cmdtab[] = {
+	{"print-rx", cmd_print_rx},
+	{"record", cmd_record_start},
+	{"record-stop", cmd_record_stop},
+	/* table search terminator */
+	{NULL, NULL}
+};
+
+void handle_user_cmd(int argc, char **argv)
+{
+	struct cmdtab *tp;
+
+	for (tp = cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, argv[0]))
+			break;
+	if (!tp->func) {
+		printf("error: unknown or unimplemented command\n");
+		return;
+	}
+	tp->func(argc, argv);
+}