diff --git a/CommonLibs/trx_vty.c b/CommonLibs/trx_vty.c index b16cd245..843d19fc 100644 --- a/CommonLibs/trx_vty.c +++ b/CommonLibs/trx_vty.c @@ -36,6 +36,22 @@ static struct trx_ctx* g_trx_ctx; +static const struct value_string clock_ref_names[] = { + { REF_INTERNAL, "internal" }, + { REF_EXTERNAL, "external" }, + { REF_GPS, "gspdo" }, + { 0, NULL } +}; + +static const struct value_string filler_names[] = { + { FILLER_DUMMY, "Dummy bursts" }, + { FILLER_ZERO, "Disabled" }, + { FILLER_NORM_RAND, "Normal bursts with random payload" }, + { FILLER_EDGE_RAND, "EDGE bursts with random payload" }, + { FILLER_ACCESS_RAND, "Access bursts with random payload" }, + { 0, NULL } +}; + struct trx_ctx *trx_from_vty(struct vty *v) { /* It can't hurt to force callers to continue to pass the vty instance @@ -50,6 +66,7 @@ struct trx_ctx *trx_from_vty(struct vty *v) enum trx_vty_node { TRX_NODE = _LAST_OSMOVTY_NODE + 1, + CHAN_NODE, }; static struct cmd_node trx_node = { @@ -58,6 +75,12 @@ static struct cmd_node trx_node = { 1, }; +static struct cmd_node chan_node = { + CHAN_NODE, + "%s(config-trx-chan)# ", + 1, +}; + DEFUN(cfg_trx, cfg_trx_cmd, "trx", "Configure the TRX\n") @@ -84,24 +107,373 @@ DEFUN(cfg_bind_ip, cfg_bind_ip_cmd, return CMD_SUCCESS; } +DEFUN(cfg_remote_ip, cfg_remote_ip_cmd, + "remote-ip A.B.C.D", + "Set the IP address for the remote BTS\n" + "IPv4 Address\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_base_port, cfg_base_port_cmd, + "base-port <1-65535>", + "Set the TRX Base Port\n" + "TRX Base Port\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + trx->cfg.base_port = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_dev_args, cfg_dev_args_cmd, + "dev-args DESC", + "Set the device-specific arguments to pass to the device\n" + "Device-specific arguments\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + osmo_talloc_replace_string(trx, &trx->cfg.dev_args, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_tx_sps, cfg_tx_sps_cmd, + "tx-sps (1|4)", + "Set the Tx Samples-per-Symbol\n" + "Tx Samples-per-Symbol\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + trx->cfg.tx_sps = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_rx_sps, cfg_rx_sps_cmd, + "rx-sps (1|4)", + "Set the Rx Samples-per-Symbol\n" + "Rx Samples-per-Symbol\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + trx->cfg.rx_sps = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_test_rtsc, cfg_test_rtsc_cmd, + "test rtsc <0-7>", + "Set the Random Normal Burst test mode with TSC\n" + "TSC\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + if (trx->cfg.rach_delay_set) { + vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + trx->cfg.rtsc_set = true; + trx->cfg.rtsc = atoi(argv[0]); + if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */ + trx->cfg.filler = FILLER_NORM_RAND; + + return CMD_SUCCESS; +} + +DEFUN(cfg_test_rach_delay, cfg_test_rach_delay_cmd, + "test rach-delay <0-68>", + "Set the Random Access Burst test mode with delay\n" + "RACH delay\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + if (trx->cfg.rtsc_set) { + vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (trx->cfg.egprs) { + vty_out(vty, "rach-delay and egprs options are mutual-exclusive%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + trx->cfg.rach_delay_set = true; + trx->cfg.rach_delay = atoi(argv[0]); + trx->cfg.filler = FILLER_ACCESS_RAND; + + return CMD_SUCCESS; +} + +DEFUN(cfg_clock_ref, cfg_clock_ref_cmd, + "clock-ref (internal|external|gpsdo)", + "Set the Reference Clock\n" + "Enable internal referece (default)\n" + "Enable external 10 MHz reference\n" + "Enable GPSDO reference\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + trx->cfg.clock_ref = get_string_value(clock_ref_names, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd, + "multi-arfcn (disable|enable)", + "Enable multi-ARFCN transceiver (default=disable)\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + if (strcmp("disable", argv[0]) == 0) { + trx->cfg.multi_arfcn = false; + } else if (strcmp("enable", argv[0]) == 0) { + trx->cfg.multi_arfcn = true; + } else { + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_offset, cfg_offset_cmd, + "offset FLOAT", + "Set the baseband frequency offset (default=0, auto)\n" + "Baseband Frequency Offset\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + trx->cfg.offset = atof(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_rssi_offset, cfg_rssi_offset_cmd, + "rssi-offset FLOAT", + "Set the RSSI to dBm offset in dB (default=0)\n" + "RSSI to dBm offset in dB\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + trx->cfg.rssi_offset = atof(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_swap_channels, cfg_swap_channels_cmd, + "swap-channels (disable|enable)", + "Swap channels (default=disable)\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + if (strcmp("disable", argv[0]) == 0) { + trx->cfg.swap_channels = false; + } else if (strcmp("enable", argv[0]) == 0) { + trx->cfg.swap_channels = true; + } else { + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_egprs, cfg_egprs_cmd, + "egprs (disable|enable)", + "Enable EDGE receiver (default=disable)\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + if (strcmp("disable", argv[0]) == 0) { + trx->cfg.egprs = false; + } else if (strcmp("enable", argv[0]) == 0) { + trx->cfg.egprs = true; + trx->cfg.filler = FILLER_EDGE_RAND; + } else { + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_rt_prio, cfg_rt_prio_cmd, + "rt-prio <1-32>", + "Set the SCHED_RR real-time priority\n" + "Real time priority\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + trx->cfg.sched_rr = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_filler, cfg_filler_cmd, + "filler dummy", + "Enable C0 filler table\n" + "Dummy method\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + + trx->cfg.filler = FILLER_DUMMY; + + return CMD_SUCCESS; +} + +DEFUN(cfg_chan, cfg_chan_cmd, + "chan <0-100>", + "Select a channel to configure\n" + "Channel index\n") +{ + struct trx_ctx *trx = trx_from_vty(vty); + int idx = atoi(argv[0]); + + if (idx >= TRX_CHAN_MAX) { + vty_out(vty, "Chan list full.%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (trx->cfg.num_chans < idx) { /* Unexisting or creating non-consecutive */ + vty_out(vty, "Non-existent or non-consecutive chan %d.%s", + idx, VTY_NEWLINE); + return CMD_WARNING; + } else if (trx->cfg.num_chans == idx) { /* creating it */ + trx->cfg.num_chans++; + trx->cfg.chans[idx].trx = trx; + trx->cfg.chans[idx].idx = idx; + } + + vty->node = CHAN_NODE; + vty->index = &trx->cfg.chans[idx]; + + return CMD_SUCCESS; +} + +DEFUN(cfg_chan_rx_path, cfg_chan_rx_path_cmd, + "rx-path NAME", + "Set the Rx Path\n" + "Rx Path name\n") +{ + struct trx_chan *chan = vty->index; + + osmo_talloc_replace_string(chan->trx, &chan->rx_path, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_chan_tx_path, cfg_chan_tx_path_cmd, + "tx-path NAME", + "Set the Tx Path\n" + "Tx Path name\n") +{ + struct trx_chan *chan = vty->index; + + osmo_talloc_replace_string(chan->trx, &chan->tx_path, argv[0]); + + return CMD_SUCCESS; +} + +static int dummy_config_write(struct vty *v) +{ + return CMD_SUCCESS; +} + static int config_write_trx(struct vty *vty) { + struct trx_chan *chan; + int i; struct trx_ctx *trx = trx_from_vty(vty); vty_out(vty, "trx%s", VTY_NEWLINE); if (trx->cfg.bind_addr) vty_out(vty, " bind-ip %s%s", trx->cfg.bind_addr, VTY_NEWLINE); + if (trx->cfg.remote_addr) + vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE); + if (trx->cfg.base_port != DEFAULT_TRX_PORT) + vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE); + if (trx->cfg.dev_args) + vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE); + if (trx->cfg.tx_sps != DEFAULT_TX_SPS) + vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE); + if (trx->cfg.rx_sps != DEFAULT_RX_SPS) + vty_out(vty, " rx-sps %u%s", trx->cfg.rx_sps, VTY_NEWLINE); + if (trx->cfg.rtsc_set) + vty_out(vty, " test rtsc %u%s", trx->cfg.rtsc, VTY_NEWLINE); + if (trx->cfg.rach_delay_set) + vty_out(vty, " test rach-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE); + if (trx->cfg.clock_ref != REF_INTERNAL) + vty_out(vty, " clock-ref %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE); + vty_out(vty, " multi-arfcn %s%s", trx->cfg.multi_arfcn ? "enable" : "disable", VTY_NEWLINE); + if (trx->cfg.offset != 0) + vty_out(vty, " offset %f%s", trx->cfg.offset, VTY_NEWLINE); + if (trx->cfg.rssi_offset != 0) + vty_out(vty, " rssi-offset %f%s", trx->cfg.rssi_offset, VTY_NEWLINE); + vty_out(vty, " swap-channels %s%s", trx->cfg.swap_channels ? "enable" : "disable", VTY_NEWLINE); + vty_out(vty, " egprs %s%s", trx->cfg.egprs ? "enable" : "disable", VTY_NEWLINE); + if (trx->cfg.sched_rr != 0) + vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE); + + for (i = 0; i < trx->cfg.num_chans; i++) { + chan = &trx->cfg.chans[i]; + vty_out(vty, " chan %u%s", chan->idx, VTY_NEWLINE); + if (chan->rx_path) + vty_out(vty, " rx-path %s%s", chan->rx_path, VTY_NEWLINE); + if (chan->tx_path) + vty_out(vty, " tx-path %s%s", chan->tx_path, VTY_NEWLINE); + } return CMD_SUCCESS; } +static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx) +{ + struct trx_chan *chan; + int i; + vty_out(vty, "TRX Config:%s", VTY_NEWLINE); + vty_out(vty, " Local IP: %s%s", trx->cfg.bind_addr, VTY_NEWLINE); + vty_out(vty, " Remote IP: %s%s", trx->cfg.remote_addr, VTY_NEWLINE); + vty_out(vty, " TRX Base Port: %u%s", trx->cfg.base_port, VTY_NEWLINE); + vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE); + vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE); + vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE); + vty_out(vty, " Test Mode: TSC: %u (%s)%s", trx->cfg.rtsc, + trx->cfg.rtsc_set ? "Enabled" : "Disabled", VTY_NEWLINE); + vty_out(vty, " Test Mode: RACH Delay: %u (%s)%s", trx->cfg.rach_delay, + trx->cfg.rach_delay_set ? "Enabled" : "Disabled", VTY_NEWLINE); + vty_out(vty, " C0 Filler Table: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE); + vty_out(vty, " Clock Reference: %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE); + vty_out(vty, " Multi-Carrier: %s%s", trx->cfg.multi_arfcn ? "Enabled" : "Disabled", VTY_NEWLINE); + vty_out(vty, " Tuning offset: %f%s", trx->cfg.offset, VTY_NEWLINE); + vty_out(vty, " RSSI to dBm offset: %f%s", trx->cfg.rssi_offset, VTY_NEWLINE); + vty_out(vty, " Swap channels: %s%s", trx->cfg.swap_channels ? "Enabled" : "Disabled", VTY_NEWLINE); + vty_out(vty, " EDGE support: %s%s", trx->cfg.egprs ? "Enabled" : "Disabled", VTY_NEWLINE); + vty_out(vty, " Real Time Priority: %u (%s)%s", trx->cfg.sched_rr, + trx->cfg.sched_rr ? "Enabled" : "Disabled", VTY_NEWLINE); + vty_out(vty, " Channels: %u%s", trx->cfg.num_chans, VTY_NEWLINE); + for (i = 0; i < trx->cfg.num_chans; i++) { + chan = &trx->cfg.chans[i]; + vty_out(vty, " Channel %u:%s", chan->idx, VTY_NEWLINE); + if (chan->rx_path) + vty_out(vty, " Rx Path: %s%s", chan->rx_path, VTY_NEWLINE); + if (chan->tx_path) + vty_out(vty, " Tx Path: %s%s", chan->tx_path, VTY_NEWLINE); + } +} + DEFUN(show_trx, show_trx_cmd, "show trx", SHOW_STR "Display information on the TRX\n") { struct trx_ctx *trx = trx_from_vty(vty); - vty_out(vty, "TRX: Bound to %s%s", trx->cfg.bind_addr, VTY_NEWLINE); + trx_dump_vty(vty, trx); return CMD_SUCCESS; } @@ -110,6 +482,7 @@ static int trx_vty_is_config_node(struct vty *vty, int node) { switch (node) { case TRX_NODE: + case CHAN_NODE: return 1; default: return 0; @@ -124,6 +497,11 @@ static int trx_vty_go_parent(struct vty *vty) vty->index = NULL; vty->index_sub = NULL; break; + case CHAN_NODE: + vty->node = TRX_NODE; + vty->index = NULL; + vty->index_sub = NULL; + break; default: OSMO_ASSERT(0); } @@ -148,6 +526,20 @@ struct vty_app_info g_vty_info = { .is_config_node = trx_vty_is_config_node, }; +struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx) +{ + struct trx_ctx * trx = talloc_zero(talloc_ctx, struct trx_ctx); + + trx->cfg.bind_addr = talloc_strdup(trx, DEFAULT_TRX_IP); + trx->cfg.remote_addr = talloc_strdup(trx, DEFAULT_TRX_IP); + trx->cfg.base_port = DEFAULT_TRX_PORT; + trx->cfg.tx_sps = DEFAULT_TX_SPS; + trx->cfg.rx_sps = DEFAULT_RX_SPS; + trx->cfg.filler = FILLER_ZERO; + + return trx; +} + int trx_vty_init(struct trx_ctx* trx) { g_trx_ctx = trx; @@ -157,6 +549,26 @@ int trx_vty_init(struct trx_ctx* trx) install_node(&trx_node, config_write_trx); install_element(TRX_NODE, &cfg_bind_ip_cmd); + install_element(TRX_NODE, &cfg_remote_ip_cmd); + install_element(TRX_NODE, &cfg_base_port_cmd); + install_element(TRX_NODE, &cfg_dev_args_cmd); + install_element(TRX_NODE, &cfg_tx_sps_cmd); + install_element(TRX_NODE, &cfg_rx_sps_cmd); + install_element(TRX_NODE, &cfg_test_rtsc_cmd); + install_element(TRX_NODE, &cfg_test_rach_delay_cmd); + install_element(TRX_NODE, &cfg_clock_ref_cmd); + install_element(TRX_NODE, &cfg_multi_arfcn_cmd); + install_element(TRX_NODE, &cfg_offset_cmd); + install_element(TRX_NODE, &cfg_rssi_offset_cmd); + install_element(TRX_NODE, &cfg_swap_channels_cmd); + install_element(TRX_NODE, &cfg_egprs_cmd); + install_element(TRX_NODE, &cfg_rt_prio_cmd); + install_element(TRX_NODE, &cfg_filler_cmd); + + install_element(TRX_NODE, &cfg_chan_cmd); + install_node(&chan_node, dummy_config_write); + install_element(CHAN_NODE, &cfg_chan_rx_path_cmd); + install_element(CHAN_NODE, &cfg_chan_tx_path_cmd); return 0; } diff --git a/CommonLibs/trx_vty.h b/CommonLibs/trx_vty.h index 74af31b6..c9217224 100644 --- a/CommonLibs/trx_vty.h +++ b/CommonLibs/trx_vty.h @@ -2,12 +2,67 @@ #include +#include "config_defs.h" + extern struct vty_app_info g_vty_info; +#define TRX_CHAN_MAX 8 + +/* Samples-per-symbol for downlink path + * 4 - Uses precision modulator (more computation, less distortion) + * 1 - Uses minimized modulator (less computation, more distortion) + * + * Other values are invalid. Receive path (uplink) is always + * downsampled to 1 sps. Default to 4 sps for all cases. + */ +#define DEFAULT_TX_SPS 4 + +/* + * Samples-per-symbol for uplink (receiver) path + * Do not modify this value. EDGE configures 4 sps automatically on + * B200/B210 devices only. Use of 4 sps on the receive path for other + * configurations is not supported. + */ +#define DEFAULT_RX_SPS 1 + +/* Default configuration parameters */ +#define DEFAULT_TRX_PORT 5700 +#define DEFAULT_TRX_IP "127.0.0.1" +#define DEFAULT_CHANS 1 + +struct trx_ctx; + +struct trx_chan { + struct trx_ctx *trx; /* backpointer */ + unsigned int idx; /* channel index */ + char *rx_path; + char *tx_path; +}; + struct trx_ctx { struct { char *bind_addr; + char *remote_addr; + char *dev_args; + unsigned int base_port; + unsigned int tx_sps; + unsigned int rx_sps; + unsigned int rtsc; + bool rtsc_set; + unsigned int rach_delay; + bool rach_delay_set; + enum ReferenceType clock_ref; + enum FillerType filler; + bool multi_arfcn; + double offset; + double rssi_offset; + bool swap_channels; + bool egprs; + unsigned int sched_rr; + unsigned int num_chans; + struct trx_chan chans[TRX_CHAN_MAX]; } cfg; }; int trx_vty_init(struct trx_ctx* trx); +struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx); diff --git a/Transceiver52M/osmo-trx.cpp b/Transceiver52M/osmo-trx.cpp index dc4e1c8e..7bc025f1 100644 --- a/Transceiver52M/osmo-trx.cpp +++ b/Transceiver52M/osmo-trx.cpp @@ -566,7 +566,7 @@ int main(int argc, char *argv[]) setup_signal_handlers(); - g_trx_ctx = talloc_zero(tall_trx_ctx, struct trx_ctx); + g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx); #ifdef HAVE_SSE3 printf("Info: SSE3 support compiled in");