From 3bd6e5b15b00c6d32badab20526a8cbb24997667 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Mon, 10 Jul 2023 15:21:22 +0200 Subject: [PATCH] apps: Add DTM shell application This application provides shell interface for Bluetooth DTM. It also provides few extra commands useful for testing (TX power configuration, antenna selection, unmodulated carrier). --- apps/dtm/pkg.yml | 33 +++ apps/dtm/src/main.c | 425 ++++++++++++++++++++++++++++++++++ apps/dtm/src/parse.c | 529 +++++++++++++++++++++++++++++++++++++++++++ apps/dtm/src/parse.h | 58 +++++ apps/dtm/syscfg.yml | 28 +++ 5 files changed, 1073 insertions(+) create mode 100644 apps/dtm/pkg.yml create mode 100644 apps/dtm/src/main.c create mode 100644 apps/dtm/src/parse.c create mode 100644 apps/dtm/src/parse.h create mode 100644 apps/dtm/syscfg.yml diff --git a/apps/dtm/pkg.yml b/apps/dtm/pkg.yml new file mode 100644 index 000000000..2204befc4 --- /dev/null +++ b/apps/dtm/pkg.yml @@ -0,0 +1,33 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +pkg.name: apps/dtm +pkg.type: app +pkg.description: "Bluetooth DTM shell application" +pkg.author: "Apache Mynewt " +pkg.homepage: "https://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/sys/log" + - "@apache-mynewt-core/sys/stats" + - "@apache-mynewt-core/sys/shell" + - "@apache-mynewt-core/sys/console" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-nimble/nimble/host" + - "@apache-mynewt-mcumgr/cmd/img_mgmt" + - "@mcuboot/boot/bootutil" diff --git a/apps/dtm/src/main.c b/apps/dtm/src/main.c new file mode 100644 index 000000000..e5e0689c8 --- /dev/null +++ b/apps/dtm/src/main.c @@ -0,0 +1,425 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "os/mynewt.h" +#include +#include +#include +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "host/ble_hs.h" +#include "host/ble_dtm.h" +#include +#include + +static const struct kv_pair phy_opts[] = { + { "1M", 0x01 }, + { "2M", 0x02 }, + { "coded", 0x03 }, + { NULL } +}; + +static const struct kv_pair modulation_index_opts[] = { + { "standard", 0x00 }, + { "stable", 0x01 }, + { NULL } +}; + +static int +cmd_rx_test(int argc, char **argv) +{ + struct ble_dtm_rx_params params; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + params.channel = parse_arg_uint8("channel", &rc); + if ((rc != 0) || (params.channel > 39)) { + console_printf("invalid channel\n"); + return rc; + } + + params.phy = parse_arg_kv_dflt("phy", phy_opts, 0x01, &rc); + if (rc != 0) { + console_printf("invalid 'phy' parameter\n"); + return rc; + } + + params.modulation_index = parse_arg_kv_dflt("modulation_index", + modulation_index_opts, 0x00, &rc); + if (rc != 0) { + console_printf("invalid 'modulation_index' parameter\n"); + return rc; + } + + rc = ble_dtm_rx_start(¶ms); + if (rc) { + console_printf("failed to start RX test\n"); + return rc; + } + + console_printf("RX test started\n"); + return 0; +} + +static int +cmd_tx_test(int argc, char **argv) +{ + struct ble_dtm_tx_params params; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + params.channel = parse_arg_uint8("channel", &rc); + if ((rc != 0) || (params.channel > 39)) { + console_printf("invalid channel\n"); + return rc; + } + + params.phy = parse_arg_kv_dflt("phy", phy_opts, 0x01, &rc); + if (rc != 0) { + console_printf("invalid 'phy' parameter\n"); + return rc; + } + + params.payload = parse_arg_uint8("payload", &rc); + if ((rc != 0) || ((params.payload > 7))) { + console_printf("invalid 'payload' parameter\n"); + return rc; + } + + params.test_data_len = parse_arg_uint8_dflt("data_length", 0, &rc); + if (rc != 0) { + console_printf("invalid 'data_length' parameter\n"); + return rc; + } + + rc = ble_dtm_tx_start(¶ms); + if (rc) { + console_printf("failed to start TX test\n"); + return rc; + } + + console_printf("TX test started\n"); + return 0; +} + +static int +cmd_stop_test(int argc, char **argv) +{ + uint16_t num_packets; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = ble_dtm_stop(&num_packets); + if (rc) { + console_printf("failed to stop test\n"); + return rc; + } + + console_printf("Test stopped (%u packets)\n", num_packets); + return 0; +} + +static int +cmd_tx_power(int argc, char **argv) +{ + struct ble_hci_vs_set_tx_pwr_cp cmd; + struct ble_hci_vs_set_tx_pwr_rp rsp; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + cmd.tx_power = parse_arg_long_bounds_dflt("power", + -127, 127, 127, &rc); + if (rc != 0) { + console_printf("invalid 'power' parameter\n"); + return rc; + } + + rc = ble_hs_hci_send_vs_cmd(BLE_HCI_OCF_VS_SET_TX_PWR, &cmd, sizeof(cmd), + &rsp, sizeof(rsp)); + if (rc) { + console_printf("failed to set TX power\n"); + return rc; + } + + console_printf("TX power set to %d dBm\n", rsp.tx_power); + return 0; +} + +static int +cmd_set_antenna(int argc, char **argv) +{ + struct ble_hci_vs_set_antenna_cp cmd; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + cmd.antenna = parse_arg_uint8_dflt("antenna", 0, &rc); + if (rc != 0 || ((cmd.antenna > 2))) { + console_printf("invalid 'antenna' parameter\n"); + return rc; + } + + rc = ble_hs_hci_send_vs_cmd(BLE_HCI_OCF_VS_SET_ANTENNA, &cmd, sizeof(cmd), + NULL, 0); + if (rc) { + console_printf("failed to set antenna\n"); + return rc; + } + + console_printf("Antenna set to %u\n", cmd.antenna); + return 0; +} + +#define BLE_HCI_OCF_VS_TEST_CARRIER (0x0020) + +static bool tx_carrier_running = false; + +static int +cmd_tx_carrier(int argc, char **argv) +{ + uint8_t cmd[2]; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + if (tx_carrier_running) { + console_printf("TX carrier already started\n"); + return 0; + } + + cmd[0] = 1; + cmd[1] = parse_arg_uint8("channel", &rc); + if ((rc != 0) || (cmd[1] > 39)) { + console_printf("invalid channel\n"); + return rc; + } + + rc = ble_hs_hci_send_vs_cmd(BLE_HCI_OCF_VS_TEST_CARRIER, &cmd, sizeof(cmd), + NULL, 0); + if (rc) { + console_printf("failed to start TX carrier\n"); + return rc; + } + + console_printf("TX carrier started\n"); + tx_carrier_running = true; + return 0; +} + +static int +cmd_stop_carrier(int argc, char **argv) +{ + uint8_t cmd[2]; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + if (!tx_carrier_running) { + console_printf("TX carrier not started\n"); + return 0; + } + cmd[0] = 0; + cmd[1] = 0; + + rc = ble_hs_hci_send_vs_cmd(BLE_HCI_OCF_VS_TEST_CARRIER, &cmd, sizeof(cmd), + NULL, 0); + if (rc) { + console_printf("failed to stop TX carrier\n"); + return rc; + } + + console_printf("TX carrier stopped\n"); + tx_carrier_running = false; + return 0; +} + +static const struct shell_param cmd_rx_test_params[] = { + {"channel", "RX channel, usage: =[0-39]"}, + {"phy", "usage: =[1M|2M], default: 1M"}, + {"modulation_index", "usage: =[standard|stable], default=standard"}, + {NULL} +}; + +static const struct shell_cmd_help cmd_rx_test_help = { + .summary = "start DTM RX test", + .usage = NULL, + .params = cmd_rx_test_params, +}; + +static const struct shell_param cmd_tx_test_params[] = { + {"channel", "RX channel, usage: =[0-39]"}, + {"phy", "usage: =[1M|2M], default: 1M"}, + {"data_length", "usage: =[0-255], default: 0"}, + {"payload", "usage: =[0-7]"}, + {NULL} +}; + +static const struct shell_cmd_help cmd_tx_test_help = { + .summary = "start DTM TX test", + .usage = NULL, + .params = cmd_tx_test_params, +}; + +static const struct shell_cmd_help cmd_stop_test_help = { + .summary = "stop DTM test", + .usage = NULL, + .params = NULL, +}; + +static const struct shell_param cmd_tx_power_params[] = { + {"power", "usage: =[-127-127], default: 127"}, + {NULL} +}; + +static const struct shell_cmd_help cmd_tx_power_help = { + .summary = "set TX power", + .usage = NULL, + .params = cmd_tx_power_params, +}; + +static const struct shell_param cmd_set_antenna_params[] = { + {"antenna", "usage: =[0,1,2], default: 0"}, + {NULL} +}; + +static const struct shell_cmd_help cmd_set_antenna_help = { + .summary = "set active antenna ", + .usage = NULL, + .params = cmd_set_antenna_params, +}; + +static const struct shell_param cmd_tx_carrier_params[] = { + {"channel", "TX channel, usage: =[0-39]"}, + {NULL} +}; + +static const struct shell_cmd_help cmd_tx_carrier_help = { + .summary = "TX unmodulated carrier", + .usage = NULL, + .params = cmd_tx_carrier_params, +}; + +static const struct shell_cmd_help cmd_stop_carrier_help = { + .summary = "stop TX unmodulated carrier", + .usage = NULL, + .params = NULL, +}; + +static const struct shell_cmd dtm_commands[] = { + { + .sc_cmd = "rx-test", + .sc_cmd_func = cmd_rx_test, + .help = &cmd_rx_test_help, + }, + { + .sc_cmd = "tx-test", + .sc_cmd_func = cmd_tx_test, + .help = &cmd_tx_test_help, + }, + { + .sc_cmd = "stop-test", + .sc_cmd_func = cmd_stop_test, + .help = &cmd_stop_test_help, + }, + { + .sc_cmd = "tx-power", + .sc_cmd_func = cmd_tx_power, + .help = &cmd_tx_power_help, + }, + { + .sc_cmd = "set-antenna", + .sc_cmd_func = cmd_set_antenna, + .help = &cmd_set_antenna_help, + }, + { + .sc_cmd = "tx-carrier", + .sc_cmd_func = cmd_tx_carrier, + .help = &cmd_tx_carrier_help, + }, + { + .sc_cmd = "stop-carrier", + .sc_cmd_func = cmd_stop_carrier, + .help = &cmd_stop_carrier_help, + }, + { } +}; + +static void +on_sync(void) +{ + console_printf("Host and controller synced\n"); +} + +static void +on_reset(int reason) +{ + console_printf("Error: Resetting state; reason=%d\n", reason); +} + +int +main(void) +{ + struct image_version the_version; + char prompt[50]; + + sysinit(); + + img_mgmt_read_info(0, &the_version, NULL, NULL); + + snprintf(prompt, sizeof(prompt), "dtm_%u.%u.%u", + the_version.iv_major, the_version.iv_minor, the_version.iv_revision); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = on_reset; + ble_hs_cfg.sync_cb = on_sync; + + shell_register(prompt, dtm_commands); + shell_register_default_module(prompt); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/apps/dtm/src/parse.c b/apps/dtm/src/parse.c new file mode 100644 index 000000000..ed79d7404 --- /dev/null +++ b/apps/dtm/src/parse.c @@ -0,0 +1,529 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "console/console.h" + +#define CMD_MAX_ARGS 16 + +static char *cmd_args[CMD_MAX_ARGS][2]; +static int cmd_num_args; + +int +parse_arg_find_idx(const char *key) +{ + int i; + + for (i = 0; i < cmd_num_args; i++) { + if (strcmp(cmd_args[i][0], key) == 0) { + return i; + } + } + + return -1; +} + +char * +parse_arg_peek(const char *key) +{ + int i; + + for (i = 0; i < cmd_num_args; i++) { + if (strcmp(cmd_args[i][0], key) == 0) { + return cmd_args[i][1]; + } + } + + return NULL; +} + +char * +parse_arg_extract(const char *key) +{ + int i; + + for (i = 0; i < cmd_num_args; i++) { + if (strcmp(cmd_args[i][0], key) == 0) { + /* Erase parameter. */ + cmd_args[i][0][0] = '\0'; + + return cmd_args[i][1]; + } + } + + return NULL; +} + +/** + * Determines which number base to use when parsing the specified numeric + * string. This just avoids base '0' so that numbers don't get interpreted as + * octal. + */ +static int +parse_arg_long_base(char *sval) +{ + if (sval[0] == '0' && sval[1] == 'x') { + return 0; + } else { + return 10; + } +} + +long +parse_long_bounds(char *sval, long min, long max, int *out_status) +{ + char *endptr; + long lval; + + lval = strtol(sval, &endptr, parse_arg_long_base(sval)); + if (sval[0] != '\0' && *endptr == '\0' && + lval >= min && lval <= max) { + + *out_status = 0; + return lval; + } + + *out_status = EINVAL; + return 0; +} + +long +parse_arg_long_bounds_peek(char *name, long min, long max, int *out_status) +{ + char *sval; + + sval = parse_arg_peek(name); + if (sval == NULL) { + *out_status = ENOENT; + return 0; + } + return parse_long_bounds(sval, min, max, out_status); +} + +long +parse_arg_long_bounds(char *name, long min, long max, int *out_status) +{ + char *sval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + *out_status = ENOENT; + return 0; + } + return parse_long_bounds(sval, min, max, out_status); +} + +long +parse_arg_long_bounds_dflt(char *name, long min, long max, + long dflt, int *out_status) +{ + long val; + int rc; + + val = parse_arg_long_bounds(name, min, max, &rc); + if (rc == ENOENT) { + rc = 0; + val = dflt; + } + + *out_status = rc; + + return val; +} + +uint64_t +parse_arg_uint64_bounds(char *name, uint64_t min, uint64_t max, int *out_status) +{ + char *endptr; + char *sval; + uint64_t lval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + *out_status = ENOENT; + return 0; + } + + lval = strtoull(sval, &endptr, parse_arg_long_base(sval)); + if (sval[0] != '\0' && *endptr == '\0' && + lval >= min && lval <= max) { + + *out_status = 0; + return lval; + } + + *out_status = EINVAL; + return 0; +} + +long +parse_arg_long(char *name, int *out_status) +{ + return parse_arg_long_bounds(name, LONG_MIN, LONG_MAX, out_status); +} + +uint8_t +parse_arg_bool(char *name, int *out_status) +{ + return parse_arg_long_bounds(name, 0, 1, out_status); +} + +uint8_t +parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status) +{ + return parse_arg_long_bounds_dflt(name, 0, 1, dflt, out_status); +} + +uint8_t +parse_arg_uint8(char *name, int *out_status) +{ + return parse_arg_long_bounds(name, 0, UINT8_MAX, out_status); +} + +uint16_t +parse_arg_uint16(char *name, int *out_status) +{ + return parse_arg_long_bounds(name, 0, UINT16_MAX, out_status); +} + +uint16_t +parse_arg_uint16_peek(char *name, int *out_status) +{ + return parse_arg_long_bounds_peek(name, 0, UINT16_MAX, out_status); +} + +uint32_t +parse_arg_uint32(char *name, int *out_status) +{ + return parse_arg_uint64_bounds(name, 0, UINT32_MAX, out_status); +} + +uint64_t +parse_arg_uint64(char *name, int *out_status) +{ + return parse_arg_uint64_bounds(name, 0, UINT64_MAX, out_status); +} + +uint8_t +parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status) +{ + uint8_t val; + int rc; + + val = parse_arg_uint8(name, &rc); + if (rc == ENOENT) { + val = dflt; + rc = 0; + } + + *out_status = rc; + return val; +} + +uint16_t +parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status) +{ + uint16_t val; + int rc; + + val = parse_arg_uint16(name, &rc); + if (rc == ENOENT) { + val = dflt; + rc = 0; + } + + *out_status = rc; + return val; +} + +uint32_t +parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status) +{ + uint32_t val; + int rc; + + val = parse_arg_uint32(name, &rc); + if (rc == ENOENT) { + val = dflt; + rc = 0; + } + + *out_status = rc; + return val; +} + +static uint32_t +parse_time_unit_mult(const char *str) +{ + if (!strcasecmp(str, "us")) { + return 1; + } else if (!strcasecmp(str, "ms")) { + return 1000; + } else if (!strcasecmp(str, "s")) { + return 1000000; + } + + return 0; +} + +static uint32_t +parse_time_us(const char *str, int *out_status) +{ + uint32_t val = 0; + uint32_t val_div = 1; + uint32_t val_mult = 1; + uint32_t val_us; + + while (isdigit((unsigned char)*str)) { + val *= 10; + val += *str - '0'; + str++; + } + + if (*str == '.') { + str++; + while (isdigit((unsigned char)*str)) { + val *= 10; + val += *str - '0'; + val_div *= 10; + str++; + } + } + + val_mult = parse_time_unit_mult(str); + if (val_mult == 0) { + *out_status = EINVAL; + return 0; + } + + if (val_mult > val_div) { + val_us = val * (val_mult / val_div); + } else { + val_us = val * (val_div / val_mult); + } + + *out_status = 0; + + return val_us; +} + +uint32_t +parse_arg_time_dflt(char *name, int step_us, uint32_t dflt, int *out_status) +{ + const char *arg; + uint32_t val; + int rc; + + arg = parse_arg_peek(name); + if (!arg) { + *out_status = 0; + return dflt; + } + + val = parse_time_us(arg, &rc); + if (rc) { + val = parse_arg_uint32(name, &rc); + if (rc == ENOENT) { + *out_status = 0; + return dflt; + } + } else { + val /= step_us; + parse_arg_extract(name); + } + + *out_status = rc; + return val; +} + +const struct kv_pair * +parse_kv_find(const struct kv_pair *kvs, char *name) +{ + const struct kv_pair *kv; + int i; + + for (i = 0; kvs[i].key != NULL; i++) { + kv = kvs + i; + if (strcmp(name, kv->key) == 0) { + return kv; + } + } + + return NULL; +} + +int +parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status) +{ + const struct kv_pair *kv; + char *sval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + *out_status = ENOENT; + return -1; + } + + kv = parse_kv_find(kvs, sval); + if (kv == NULL) { + *out_status = EINVAL; + return -1; + } + + *out_status = 0; + return kv->val; +} + +int +parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val, + int *out_status) +{ + int val; + int rc; + + val = parse_arg_kv(name, kvs, &rc); + if (rc == ENOENT) { + rc = 0; + val = def_val; + } + + *out_status = rc; + + return val; +} + + +static int +parse_arg_byte_stream_delim(char *sval, char *delims, int max_len, + uint8_t *dst, int *out_len) +{ + unsigned long ul; + char *endptr; + char *token; + int i; + char *tok_ptr; + + i = 0; + for (token = strtok_r(sval, delims, &tok_ptr); + token != NULL; + token = strtok_r(NULL, delims, &tok_ptr)) { + + if (i >= max_len) { + return EINVAL; + } + + ul = strtoul(token, &endptr, 16); + if (sval[0] == '\0' || *endptr != '\0' || ul > UINT8_MAX) { + return -1; + } + + dst[i] = ul; + i++; + } + + *out_len = i; + + return 0; +} + +int +parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len) +{ + char *sval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + return ENOENT; + } + + return parse_arg_byte_stream_delim(sval, ":-", max_len, dst, out_len); +} + +int +parse_arg_uint8_list_with_separator(char *name, char *separator, int max_len, + uint8_t *dst, int *out_len) +{ + char *sval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + return ENOENT; + } + + return parse_arg_byte_stream_delim(sval, separator, max_len, dst, out_len); +} + +int +parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len) +{ + int actual_len; + int rc; + + rc = parse_arg_byte_stream(name, len, dst, &actual_len); + if (rc != 0) { + return rc; + } + + if (actual_len != len) { + return EINVAL; + } + + return 0; +} + +int +parse_arg_all(int argc, char **argv) +{ + char *key; + char *val; + int i; + char *tok_ptr; + + cmd_num_args = 0; + + for (i = 0; i < argc; i++) { + key = strtok_r(argv[i], "=", &tok_ptr); + val = strtok_r(NULL, "=", &tok_ptr); + + if (key != NULL && val != NULL) { + if (strlen(key) == 0) { + console_printf("Error: invalid argument: %s\n", argv[i]); + return -1; + } + + if (cmd_num_args >= CMD_MAX_ARGS) { + console_printf("Error: too many arguments"); + return -1; + } + + cmd_args[cmd_num_args][0] = key; + cmd_args[cmd_num_args][1] = val; + cmd_num_args++; + } + } + + return 0; +} diff --git a/apps/dtm/src/parse.h b/apps/dtm/src/parse.h new file mode 100644 index 000000000..a0255d368 --- /dev/null +++ b/apps/dtm/src/parse.h @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef PARSE_H +#define PARSE_H + +#include + +struct kv_pair { + char *key; + int val; +}; + +uint32_t parse_arg_time_dflt(char *name, int step, uint32_t dflt, int *out_status); +const struct kv_pair *parse_kv_find(const struct kv_pair *kvs, char *name); +int parse_arg_find_idx(const char *key); +char *parse_arg_extract(const char *key); +long parse_arg_long_bounds(char *name, long min, long max, int *out_status); +long parse_arg_long_bounds_dflt(char *name, long min, long max, + long dflt, int *out_status); +uint64_t parse_arg_uint64_bounds(char *name, uint64_t min, + uint64_t max, int *out_status); +long parse_arg_long(char *name, int *staus); +uint8_t parse_arg_bool(char *name, int *status); +uint8_t parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status); +uint8_t parse_arg_uint8(char *name, int *status); +uint8_t parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status); +uint16_t parse_arg_uint16(char *name, int *status); +uint16_t parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status); +uint32_t parse_arg_uint32(char *name, int *out_status); +uint32_t parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status); +uint64_t parse_arg_uint64(char *name, int *out_status); +int parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status); +int parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val, + int *out_status); +int parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len); +int parse_arg_uint8_list_with_separator(char *name, char *separator, int max_len, + uint8_t *dst, int *out_len); +int parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len); +int parse_arg_all(int argc, char **argv); + +#endif diff --git a/apps/dtm/syscfg.yml b/apps/dtm/syscfg.yml new file mode 100644 index 000000000..b88c387a1 --- /dev/null +++ b/apps/dtm/syscfg.yml @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +syscfg.vals: + # Enable the shell task. + SHELL_TASK: 1 + SHELL_CMD_HELP: 1 + CONSOLE_HISTORY: ram + CONSOLE_HISTORY_RAM_HISTORY_SIZE: 50 + + BLE_LL_DTM: 1 + BLE_LL_DTM_EXTENSIONS: 1 + BLE_LL_CFG_FEAT_LE_2M_PHY: 1 + BLE_LL_CFG_FEAT_LE_CODED_PHY: 1