zephyr/subsys/bluetooth/shell/mcc.c
Emil Gydesen 1d83e561e6 Bluetooth: Audio: Refactor use of cur_mcs_inst
Removes the global cur_mcs_inst and replace it
with lookup functionality.

This fixes an issue with bt_mcc_discover_mcs not being able
to be called more than once, as well
as streamline the implementation significantly.

This is also the first step towards not only supporting
multipe MCS instances for a single device, but also a
step towards handling multiple devices.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2022-11-15 12:18:50 +00:00

1541 lines
38 KiB
C

/** @file
* @brief Media Control Client shell implementation
*
*/
/*
* Copyright (c) 2020 - 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <zephyr/bluetooth/audio/mcc.h>
#include <zephyr/shell/shell.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include "bt.h"
#include <zephyr/bluetooth/services/ots.h>
#include "../services/ots/ots_client_internal.h"
#include "../audio/media_proxy_internal.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_MCC)
#define LOG_MODULE_NAME bt_mcc_shell
#include "common/log.h"
static struct bt_mcc_cb cb;
#ifdef CONFIG_BT_MCC_OTS
struct object_ids_t {
uint64_t icon_obj_id;
uint64_t track_segments_obj_id;
uint64_t current_track_obj_id;
uint64_t next_track_obj_id;
uint64_t parent_group_obj_id;
uint64_t current_group_obj_id;
uint64_t search_results_obj_id;
};
static struct object_ids_t obj_ids;
#endif /* CONFIG_BT_MCC_OTS */
static void mcc_discover_mcs_cb(struct bt_conn *conn, int err)
{
if (err) {
shell_error(ctx_shell, "Discovery failed (%d)", err);
return;
}
shell_print(ctx_shell, "Discovery complete");
}
static void mcc_read_player_name_cb(struct bt_conn *conn, int err, const char *name)
{
if (err) {
shell_error(ctx_shell, "Player Name read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Player name: %s", name);
}
#ifdef CONFIG_BT_MCC_OTS
static void mcc_read_icon_obj_id_cb(struct bt_conn *conn, int err, uint64_t id)
{
char str[BT_OTS_OBJ_ID_STR_LEN];
if (err) {
shell_error(ctx_shell, "Icon Object ID read failed (%d)", err);
return;
}
(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
shell_print(ctx_shell, "Icon object ID: %s", str);
obj_ids.icon_obj_id = id;
}
#endif /* CONFIG_BT_MCC_OTS */
static void mcc_read_icon_url_cb(struct bt_conn *conn, int err, const char *url)
{
if (err) {
shell_error(ctx_shell, "Icon URL read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Icon URL: 0x%s", url);
}
static void mcc_read_track_title_cb(struct bt_conn *conn, int err, const char *title)
{
if (err) {
shell_error(ctx_shell, "Track title read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Track title: %s", title);
}
static void mcc_track_changed_ntf_cb(struct bt_conn *conn, int err)
{
if (err) {
shell_error(ctx_shell, "Track changed notification failed (%d)", err);
return;
}
shell_print(ctx_shell, "Track changed");
}
static void mcc_read_track_duration_cb(struct bt_conn *conn, int err, int32_t dur)
{
if (err) {
shell_error(ctx_shell, "Track duration read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Track duration: %d", dur);
}
static void mcc_read_track_position_cb(struct bt_conn *conn, int err, int32_t pos)
{
if (err) {
shell_error(ctx_shell, "Track position read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Track Position: %d", pos);
}
static void mcc_set_track_position_cb(struct bt_conn *conn, int err, int32_t pos)
{
if (err) {
shell_error(ctx_shell, "Track Position set failed (%d)", err);
return;
}
shell_print(ctx_shell, "Track Position: %d", pos);
}
static void mcc_read_playback_speed_cb(struct bt_conn *conn, int err,
int8_t speed)
{
if (err) {
shell_error(ctx_shell, "Playback speed read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Playback speed: %d", speed);
}
static void mcc_set_playback_speed_cb(struct bt_conn *conn, int err, int8_t speed)
{
if (err) {
shell_error(ctx_shell, "Playback speed set failed (%d)", err);
return;
}
shell_print(ctx_shell, "Playback speed: %d", speed);
}
static void mcc_read_seeking_speed_cb(struct bt_conn *conn, int err,
int8_t speed)
{
if (err) {
shell_error(ctx_shell, "Seeking speed read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Seeking speed: %d", speed);
}
#ifdef CONFIG_BT_MCC_OTS
static void mcc_read_segments_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
char str[BT_OTS_OBJ_ID_STR_LEN];
if (err) {
shell_error(ctx_shell,
"Track Segments Object ID read failed (%d)", err);
return;
}
(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
shell_print(ctx_shell, "Track Segments Object ID: %s", str);
obj_ids.track_segments_obj_id = id;
}
static void mcc_read_current_track_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
char str[BT_OTS_OBJ_ID_STR_LEN];
if (err) {
shell_error(ctx_shell, "Current Track Object ID read failed (%d)",
err);
return;
}
(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
shell_print(ctx_shell, "Current Track Object ID: %s", str);
obj_ids.current_track_obj_id = id;
}
static void mcc_set_current_track_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
char str[BT_OTS_OBJ_ID_STR_LEN];
if (err) {
shell_error(ctx_shell, "Current Track Object ID set failed (%d)", err);
return;
}
(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
shell_print(ctx_shell, "Current Track Object ID written: %s", str);
}
static void mcc_read_next_track_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
char str[BT_OTS_OBJ_ID_STR_LEN];
if (err) {
shell_error(ctx_shell, "Next Track Object ID read failed (%d)",
err);
return;
}
if (id == MPL_NO_TRACK_ID) {
shell_print(ctx_shell, "Next Track Object ID is empty");
} else {
(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
shell_print(ctx_shell, "Next Track Object ID: %s", str);
}
obj_ids.next_track_obj_id = id;
}
static void mcc_set_next_track_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
char str[BT_OTS_OBJ_ID_STR_LEN];
if (err) {
shell_error(ctx_shell, "Next Track Object ID set failed (%d)", err);
return;
}
(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
shell_print(ctx_shell, "Next Track Object ID written: %s", str);
}
static void mcc_read_parent_group_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
char str[BT_OTS_OBJ_ID_STR_LEN];
if (err) {
shell_error(ctx_shell,
"Parent Group Object ID read failed (%d)", err);
return;
}
(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
shell_print(ctx_shell, "Parent Group Object ID: %s", str);
obj_ids.parent_group_obj_id = id;
}
static void mcc_read_current_group_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
char str[BT_OTS_OBJ_ID_STR_LEN];
if (err) {
shell_error(ctx_shell,
"Current Group Object ID read failed (%d)", err);
return;
}
(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
shell_print(ctx_shell, "Current Group Object ID: %s", str);
obj_ids.current_group_obj_id = id;
}
static void mcc_set_current_group_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
char str[BT_OTS_OBJ_ID_STR_LEN];
if (err) {
shell_error(ctx_shell, "Current Group Object ID set failed (%d)", err);
return;
}
(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
shell_print(ctx_shell, "Current Group Object ID written: %s", str);
}
#endif /* CONFIG_BT_MCC_OTS */
static void mcc_read_playing_order_cb(struct bt_conn *conn, int err, uint8_t order)
{
if (err) {
shell_error(ctx_shell, "Playing order read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Playing order: %d", order);
}
static void mcc_set_playing_order_cb(struct bt_conn *conn, int err, uint8_t order)
{
if (err) {
shell_error(ctx_shell, "Playing order set failed (%d)", err);
return;
}
shell_print(ctx_shell, "Playing order: %d", order);
}
static void mcc_read_playing_orders_supported_cb(struct bt_conn *conn, int err,
uint16_t orders)
{
if (err) {
shell_error(ctx_shell,
"Playing orders supported read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Playing orders supported: %d", orders);
}
static void mcc_read_media_state_cb(struct bt_conn *conn, int err, uint8_t state)
{
if (err) {
shell_error(ctx_shell, "Media State read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Media State: %d", state);
}
static void mcc_send_cmd_cb(struct bt_conn *conn, int err, const struct mpl_cmd *cmd)
{
if (err) {
shell_error(ctx_shell,
"Command send failed (%d) - opcode: %d, param: %d",
err, cmd->opcode, cmd->param);
return;
}
shell_print(ctx_shell, "Command opcode: %d, param: %d", cmd->opcode, cmd->param);
}
static void mcc_cmd_ntf_cb(struct bt_conn *conn, int err,
const struct mpl_cmd_ntf *ntf)
{
if (err) {
shell_error(ctx_shell,
"Command notification error (%d) - opcode: %d, result: %d",
err, ntf->requested_opcode, ntf->result_code);
return;
}
shell_print(ctx_shell, "Command opcode: %d, result: %d",
ntf->requested_opcode, ntf->result_code);
}
static void mcc_read_opcodes_supported_cb(struct bt_conn *conn, int err,
uint32_t opcodes)
{
if (err) {
shell_error(ctx_shell, "Opcodes supported read failed (%d)",
err);
return;
}
shell_print(ctx_shell, "Opcodes supported: %d", opcodes);
}
#ifdef CONFIG_BT_MCC_OTS
static void mcc_send_search_cb(struct bt_conn *conn, int err,
const struct mpl_search *search)
{
if (err) {
shell_error(ctx_shell,
"Search send failed (%d)", err);
return;
}
shell_print(ctx_shell, "Search sent");
}
static void mcc_search_ntf_cb(struct bt_conn *conn, int err, uint8_t result_code)
{
if (err) {
shell_error(ctx_shell,
"Search notification error (%d), result code: %d",
err, result_code);
return;
}
shell_print(ctx_shell, "Search notification result code: %d",
result_code);
}
static void mcc_read_search_results_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
char str[BT_OTS_OBJ_ID_STR_LEN];
if (err) {
shell_error(ctx_shell,
"Search Results Object ID read failed (%d)", err);
return;
}
if (id == 0) {
shell_print(ctx_shell, "Search Results Object ID: 0x000000000000");
} else {
(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
shell_print(ctx_shell, "Search Results Object ID: %s", str);
}
obj_ids.search_results_obj_id = id;
}
#endif /* CONFIG_BT_MCC_OTS */
static void mcc_read_content_control_id_cb(struct bt_conn *conn, int err, uint8_t ccid)
{
if (err) {
shell_error(ctx_shell, "Content Control ID read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Content Control ID: %d", ccid);
}
#ifdef CONFIG_BT_MCC_OTS
/**** Callback functions for the included Object Transfer service *************/
static void mcc_otc_obj_selected_cb(struct bt_conn *conn, int err)
{
if (err) {
shell_error(ctx_shell,
"Error in selecting object (err %d)", err);
return;
}
shell_print(ctx_shell, "Selecting object succeeded");
}
static void mcc_otc_obj_metadata_cb(struct bt_conn *conn, int err)
{
if (err) {
shell_error(ctx_shell,
"Error in reading object metadata (err %d)", err);
return;
}
shell_print(ctx_shell, "Reading object metadata succeeded\n");
}
static void mcc_icon_object_read_cb(struct bt_conn *conn, int err,
struct net_buf_simple *buf)
{
if (err) {
shell_error(ctx_shell,
"Icon Object read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Icon content (%d octets)", buf->len);
shell_hexdump(ctx_shell, buf->data, buf->len);
}
/* TODO: May want to use a parsed type, instead of the raw buf, here */
static void mcc_track_segments_object_read_cb(struct bt_conn *conn, int err,
struct net_buf_simple *buf)
{
if (err) {
shell_error(ctx_shell,
"Track Segments Object read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Track Segments content (%d octets)", buf->len);
shell_hexdump(ctx_shell, buf->data, buf->len);
}
static void mcc_otc_read_current_track_object_cb(struct bt_conn *conn, int err,
struct net_buf_simple *buf)
{
if (err) {
shell_error(ctx_shell,
"Current Track Object read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Current Track content (%d octets)", buf->len);
shell_hexdump(ctx_shell, buf->data, buf->len);
}
static void mcc_otc_read_next_track_object_cb(struct bt_conn *conn, int err,
struct net_buf_simple *buf)
{
if (err) {
shell_error(ctx_shell,
"Next Track Object read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Next Track content (%d octets)", buf->len);
shell_hexdump(ctx_shell, buf->data, buf->len);
}
static void mcc_otc_read_parent_group_object_cb(struct bt_conn *conn, int err,
struct net_buf_simple *buf)
{
if (err) {
shell_error(ctx_shell,
"Parent Group Object read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Parent Group content (%d octets)", buf->len);
shell_hexdump(ctx_shell, buf->data, buf->len);
}
static void mcc_otc_read_current_group_object_cb(struct bt_conn *conn, int err,
struct net_buf_simple *buf)
{
if (err) {
shell_error(ctx_shell,
"Current Group Object read failed (%d)", err);
return;
}
shell_print(ctx_shell, "Current Group content (%d octets)", buf->len);
shell_hexdump(ctx_shell, buf->data, buf->len);
}
#endif /* CONFIG_BT_MCC_OTS */
int cmd_mcc_init(const struct shell *sh, size_t argc, char **argv)
{
int result;
if (!ctx_shell) {
ctx_shell = sh;
}
/* Set up the callbacks */
cb.discover_mcs = mcc_discover_mcs_cb;
cb.read_player_name = mcc_read_player_name_cb;
#ifdef CONFIG_BT_MCC_OTS
cb.read_icon_obj_id = mcc_read_icon_obj_id_cb;
#endif /* CONFIG_BT_MCC_OTS */
cb.read_icon_url = mcc_read_icon_url_cb;
cb.track_changed_ntf = mcc_track_changed_ntf_cb;
cb.read_track_title = mcc_read_track_title_cb;
cb.read_track_duration = mcc_read_track_duration_cb;
cb.read_track_position = mcc_read_track_position_cb;
cb.set_track_position = mcc_set_track_position_cb;
cb.read_playback_speed = mcc_read_playback_speed_cb;
cb.set_playback_speed = mcc_set_playback_speed_cb;
cb.read_seeking_speed = mcc_read_seeking_speed_cb;
#ifdef CONFIG_BT_MCC_OTS
cb.read_segments_obj_id = mcc_read_segments_obj_id_cb;
cb.read_current_track_obj_id = mcc_read_current_track_obj_id_cb;
cb.set_current_track_obj_id = mcc_set_current_track_obj_id_cb;
cb.read_next_track_obj_id = mcc_read_next_track_obj_id_cb;
cb.set_next_track_obj_id = mcc_set_next_track_obj_id_cb;
cb.read_parent_group_obj_id = mcc_read_parent_group_obj_id_cb;
cb.read_current_group_obj_id = mcc_read_current_group_obj_id_cb;
cb.set_current_group_obj_id = mcc_set_current_group_obj_id_cb;
#endif /* CONFIG_BT_MCC_OTS */
cb.read_playing_order = mcc_read_playing_order_cb;
cb.set_playing_order = mcc_set_playing_order_cb;
cb.read_playing_orders_supported = mcc_read_playing_orders_supported_cb;
cb.read_media_state = mcc_read_media_state_cb;
cb.send_cmd = mcc_send_cmd_cb;
cb.cmd_ntf = mcc_cmd_ntf_cb;
cb.read_opcodes_supported = mcc_read_opcodes_supported_cb;
#ifdef CONFIG_BT_MCC_OTS
cb.send_search = mcc_send_search_cb;
cb.search_ntf = mcc_search_ntf_cb;
cb.read_search_results_obj_id = mcc_read_search_results_obj_id_cb;
#endif /* CONFIG_BT_MCC_OTS */
cb.read_content_control_id = mcc_read_content_control_id_cb;
#ifdef CONFIG_BT_MCC_OTS
cb.otc_obj_selected = mcc_otc_obj_selected_cb;
cb.otc_obj_metadata = mcc_otc_obj_metadata_cb;
cb.otc_icon_object = mcc_icon_object_read_cb;
cb.otc_track_segments_object = mcc_track_segments_object_read_cb;
cb.otc_current_track_object = mcc_otc_read_current_track_object_cb;
cb.otc_next_track_object = mcc_otc_read_next_track_object_cb;
cb.otc_parent_group_object = mcc_otc_read_parent_group_object_cb;
cb.otc_current_group_object = mcc_otc_read_current_group_object_cb;
#endif /* CONFIG_BT_MCC_OTS */
/* Initialize the module */
result = bt_mcc_init(&cb);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_discover_mcs(const struct shell *sh, size_t argc, char **argv)
{
int result;
int subscribe = 1;
if (argc > 1) {
subscribe = strtol(argv[1], NULL, 0);
if (subscribe < 0 || subscribe > 1) {
shell_error(sh, "Invalid parameter");
return -ENOEXEC;
}
}
result = bt_mcc_discover_mcs(default_conn, (bool)subscribe);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_player_name(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_player_name(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
#ifdef CONFIG_BT_MCC_OTS
int cmd_mcc_read_icon_obj_id(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_icon_obj_id(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
#endif /* CONFIG_BT_MCC_OTS */
int cmd_mcc_read_icon_url(const struct shell *sh, size_t argc, char *argv[])
{
int result;
result = bt_mcc_read_icon_url(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_track_title(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_track_title(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_track_duration(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_track_duration(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_track_position(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_track_position(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_set_track_position(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
int32_t pos = strtol(argv[1], NULL, 0);
/* Todo: Check input "pos" for validity, or for errors in conversion? */
result = bt_mcc_set_track_position(default_conn, pos);
if (result) {
shell_print(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_playback_speed(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_playback_speed(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_set_playback_speed(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
int8_t speed = strtol(argv[1], NULL, 0);
result = bt_mcc_set_playback_speed(default_conn, speed);
if (result) {
shell_print(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_seeking_speed(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_seeking_speed(default_conn);
if (result) {
shell_print(sh, "Fail: %d", result);
}
return result;
}
#ifdef CONFIG_BT_MCC_OTS
int cmd_mcc_read_track_segments_obj_id(const struct shell *sh,
size_t argc, char *argv[])
{
int result;
result = bt_mcc_read_segments_obj_id(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_current_track_obj_id(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_current_track_obj_id(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_set_current_track_obj_id(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
uint64_t id = strtoul(argv[1], NULL, 0);
id = id & 0x0000FFFFFFFFFFFFUL; /* 48 bits only */
result = bt_mcc_set_current_track_obj_id(default_conn, id);
if (result) {
shell_print(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_next_track_obj_id(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_next_track_obj_id(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_set_next_track_obj_id(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
uint64_t id = strtoul(argv[1], NULL, 0);
id = id & 0x0000FFFFFFFFFFFFUL; /* 48 bits only */
result = bt_mcc_set_next_track_obj_id(default_conn, id);
if (result) {
shell_print(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_parent_group_obj_id(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_parent_group_obj_id(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_current_group_obj_id(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_current_group_obj_id(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_set_current_group_obj_id(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
uint64_t id = strtoul(argv[1], NULL, 0);
id = id & 0x0000FFFFFFFFFFFFUL; /* 48 bits only */
result = bt_mcc_set_current_group_obj_id(default_conn, id);
if (result) {
shell_print(sh, "Fail: %d", result);
}
return result;
}
#endif /* CONFIG_BT_MCC_OTS */
int cmd_mcc_read_playing_order(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_playing_order(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_set_playing_order(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
uint8_t order = strtol(argv[1], NULL, 0);
result = bt_mcc_set_playing_order(default_conn, order);
if (result) {
shell_print(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_playing_orders_supported(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_playing_orders_supported(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_media_state(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_media_state(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_set_cp(const struct shell *sh, size_t argc, char *argv[])
{
int result;
struct mpl_cmd cmd;
if (argc > 1) {
cmd.opcode = strtol(argv[1], NULL, 0);
} else {
shell_error(sh, "Invalid parameter");
return -ENOEXEC;
}
if (argc > 2) {
cmd.use_param = true;
cmd.param = strtol(argv[2], NULL, 0);
} else {
cmd.use_param = false;
cmd.param = 0;
}
result = bt_mcc_send_cmd(default_conn, &cmd);
if (result) {
shell_print(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_read_opcodes_supported(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_opcodes_supported(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
#ifdef CONFIG_BT_MCC_OTS
int cmd_mcc_send_search_raw(const struct shell *sh, size_t argc, char *argv[])
{
int result;
struct mpl_search search;
search.len = strlen(argv[1]);
memcpy(search.search, argv[1], search.len);
BT_DBG("Search string: %s", argv[1]);
result = bt_mcc_send_search(default_conn, &search);
if (result) {
shell_print(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_send_search_ioptest(const struct shell *sh, size_t argc,
char *argv[])
{
/* Implementation follows Media control service testspec 0.9.0r13 */
/* Testcase MCS/SR/SCP/BV-01-C [Search Control Point], rounds 1 - 9 */
int result;
uint8_t testround;
struct mpl_sci sci_1 = {0};
struct mpl_sci sci_2 = {0};
struct mpl_search search;
testround = strtol(argv[1], NULL, 0);
switch (testround) {
case 1:
case 8:
case 9:
/* 1, 8 and 9 have the same first SCI */
sci_1.type = BT_MCS_SEARCH_TYPE_TRACK_NAME;
strcpy(sci_1.param, "TSPX_Track_Name");
break;
case 2:
sci_1.type = BT_MCS_SEARCH_TYPE_ARTIST_NAME;
strcpy(sci_1.param, "TSPX_Artist_Name");
break;
case 3:
sci_1.type = BT_MCS_SEARCH_TYPE_ALBUM_NAME;
strcpy(sci_1.param, "TSPX_Album_Name");
break;
case 4:
sci_1.type = BT_MCS_SEARCH_TYPE_GROUP_NAME;
strcpy(sci_1.param, "TSPX_Group_Name");
break;
case 5:
sci_1.type = BT_MCS_SEARCH_TYPE_EARLIEST_YEAR;
strcpy(sci_1.param, "TSPX_Earliest_Year");
break;
case 6:
sci_1.type = BT_MCS_SEARCH_TYPE_LATEST_YEAR;
strcpy(sci_1.param, "TSPX_Latest_Year");
break;
case 7:
sci_1.type = BT_MCS_SEARCH_TYPE_GENRE;
strcpy(sci_1.param, "TSPX_Genre");
break;
default:
shell_error(sh, "Invalid parameter");
return -ENOEXEC;
}
switch (testround) {
case 8:
sci_2.type = BT_MCS_SEARCH_TYPE_ONLY_TRACKS;
break;
case 9:
sci_2.type = BT_MCS_SEARCH_TYPE_ONLY_GROUPS;
break;
}
/* Length is length of type, plus length of param w/o termination */
sci_1.len = sizeof(sci_1.type) + strlen(sci_1.param);
search.len = 0;
memcpy(&search.search[search.len], &sci_1.len, sizeof(sci_1.len));
search.len += sizeof(sci_1.len);
memcpy(&search.search[search.len], &sci_1.type, sizeof(sci_1.type));
search.len += sizeof(sci_1.type);
memcpy(&search.search[search.len], &sci_1.param, strlen(sci_1.param));
search.len += strlen(sci_1.param);
if (testround == 8 || testround == 9) {
sci_2.len = sizeof(sci_2.type); /* The type only, no param */
memcpy(&search.search[search.len], &sci_2.len,
sizeof(sci_2.len));
search.len += sizeof(sci_2.len);
memcpy(&search.search[search.len], &sci_2.type,
sizeof(sci_2.type));
search.len += sizeof(sci_2.type);
}
shell_print(sh, "Search string: ");
shell_hexdump(sh, (uint8_t *)&search.search, search.len);
result = bt_mcc_send_search(default_conn, &search);
if (result) {
shell_print(sh, "Fail: %d", result);
}
return result;
}
#if defined(CONFIG_BT_DEBUG_MCC) && defined(CONFIG_BT_TESTING)
int cmd_mcc_test_send_search_iop_invalid_type(const struct shell *sh,
size_t argc, char *argv[])
{
int result;
struct mpl_search search;
search.search[0] = 2;
search.search[1] = (char)14; /* Invalid type value */
search.search[2] = 't'; /* Anything */
search.len = 3;
shell_print(sh, "Search string: ");
shell_hexdump(sh, (uint8_t *)&search.search, search.len);
result = bt_mcc_send_search(default_conn, &search);
if (result) {
shell_print(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_test_send_search_invalid_sci_len(const struct shell *sh,
size_t argc, char *argv[])
{
/* Reproduce a search that caused hard fault when sent from peer */
/* in IOP testing */
int result;
struct mpl_search search;
char offending_search[9] = {6, 1, 't', 'r', 'a', 'c', 'k', 0, 1 };
search.len = 9;
memcpy(&search.search, offending_search, search.len);
shell_print(sh, "Search string: ");
shell_hexdump(sh, (uint8_t *)&search.search, search.len);
result = bt_mcc_send_search(default_conn, &search);
if (result) {
shell_print(sh, "Fail: %d", result);
}
return result;
}
#endif /* CONFIG_BT_DEBUG_MCC && CONFIG_BT_TESTING */
int cmd_mcc_read_search_results_obj_id(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_search_results_obj_id(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
#endif /* CONFIG_BT_MCC_OTS */
int cmd_mcc_read_content_control_id(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_mcc_read_content_control_id(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
#ifdef CONFIG_BT_MCC_OTS
int cmd_mcc_otc_read_features(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_ots_client_read_feature(bt_mcc_otc_inst(default_conn),
default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_read(const struct shell *sh, size_t argc, char *argv[])
{
int result;
result = bt_ots_client_read_object_data(bt_mcc_otc_inst(default_conn),
default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_read_metadata(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_ots_client_read_object_metadata(bt_mcc_otc_inst(default_conn),
default_conn,
BT_OTS_METADATA_REQ_ALL);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_select(const struct shell *sh, size_t argc, char *argv[])
{
int result;
uint64_t id;
if (argc > 1) {
id = strtol(argv[1], NULL, 0);
} else {
shell_error(sh, "Invalid parameter, requires the Object ID");
return -ENOEXEC;
}
result = bt_ots_client_select_id(bt_mcc_otc_inst(default_conn),
default_conn, id);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_select_first(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_ots_client_select_first(bt_mcc_otc_inst(default_conn),
default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_select_last(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_ots_client_select_last(bt_mcc_otc_inst(default_conn),
default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_select_next(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_ots_client_select_next(bt_mcc_otc_inst(default_conn),
default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_select_prev(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_ots_client_select_prev(bt_mcc_otc_inst(default_conn),
default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_read_icon_object(const struct shell *sh, size_t argc,
char *argv[])
{
/* Assumes the Icon Object has already been selected by ID */
int result;
result = bt_mcc_otc_read_icon_object(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_read_track_segments_object(const struct shell *sh,
size_t argc, char *argv[])
{
/* Assumes the Segment Object has already been selected by ID */
int result;
result = bt_mcc_otc_read_track_segments_object(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_read_current_track_object(const struct shell *sh,
size_t argc, char *argv[])
{
/* Assumes the Curent Track Object has already been selected by ID */
int result;
result = bt_mcc_otc_read_current_track_object(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_read_next_track_object(const struct shell *sh,
size_t argc, char *argv[])
{
/* Assumes the Next Track Object has already been selected by ID */
int result;
result = bt_mcc_otc_read_next_track_object(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_read_parent_group_object(const struct shell *sh,
size_t argc, char *argv[])
{
/* Assumes the Parent Group Object has already been selected by ID */
int result;
result = bt_mcc_otc_read_parent_group_object(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_otc_read_current_group_object(const struct shell *sh,
size_t argc, char *argv[])
{
/* Assumes the Current Group Object has already been selected by ID */
int result;
result = bt_mcc_otc_read_current_group_object(default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_ots_select_first(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_ots_client_select_first(0, default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_ots_select_last(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_ots_client_select_last(0, default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_ots_select_next(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_ots_client_select_next(0, default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
int cmd_mcc_ots_select_prev(const struct shell *sh, size_t argc,
char *argv[])
{
int result;
result = bt_ots_client_select_prev(0, default_conn);
if (result) {
shell_error(sh, "Fail: %d", result);
}
return result;
}
#endif /* CONFIG_BT_MCC_OTS */
static int cmd_mcc(const struct shell *sh, size_t argc, char **argv)
{
shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
return -ENOEXEC;
}
SHELL_STATIC_SUBCMD_SET_CREATE(mcc_cmds,
SHELL_CMD_ARG(init, NULL, "Initialize client",
cmd_mcc_init, 1, 0),
SHELL_CMD_ARG(discover_mcs, NULL,
"Discover Media Control Service [subscribe]",
cmd_mcc_discover_mcs, 1, 1),
SHELL_CMD_ARG(read_player_name, NULL, "Read Media Player Name",
cmd_mcc_read_player_name, 1, 0),
#ifdef CONFIG_BT_MCC_OTS
SHELL_CMD_ARG(read_icon_obj_id, NULL, "Read Icon Object ID",
cmd_mcc_read_icon_obj_id, 1, 0),
#endif /* CONFIG_BT_MCC_OTS */
SHELL_CMD_ARG(read_icon_url, NULL, "Read Icon URL",
cmd_mcc_read_icon_url, 1, 0),
SHELL_CMD_ARG(read_track_title, NULL, "Read Track Title",
cmd_mcc_read_track_title, 1, 0),
SHELL_CMD_ARG(read_track_duration, NULL, "Read Track Duration",
cmd_mcc_read_track_duration, 1, 0),
SHELL_CMD_ARG(read_track_position, NULL, "Read Track Position",
cmd_mcc_read_track_position, 1, 0),
SHELL_CMD_ARG(set_track_position, NULL, "Set Track position <position>",
cmd_mcc_set_track_position, 2, 0),
SHELL_CMD_ARG(read_playback_speed, NULL, "Read Playback Speed",
cmd_mcc_read_playback_speed, 1, 0),
SHELL_CMD_ARG(set_playback_speed, NULL, "Set Playback Speed <speed>",
cmd_mcc_set_playback_speed, 2, 0),
SHELL_CMD_ARG(read_seeking_speed, NULL, "Read Seeking Speed",
cmd_mcc_read_seeking_speed, 1, 0),
#ifdef CONFIG_BT_MCC_OTS
SHELL_CMD_ARG(read_track_segments_obj_id, NULL,
"Read Track Segments Object ID",
cmd_mcc_read_track_segments_obj_id, 1, 0),
SHELL_CMD_ARG(read_current_track_obj_id, NULL,
"Read Current Track Object ID",
cmd_mcc_read_current_track_obj_id, 1, 0),
SHELL_CMD_ARG(set_current_track_obj_id, NULL,
"Set Current Track Object ID <id: 48 bits or less>",
cmd_mcc_set_current_track_obj_id, 2, 0),
SHELL_CMD_ARG(read_next_track_obj_id, NULL,
"Read Next Track Object ID",
cmd_mcc_read_next_track_obj_id, 1, 0),
SHELL_CMD_ARG(set_next_track_obj_id, NULL,
"Set Next Track Object ID <id: 48 bits or less>",
cmd_mcc_set_next_track_obj_id, 2, 0),
SHELL_CMD_ARG(read_current_group_obj_id, NULL,
"Read Current Group Object ID",
cmd_mcc_read_current_group_obj_id, 1, 0),
SHELL_CMD_ARG(read_parent_group_obj_id, NULL,
"Read Parent Group Object ID",
cmd_mcc_read_parent_group_obj_id, 1, 0),
SHELL_CMD_ARG(set_current_group_obj_id, NULL,
"Set Current Group Object ID <id: 48 bits or less>",
cmd_mcc_set_current_group_obj_id, 2, 0),
#endif /* CONFIG_BT_MCC_OTS */
SHELL_CMD_ARG(read_playing_order, NULL, "Read Playing Order",
cmd_mcc_read_playing_order, 1, 0),
SHELL_CMD_ARG(set_playing_order, NULL, "Set Playing Order <order>",
cmd_mcc_set_playing_order, 2, 0),
SHELL_CMD_ARG(read_playing_orders_supported, NULL,
"Read Playing Orders Supported",
cmd_mcc_read_playing_orders_supported, 1, 0),
SHELL_CMD_ARG(read_media_state, NULL, "Read Media State",
cmd_mcc_read_media_state, 1, 0),
SHELL_CMD_ARG(set_cp, NULL, "Set opcode/operation <opcode> [argument]",
cmd_mcc_set_cp, 2, 1),
SHELL_CMD_ARG(read_opcodes_supported, NULL, "Read Opcodes Supported",
cmd_mcc_read_opcodes_supported, 1, 0),
#ifdef CONFIG_BT_MCC_OTS
SHELL_CMD_ARG(send_search_raw, NULL, "Send search <search control item sequence>",
cmd_mcc_send_search_raw, 2, 0),
SHELL_CMD_ARG(send_search_scp_ioptest, NULL,
"Send search - IOP test round as input <round number>",
cmd_mcc_send_search_ioptest, 2, 0),
#if defined(CONFIG_BT_DEBUG_MCC) && defined(CONFIG_BT_TESTING)
SHELL_CMD_ARG(test_send_search_iop_invalid_type, NULL,
"Send search - IOP test, invalid type value (test)",
cmd_mcc_test_send_search_iop_invalid_type, 1, 0),
SHELL_CMD_ARG(test_send_Search_invalid_sci_len, NULL,
"Send search - invalid sci length (test)",
cmd_mcc_test_send_search_invalid_sci_len, 1, 0),
#endif /* CONFIG_BT_DEBUG_MCC && CONFIG_BT_TESTING */
SHELL_CMD_ARG(read_search_results_obj_id, NULL,
"Read Search Results Object ID",
cmd_mcc_read_search_results_obj_id, 1, 0),
#endif /* CONFIG_BT_MCC_OTS */
SHELL_CMD_ARG(read_content_control_id, NULL, "Read Content Control ID",
cmd_mcc_read_content_control_id, 1, 0),
#ifdef CONFIG_BT_MCC_OTS
SHELL_CMD_ARG(ots_read_features, NULL, "Read OTC Features",
cmd_mcc_otc_read_features, 1, 0),
SHELL_CMD_ARG(ots_oacp_read, NULL, "Read current object",
cmd_mcc_otc_read, 1, 0),
SHELL_CMD_ARG(ots_read_metadata, NULL, "Read current object's metadata",
cmd_mcc_otc_read_metadata, 1, 0),
SHELL_CMD_ARG(ots_select, NULL, "Select an object by its ID <ID>",
cmd_mcc_otc_select, 2, 0),
SHELL_CMD_ARG(ots_read_icon_object, NULL, "Read Icon Object",
cmd_mcc_otc_read_icon_object, 1, 0),
SHELL_CMD_ARG(ots_read_track_segments_object, NULL,
"Read Track Segments Object",
cmd_mcc_otc_read_track_segments_object, 1, 0),
SHELL_CMD_ARG(ots_read_current_track_object, NULL,
"Read Current Track Object",
cmd_mcc_otc_read_current_track_object, 1, 0),
SHELL_CMD_ARG(ots_read_next_track_object, NULL,
"Read Next Track Object",
cmd_mcc_otc_read_next_track_object, 1, 0),
SHELL_CMD_ARG(ots_read_parent_group_object, NULL,
"Read Parent Group Object",
cmd_mcc_otc_read_parent_group_object, 1, 0),
SHELL_CMD_ARG(ots_read_current_group_object, NULL,
"Read Current Group Object",
cmd_mcc_otc_read_current_group_object, 1, 0),
SHELL_CMD_ARG(ots_select_first, NULL, "Select first object",
cmd_mcc_otc_select_first, 1, 0),
SHELL_CMD_ARG(ots_select_last, NULL, "Select last object",
cmd_mcc_otc_select_last, 1, 0),
SHELL_CMD_ARG(ots_select_next, NULL, "Select next object",
cmd_mcc_otc_select_next, 1, 0),
SHELL_CMD_ARG(ots_select_previous, NULL, "Select previous object",
cmd_mcc_otc_select_prev, 1, 0),
#endif /* CONFIG_BT_MCC_OTS */
SHELL_SUBCMD_SET_END
);
SHELL_CMD_ARG_REGISTER(mcc, &mcc_cmds, "MCC commands",
cmd_mcc, 1, 1);