/** * @file * @brief Bluetooth Hearing Access Service (HAS) shell. * * Copyright (c) 2022 Codecoup * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include "host/shell/bt.h" static struct bt_has *inst; static void has_client_discover_cb(struct bt_conn *conn, int err, struct bt_has *has, enum bt_has_hearing_aid_type type, enum bt_has_capabilities caps) { if (err) { shell_error(ctx_shell, "HAS discovery (err %d)", err); return; } shell_print(ctx_shell, "HAS discovered %p type 0x%02x caps 0x%02x for conn %p", has, type, caps, conn); inst = has; } static void has_client_preset_switch_cb(struct bt_has *has, int err, uint8_t index) { if (err != 0) { shell_error(ctx_shell, "HAS %p preset switch error (err %d)", has, err); } else { shell_print(ctx_shell, "HAS %p preset switch index 0x%02x", has, index); } } static void has_client_preset_read_rsp_cb(struct bt_has *has, int err, const struct bt_has_preset_record *record, bool is_last) { if (err) { shell_error(ctx_shell, "Preset Read operation failed (err %d)", err); return; } shell_print(ctx_shell, "Preset Index: 0x%02x\tProperties: 0x%02x\tName: %s", record->index, record->properties, record->name); if (is_last) { shell_print(ctx_shell, "Preset Read operation complete"); } } static const struct bt_has_client_cb has_client_cb = { .discover = has_client_discover_cb, .preset_switch = has_client_preset_switch_cb, .preset_read_rsp = has_client_preset_read_rsp_cb, }; static int cmd_has_client_init(const struct shell *sh, size_t argc, char **argv) { int err; if (!ctx_shell) { ctx_shell = sh; } err = bt_has_client_cb_register(&has_client_cb); if (err != 0) { shell_error(sh, "bt_has_client_cb_register (err %d)", err); } return err; } static int cmd_has_client_discover(const struct shell *sh, size_t argc, char **argv) { int err; if (default_conn == NULL) { shell_error(sh, "Not connected"); return -ENOEXEC; } if (!ctx_shell) { ctx_shell = sh; } err = bt_has_client_discover(default_conn); if (err != 0) { shell_error(sh, "bt_has_client_discover (err %d)", err); } return err; } static int cmd_has_client_read_presets(const struct shell *sh, size_t argc, char **argv) { int err; const uint8_t index = shell_strtoul(argv[1], 16, &err); const uint8_t count = shell_strtoul(argv[2], 10, &err); if (err < 0) { shell_error(sh, "Invalid command parameter (err %d)", err); return err; } if (default_conn == NULL) { shell_error(sh, "Not connected"); return -ENOEXEC; } if (!inst) { shell_error(sh, "No instance discovered"); return -ENOEXEC; } err = bt_has_client_presets_read(inst, index, count); if (err != 0) { shell_error(sh, "bt_has_client_discover (err %d)", err); } return err; } static int cmd_has_client_preset_set(const struct shell *sh, size_t argc, char **argv) { bool sync = false; uint8_t index; int err = 0; index = shell_strtoul(argv[1], 16, &err); if (err < 0) { shell_error(sh, "Invalid command parameter (err %d)", err); return -ENOEXEC; } for (size_t argn = 2; argn < argc; argn++) { const char *arg = argv[argn]; if (!strcmp(arg, "sync")) { sync = true; } else { shell_error(sh, "Invalid argument"); return -ENOEXEC; } } if (default_conn == NULL) { shell_error(sh, "Not connected"); return -ENOEXEC; } if (!inst) { shell_error(sh, "No instance discovered"); return -ENOEXEC; } err = bt_has_client_preset_set(inst, index, sync); if (err != 0) { shell_error(sh, "bt_has_client_preset_switch (err %d)", err); return -ENOEXEC; } return 0; } static int cmd_has_client_preset_next(const struct shell *sh, size_t argc, char **argv) { bool sync = false; int err; for (size_t argn = 1; argn < argc; argn++) { const char *arg = argv[argn]; if (!strcmp(arg, "sync")) { sync = true; } else { shell_error(sh, "Invalid argument"); return -ENOEXEC; } } if (default_conn == NULL) { shell_error(sh, "Not connected"); return -ENOEXEC; } if (!inst) { shell_error(sh, "No instance discovered"); return -ENOEXEC; } err = bt_has_client_preset_next(inst, sync); if (err != 0) { shell_error(sh, "bt_has_client_preset_next (err %d)", err); return -ENOEXEC; } return err; } static int cmd_has_client_preset_prev(const struct shell *sh, size_t argc, char **argv) { bool sync = false; int err; for (size_t argn = 1; argn < argc; argn++) { const char *arg = argv[argn]; if (!strcmp(arg, "sync")) { sync = true; } else { shell_error(sh, "Invalid argument"); return -ENOEXEC; } } if (default_conn == NULL) { shell_error(sh, "Not connected"); return -ENOEXEC; } if (!inst) { shell_error(sh, "No instance discovered"); return -ENOEXEC; } err = bt_has_client_preset_prev(inst, sync); if (err != 0) { shell_error(sh, "bt_has_client_preset_prev (err %d)", err); return -ENOEXEC; } return err; } static int cmd_has_client(const struct shell *sh, size_t argc, char **argv) { if (argc > 1) { shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]); } else { shell_error(sh, "%s missing subcomand", argv[0]); } return -ENOEXEC; } #define HELP_NONE "[none]" SHELL_STATIC_SUBCMD_SET_CREATE(has_client_cmds, SHELL_CMD_ARG(init, NULL, HELP_NONE, cmd_has_client_init, 1, 0), SHELL_CMD_ARG(discover, NULL, HELP_NONE, cmd_has_client_discover, 1, 0), SHELL_CMD_ARG(presets_read, NULL, " ", cmd_has_client_read_presets, 3, 0), SHELL_CMD_ARG(preset_set, NULL, " [sync]", cmd_has_client_preset_set, 2, 1), SHELL_CMD_ARG(preset_next, NULL, "[sync]", cmd_has_client_preset_next, 1, 1), SHELL_CMD_ARG(preset_prev, NULL, "[sync]", cmd_has_client_preset_prev, 1, 1), SHELL_SUBCMD_SET_END ); SHELL_CMD_ARG_REGISTER(has_client, &has_client_cmds, "Bluetooth HAS client shell commands", cmd_has_client, 1, 1);