FreeCalypso > hg > ice1-trau-tester
changeset 42:ff94d7fc5891
new program itt-ater-8
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Fri, 30 Aug 2024 19:02:42 +0000 |
parents | 50a72d4ff498 |
children | 55f02d4aee79 |
files | .hgignore ater8/Makefile ater8/activate.c ater8/globals.h ater8/main.c ater8/out_frame.c ater8/out_frame.h ater8/play_cmd.c ater8/read_file.c ater8/read_file.h ater8/read_ts.c ater8/record_ctrl.c ater8/submux.h ater8/subslot_rx.c ater8/tx_func.c ater8/user_cmd.c |
diffstat | 16 files changed, 816 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Fri Aug 30 16:42:50 2024 +0000 +++ b/.hgignore Fri Aug 30 19:02:42 2024 +0000 @@ -4,5 +4,6 @@ ^config\.defs$ ^ater/itt-ater-16$ +^ater8/itt-ater-8$ ^pcm/itt-pcm-one$ ^pcm-br/itt-pcm-br$
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ater8/Makefile Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,26 @@ +PROG= itt-ater-8 +OBJS= activate.o main.o out_frame.o play_cmd.o read_file.o read_ts.o \ + record_ctrl.o subslot_rx.o tx_func.o user_cmd.o +HDRS= globals.h out_frame.h read_file.h submux.h +LIBUTIL=../libutil/libutil.a +LIBHR= ../libhr/libhr.a + +include ../config.defs + +CPPFLAGS=${OSMO_INCLUDE} +OSMO_LINK=${OSMO_LPATH} ${OSMO_RPATH} ${OSMO_LIBS} +LOCAL_LIBS=${LIBUTIL} ${LIBHR} + +all: ${PROG} + +${OBJS}: ${HDRS} + +${PROG}: ${OBJS} ${LOCAL_LIBS} + ${CC} -o $@ ${OBJS} ${LOCAL_LIBS} ${OSMO_LINK} + +install: + mkdir -p ${DESTDIR}${bindir} + install -c -m 755 ${PROG} ${DESTDIR}${bindir} + +clean: + rm -f *.o ${PROG} errs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ater8/activate.c Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,89 @@ +/* + * Here we implement the operation of activating a new TRAU channel + * on a sub-timeslot. + */ + +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.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 "read_file.h" +#include "out_frame.h" + +void cmd_activate(int argc, char **argv) +{ + int nr, rc; + bool dtxd; + struct ater_subslot *at; + int16_t *init_frame; + unsigned init_frame_count; + + if (argc < 3 || argc > 4) { +usage: fprintf(stderr, "usage: %s 0-7 initial-frame.dec [dtxd]\n", + argv[0]); + return; + } + if (argv[1][0] < '0' || argv[1][0] > '7' || argv[1][1]) + goto usage; + nr = argv[1][0] - '0'; + if (argv[3]) { + if (strcmp(argv[3], "dtxd")) + goto usage; + dtxd = true; + } else + dtxd = false; + + at = &subslots[nr]; + if (at->is_active) { + fprintf(stderr, "error: subslot %d is already active\n", nr); + return; + } + rc = read_binary_file(argv[2], &init_frame, &init_frame_count); + if (rc < 0) + return; /* error msg already printed */ + if (init_frame_count != 1) { + free(init_frame); + fprintf(stderr, "error: %s contains more than one frame\n", + argv[2]); + return; + } + + /* good to proceed now */ + at->is_active = true; + init_trau_ul_frame(nr); + at->ul_frame.c_bits[8] = dtxd; + trau_frame_from_record(init_frame, &at->ul_frame, &at->frame_has_taf); + free(init_frame); +} + +void cmd_deact(int argc, char **argv) +{ + int nr; + struct ater_subslot *at; + + if (argc != 2) { +usage: fprintf(stderr, "usage: %s 0-7\n", argv[0]); + return; + } + if (argv[1][0] < '0' || argv[1][0] > '7' || argv[1][1]) + goto usage; + nr = argv[1][0] - '0'; + at = &subslots[nr]; + if (!at->is_active) { + fprintf(stderr, "error: subslot %d is not active\n", nr); + return; + } + at->is_active = false; + if (at->play_buffer) { + free(at->play_buffer); + at->play_buffer = NULL; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ater8/globals.h Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,22 @@ +/* global vars and intermodule-linkage functions in itt-ater-8 */ + +#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); +void cmd_activate(int argc, char **argv); +void cmd_deact(int argc, char **argv); +void cmd_play_file(int argc, char **argv); +void cmd_play_stop(int argc, char **argv);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ater8/main.c Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,110 @@ +/* + * This C module is the main for itt-ater-8, a program in the icE1 TRAU tester + * suite that operates on a single E1 timeslot on the Ater interface. + * + * 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 "../libutil/open_ts.h" +#include "../libutil/stdin_handler.h" +#include "globals.h" +#include "submux.h" + +void *g_ctx; +struct osmo_e1dp_client *g_client; +int ts_fd; +struct osmo_i460_timeslot i460_ts; +struct ater_subslot subslots[ATER_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_8k; + chd.demux.num_bits = 160; + chd.demux.out_cb_bits = i460_rx_func; + + for (nr = 0; nr < ATER_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 += 1; + } +} + +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(); + + 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/ater8/out_frame.c Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,80 @@ +/* + * Here we implement our TRAU-UL bit filling function for HRv1 8 kbit/s format. + * + * This code is based (very loosely) on trau_rtp_conv.c in libosmo-abis. + */ + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include <osmocom/core/bits.h> +#include <osmocom/core/crc8gen.h> +#include <osmocom/core/utils.h> +#include <osmocom/trau/trau_frame.h> +#include "../libhr/tw_gsmhr.h" + +#include "out_frame.h" + +/* + * EFR TRAU parity (also used for HR) + * + * g(x) = x^3 + x^1 + 1 + */ +static const struct osmo_crc8gen_code gsm0860_efr_crc3 = { + .bits = 3, + .poly = 0x3, + .init = 0x0, + .remainder = 0x7, +}; + +void trau_frame_from_record(const int16_t *rec, struct osmo_trau_frame *tf, + bool *has_taf) +{ + uint8_t ts101318[GSMHR_FRAME_LEN_RPF]; + int16_t bfi, sid; + + /* payload transformation is straightforward */ + gsmhr_pack_ts101318(rec, ts101318); + osmo_pbit2ubit(tf->d_bits, ts101318, 14 * 8); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, tf->d_bits, 44, tf->crc_bits); + + /* transformation of metadata flags is the messy part */ + bfi = rec[18]; + sid = rec[20]; + switch (sid) { + case 0: + if (bfi) { + tf->xc_bits[1] = 1; + tf->xc_bits[2] = 1; + *has_taf = true; + } else { + tf->xc_bits[1] = 0; + tf->xc_bits[2] = 0; + tf->xc_bits[3] = 0; + *has_taf = false; + } + break; + case 1: + tf->xc_bits[1] = 1; + tf->xc_bits[2] = 0; + *has_taf = true; + break; + case 2: + if (bfi) { + tf->xc_bits[1] = 1; + tf->xc_bits[2] = 0; + *has_taf = true; + } else { + tf->xc_bits[1] = 0; + tf->xc_bits[2] = 0; + tf->xc_bits[3] = 1; + *has_taf = false; + } + break; + default: + OSMO_ASSERT(0); + } + /* XC5 is always UFI */ + tf->xc_bits[4] = rec[19]; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ater8/out_frame.h Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,15 @@ +/* + * This header file defines the interface to the function that + * fills struct osmo_trau_frame based on a 22-word decoder input + * record we read from an ETSI *.dec file. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/trau/trau_frame.h> + +void trau_frame_from_record(const int16_t *rec, struct osmo_trau_frame *fr, + bool *has_taf);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ater8/play_cmd.c Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,68 @@ +/* + * Here we implement user commands controlling file play functionality. + */ + +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <osmocom/core/select.h> + +#include "globals.h" +#include "submux.h" +#include "read_file.h" + +void cmd_play_file(int argc, char **argv) +{ + int nr, rc; + struct ater_subslot *at; + + if (argc != 3) { +usage: fprintf(stderr, "usage: %s 0-7 play-file.dec\n", argv[0]); + return; + } + if (argv[1][0] < '0' || argv[1][0] > '7' || argv[1][1]) + goto usage; + nr = argv[1][0] - '0'; + at = &subslots[nr]; + if (!at->is_active) { + fprintf(stderr, "error: subslot %d is not active\n", nr); + return; + } + if (at->play_buffer) { + fprintf(stderr, "error: file play already in progress\n"); + return; + } + rc = read_binary_file(argv[2], &at->play_buffer, &at->play_buf_total); + if (rc < 0) + return; /* error msg already printed */ + at->play_buf_ptr = 0; + at->play_wait_align = true; +} + +void cmd_play_stop(int argc, char **argv) +{ + int nr; + struct ater_subslot *at; + + if (argc != 2) { +usage: fprintf(stderr, "usage: %s 0-7\n", argv[0]); + return; + } + if (argv[1][0] < '0' || argv[1][0] > '7' || argv[1][1]) + goto usage; + nr = argv[1][0] - '0'; + at = &subslots[nr]; + if (!at->is_active) { + fprintf(stderr, "error: subslot %d is not active\n", nr); + return; + } + if (!at->play_buffer) { + fprintf(stderr, "error: no file play in progress\n"); + return; + } + free(at->play_buffer); + at->play_buffer = NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ater8/read_file.c Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,95 @@ +/* + * Here we implement the function that reads ETSI *.dec binary files + * which we've adopted as our TRAU-UL test input format for GSM-HR. + */ + +#include <sys/types.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "../libhr/tw_gsmhr.h" + +#include "read_file.h" + +static int validate_frames(const int16_t *buf, unsigned nframes) +{ + unsigned n; + int rc; + int16_t expect_taf; + + for (n = 0; n < nframes; n++) { + rc = gsmhr_check_decoder_params(buf); + if (rc < 0) + return rc; + /* disallow BFI=2 non-ETSI extension */ + if (buf[18] > 1) + return -1; + /* enforce TAF matching position */ + if (n % 12 == 11) + expect_taf = 1; + else + expect_taf = 0; + if (buf[21] != expect_taf) + return -1; + buf += GSMHR_NUM_PARAMS_DEC; + } + return 0; +} + +int read_binary_file(const char *filename, int16_t **bufret, unsigned *size_ret) +{ + int fd, rc; + struct stat st; + int16_t *buf; + unsigned nframes; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror(filename); + return -1; + } + fstat(fd, &st); + if (!S_ISREG(st.st_mode)) { + close(fd); + fprintf(stderr, "error: %s is not a regular file\n", filename); + return -1; + } + if (!st.st_size) { + close(fd); + fprintf(stderr, "error: %s is an empty file\n", filename); + return -1; + } + if (st.st_size % 44) { + close(fd); + fprintf(stderr, + "error: size of %s is not a multiple of 44 bytes\n", + filename); + return -1; + } + buf = malloc(st.st_size); + if (!buf) { + close(fd); + fprintf(stderr, "unable to malloc buffer for %s\n", filename); + return -1; + } + read(fd, buf, st.st_size); + close(fd); + nframes = st.st_size / 44; + + rc = validate_frames(buf, nframes); + if (rc < 0) { + free(buf); + fprintf(stderr, + "error: %s is not a valid GSM-HR dec file, or has wrong endian\n", + filename); + return -1; + } + *bufret = buf; + *size_ret = nframes; + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ater8/read_file.h Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,12 @@ +/* + * This header file defines the interface to the function that reads + * ETSI *.dec binary files which we've adopted as our TRAU-UL test + * input format for GSM-HR. + */ + +#pragma once + +#include <stdint.h> + +int read_binary_file(const char *filename, int16_t **bufret, + unsigned *size_ret);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ater8/read_ts.c Fri Aug 30 19:02:42 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/ater8/record_ctrl.c Fri Aug 30 19:02:42 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/ater8/submux.h Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,36 @@ +/* + * The structures and functions defined in this header file deal with + * interfacing to the Submultiplexer part of Nokia's Transcoder and + * Submultiplexer - the 8 kbit/s version. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/bits.h> +#include <osmocom/isdn/i460_mux.h> +#include <osmocom/trau/trau_frame.h> + +#define ATER_SUBSLOTS 8 + +struct ater_subslot { + struct osmo_i460_subchan *schan; + int nr; + bool is_active; + bool frame_has_taf; + struct osmo_trau_frame ul_frame; + unsigned mfrm_count; + int16_t *play_buffer; + unsigned play_buf_total; + unsigned play_buf_ptr; + bool play_wait_align; +}; + +extern struct ater_subslot subslots[ATER_SUBSLOTS]; + +void i460_rx_func(struct osmo_i460_subchan *schan, void *user_data, + const ubit_t *bits, unsigned int num_bits); + +void init_trau_ul_frame(int nr);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ater8/subslot_rx.c Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,21 @@ +/* + * Here we are going to implement Ater subslot Rx. + */ + +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <osmocom/core/select.h> +#include <osmocom/isdn/i460_mux.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) +{ + /* to be filled */ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ater8/tx_func.c Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,112 @@ +/* + * Here we are going to implement Tx on Ater toward the TRAU. + */ + +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <osmocom/core/bits.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 "out_frame.h" + +void init_trau_ul_frame(int nr) +{ + struct ater_subslot *at = &subslots[nr]; + struct osmo_trau_frame *fr = &at->ul_frame; + + fr->type = OSMO_TRAU8_SPEECH; + fr->dir = OSMO_TRAU_DIR_UL; + memset(fr->c_bits + 5, 1, 3); + fr->xc_bits[0] = 0; + memset(fr->t_bits, 1, 2); +} + +static void handle_play(struct ater_subslot *at) +{ + if (at->play_wait_align) { + if (at->mfrm_count) + return; + at->play_wait_align = false; + } + trau_frame_from_record(at->play_buffer + at->play_buf_ptr * 22, + &at->ul_frame, &at->frame_has_taf); + at->play_buf_ptr++; + if (at->play_buf_ptr < at->play_buf_total) + return; + free(at->play_buffer); + at->play_buffer = NULL; + printf("file play finished\n"); +} + +/* compute the odd parity bit of the given input bit sequence */ +static ubit_t compute_odd_parity(const ubit_t *in, unsigned int num_bits) +{ + int i; + unsigned int sum = 0; + + for (i = 0; i < num_bits; i++) { + if (in[i]) + sum++; + } + + if (sum & 1) + return 0; + else + return 1; +} + +static void tx_service_subslot(int nr) +{ + struct ater_subslot *at = &subslots[nr]; + struct osmo_trau_frame *fr = &at->ul_frame; + ubit_t taf; + struct msgb *msg; + int len; + + if (!at->is_active) + return; + if (at->play_buffer) + handle_play(at); + at->mfrm_count++; + if (at->mfrm_count >= 12) { + at->mfrm_count = 0; + taf = 1; + } else { + taf = 0; + } + if (at->frame_has_taf) + fr->xc_bits[3] = taf; + fr->xc_bits[5] = compute_odd_parity(fr->xc_bits, 5); + + msg = msgb_alloc_c(g_ctx, 320, "TRAU-UL-frame"); + if (!msg) + return; + len = osmo_trau_frame_encode(msg->tail, msgb_tailroom(msg), fr); + if (len <= 0) { + msgb_free(msg); + return; + } + msgb_put(msg, len); + osmo_i460_mux_enqueue(at->schan, msg); +} + +void transmit_e1_ts(void) +{ + uint8_t buf[160]; + int nr; + + for (nr = 0; nr < ATER_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/ater8/user_cmd.c Fri Aug 30 19:02:42 2024 +0000 @@ -0,0 +1,43 @@ +/* + * In this module we handle user-issued stdin commands during + * itt-ater-8 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[] = { + {"activ", cmd_activate}, + {"deact", cmd_deact}, + {"play", cmd_play_file}, + {"play-stop", cmd_play_stop}, + {"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); +}