Bluetooth: audio: mcs: Offload notifications to system work queue

This implements the deferring of characteristic value notifications to
be sent from system work queue thread context. Notifications issued from
Bluetooth Rx thread might not be sent if there are no L2CAP Tx buffers
available, as the operation is non-blocking to prevent deadlock while
waiting for free buffers.
The same operation issued from other thread context is blocking,
meaning that the thread waits until L2CAP Tx buffers become available.
Thus it's guaranteed the notifications will be sent.

With this patch, the control point operations become blocking until the
control point response is sent. Meaning there might be only one
pending operation waiting for completition. This might be further
improved by queuing the operations if needed.

Fixes: #57444
Signed-off-by: Mariusz Skamra <mariusz.skamra@codecoup.pl>
This commit is contained in:
Mariusz Skamra 2023-05-15 09:14:20 +02:00 committed by Fabio Baltieri
commit 84fd596062
2 changed files with 544 additions and 197 deletions

View file

@ -171,15 +171,11 @@ static struct mcs_instance_t *lookup_inst_by_conn(struct bt_conn *conn)
return &mcs_inst;
}
static uint8_t mcc_read_player_name_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
static void mcc_player_name_cb(struct bt_conn *conn, uint8_t err, const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
int cb_err = err;
char name[CONFIG_BT_MCC_MEDIA_PLAYER_NAME_MAX];
mcs_inst->busy = false;
LOG_DBG("err: 0x%02x, length: %d, data: %p", err, length, data);
if (err) {
@ -201,6 +197,16 @@ static uint8_t mcc_read_player_name_cb(struct bt_conn *conn, uint8_t err,
if (mcc_cb && mcc_cb->read_player_name) {
mcc_cb->read_player_name(conn, cb_err, name);
}
}
static uint8_t mcc_read_player_name_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_player_name_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
@ -270,15 +276,11 @@ static uint8_t mcc_read_icon_url_cb(struct bt_conn *conn, uint8_t err,
return BT_GATT_ITER_STOP;
}
static uint8_t mcc_read_track_title_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
static void mcc_track_title_cb(struct bt_conn *conn, uint8_t err, const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
int cb_err = err;
char title[CONFIG_BT_MCC_TRACK_TITLE_MAX];
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if (!data) {
@ -297,19 +299,26 @@ static uint8_t mcc_read_track_title_cb(struct bt_conn *conn, uint8_t err,
if (mcc_cb && mcc_cb->read_track_title) {
mcc_cb->read_track_title(conn, cb_err, title);
}
return BT_GATT_ITER_STOP;
}
static uint8_t mcc_read_track_duration_cb(struct bt_conn *conn, uint8_t err,
static uint8_t mcc_read_track_title_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_track_title_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
static void mcc_track_duration_cb(struct bt_conn *conn, uint8_t err, const void *data,
uint16_t length)
{
int cb_err = err;
int32_t dur = 0;
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if ((!data) || (length != sizeof(dur))) {
@ -324,19 +333,26 @@ static uint8_t mcc_read_track_duration_cb(struct bt_conn *conn, uint8_t err,
if (mcc_cb && mcc_cb->read_track_duration) {
mcc_cb->read_track_duration(conn, cb_err, dur);
}
return BT_GATT_ITER_STOP;
}
static uint8_t mcc_read_track_position_cb(struct bt_conn *conn, uint8_t err,
static uint8_t mcc_read_track_duration_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_track_duration_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
static void mcc_track_position_cb(struct bt_conn *conn, uint8_t err, const void *data,
uint16_t length)
{
int cb_err = err;
int32_t pos = 0;
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if ((!data) || (length != sizeof(pos))) {
@ -351,6 +367,16 @@ static uint8_t mcc_read_track_position_cb(struct bt_conn *conn, uint8_t err,
if (mcc_cb && mcc_cb->read_track_position) {
mcc_cb->read_track_position(conn, cb_err, pos);
}
}
static uint8_t mcc_read_track_position_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_track_position_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
@ -379,15 +405,12 @@ static void mcs_write_track_position_cb(struct bt_conn *conn, uint8_t err,
}
}
static uint8_t mcc_read_playback_speed_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
static void mcc_playback_speed_cb(struct bt_conn *conn, uint8_t err, const void *data,
uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
int cb_err = err;
int8_t speed = 0;
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if ((!data) || (length != sizeof(speed))) {
@ -402,6 +425,16 @@ static uint8_t mcc_read_playback_speed_cb(struct bt_conn *conn, uint8_t err,
if (mcc_cb && mcc_cb->read_playback_speed) {
mcc_cb->read_playback_speed(conn, cb_err, speed);
}
}
static uint8_t mcc_read_playback_speed_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_playback_speed_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
@ -429,15 +462,12 @@ static void mcs_write_playback_speed_cb(struct bt_conn *conn, uint8_t err,
}
}
static uint8_t mcc_read_seeking_speed_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
static void mcc_seeking_speed_cb(struct bt_conn *conn, uint8_t err, const void *data,
uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
int cb_err = err;
int8_t speed = 0;
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if ((!data) || (length != sizeof(speed))) {
@ -452,6 +482,16 @@ static uint8_t mcc_read_seeking_speed_cb(struct bt_conn *conn, uint8_t err,
if (mcc_cb && mcc_cb->read_seeking_speed) {
mcc_cb->read_seeking_speed(conn, cb_err, speed);
}
}
static uint8_t mcc_read_seeking_speed_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_seeking_speed_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
@ -489,16 +529,13 @@ static uint8_t mcc_read_segments_obj_id_cb(struct bt_conn *conn, uint8_t err,
return BT_GATT_ITER_STOP;
}
static uint8_t mcc_read_current_track_obj_id_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
static void mcc_current_track_obj_id_cb(struct bt_conn *conn, uint8_t err, const void *data,
uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
int cb_err = err;
uint8_t *pid = (uint8_t *)data;
uint64_t id = 0;
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if ((!pid) || (length != BT_OTS_OBJ_ID_SIZE)) {
@ -517,6 +554,16 @@ static uint8_t mcc_read_current_track_obj_id_cb(struct bt_conn *conn, uint8_t er
if (mcc_cb && mcc_cb->read_current_track_obj_id) {
mcc_cb->read_current_track_obj_id(conn, cb_err, id);
}
}
static uint8_t mcc_read_current_track_obj_id_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_current_track_obj_id_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
@ -548,16 +595,13 @@ static void mcs_write_current_track_obj_id_cb(struct bt_conn *conn, uint8_t err,
}
}
static uint8_t mcc_read_next_track_obj_id_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
static void mcc_next_track_obj_id_cb(struct bt_conn *conn, uint8_t err, const void *data,
uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
int cb_err = err;
uint8_t *pid = (uint8_t *)data;
uint64_t id = 0;
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if (length == 0) {
@ -578,6 +622,16 @@ static uint8_t mcc_read_next_track_obj_id_cb(struct bt_conn *conn, uint8_t err,
if (mcc_cb && mcc_cb->read_next_track_obj_id) {
mcc_cb->read_next_track_obj_id(conn, cb_err, id);
}
}
static uint8_t mcc_read_next_track_obj_id_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_next_track_obj_id_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
@ -609,16 +663,13 @@ static void mcs_write_next_track_obj_id_cb(struct bt_conn *conn, uint8_t err,
}
}
static uint8_t mcc_read_parent_group_obj_id_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
static void mcc_parent_group_obj_id_cb(struct bt_conn *conn, uint8_t err, const void *data,
uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
int cb_err = err;
uint8_t *pid = (uint8_t *)data;
uint64_t id = 0;
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if (!pid || (length != BT_OTS_OBJ_ID_SIZE)) {
@ -637,20 +688,27 @@ static uint8_t mcc_read_parent_group_obj_id_cb(struct bt_conn *conn, uint8_t err
if (mcc_cb && mcc_cb->read_parent_group_obj_id) {
mcc_cb->read_parent_group_obj_id(conn, cb_err, id);
}
return BT_GATT_ITER_STOP;
}
static uint8_t mcc_read_current_group_obj_id_cb(struct bt_conn *conn, uint8_t err,
static uint8_t mcc_read_parent_group_obj_id_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_parent_group_obj_id_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
static void mcc_current_group_obj_id_cb(struct bt_conn *conn, uint8_t err, const void *data,
uint16_t length)
{
int cb_err = err;
uint8_t *pid = (uint8_t *)data;
uint64_t id = 0;
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if (!pid || (length != BT_OTS_OBJ_ID_SIZE)) {
@ -669,6 +727,16 @@ static uint8_t mcc_read_current_group_obj_id_cb(struct bt_conn *conn, uint8_t er
if (mcc_cb && mcc_cb->read_current_group_obj_id) {
mcc_cb->read_current_group_obj_id(conn, cb_err, id);
}
}
static uint8_t mcc_read_current_group_obj_id_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_current_group_obj_id_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
@ -701,15 +769,12 @@ static void mcs_write_current_group_obj_id_cb(struct bt_conn *conn, uint8_t err,
}
#endif /* CONFIG_BT_MCC_OTS */
static uint8_t mcc_read_playing_order_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
static void mcc_playing_order_cb(struct bt_conn *conn, uint8_t err, const void *data,
uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
int cb_err = err;
uint8_t order = 0;
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if ((!data) || (length != sizeof(order))) {
@ -724,6 +789,16 @@ static uint8_t mcc_read_playing_order_cb(struct bt_conn *conn, uint8_t err,
if (mcc_cb && mcc_cb->read_playing_order) {
mcc_cb->read_playing_order(conn, cb_err, order);
}
}
static uint8_t mcc_read_playing_order_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_playing_order_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
@ -778,15 +853,11 @@ static uint8_t mcc_read_playing_orders_supported_cb(struct bt_conn *conn, uint8_
return BT_GATT_ITER_STOP;
}
static uint8_t mcc_read_media_state_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
static void mcc_media_state_cb(struct bt_conn *conn, uint8_t err, const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
int cb_err = err;
uint8_t state = 0;
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if (!data || length != sizeof(state)) {
@ -801,6 +872,16 @@ static uint8_t mcc_read_media_state_cb(struct bt_conn *conn, uint8_t err,
if (mcc_cb && mcc_cb->read_media_state) {
mcc_cb->read_media_state(conn, cb_err, state);
}
}
static uint8_t mcc_read_media_state_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_media_state_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
@ -839,16 +920,12 @@ static void mcs_write_cp_cb(struct bt_conn *conn, uint8_t err,
}
}
static uint8_t mcc_read_opcodes_supported_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
static void mcc_opcodes_supported_cb(struct bt_conn *conn, uint8_t err, const void *data,
uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
int cb_err = err;
int32_t operations = 0;
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if ((!data) || (length != sizeof(operations))) {
@ -864,6 +941,16 @@ static uint8_t mcc_read_opcodes_supported_cb(struct bt_conn *conn, uint8_t err,
if (mcc_cb && mcc_cb->read_opcodes_supported) {
mcc_cb->read_opcodes_supported(conn, cb_err, operations);
}
}
static uint8_t mcc_read_opcodes_supported_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_opcodes_supported_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
@ -895,16 +982,13 @@ static void mcs_write_scp_cb(struct bt_conn *conn, uint8_t err,
}
}
static uint8_t mcc_read_search_results_obj_id_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
static void mcc_search_results_obj_id_cb(struct bt_conn *conn, uint8_t err,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
int cb_err = err;
uint8_t *pid = (uint8_t *)data;
uint64_t id = 0;
mcs_inst->busy = false;
if (err) {
LOG_DBG("err: 0x%02x", err);
} else if (length == 0) {
@ -926,6 +1010,16 @@ static uint8_t mcc_read_search_results_obj_id_cb(struct bt_conn *conn, uint8_t e
if (mcc_cb && mcc_cb->read_search_results_obj_id) {
mcc_cb->read_search_results_obj_id(conn, cb_err, id);
}
}
static uint8_t mcc_read_search_results_obj_id_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
mcs_inst->busy = false;
mcc_search_results_obj_id_cb(conn, err, data, length);
return BT_GATT_ITER_STOP;
}
@ -963,7 +1057,6 @@ static uint8_t mcs_notify_handler(struct bt_conn *conn,
struct bt_gatt_subscribe_params *params,
const void *data, uint16_t length)
{
struct bt_gatt_read_params *read_params;
uint16_t handle = params->value_handle;
struct mcs_instance_t *mcs_inst;
@ -981,18 +1074,11 @@ static uint8_t mcs_notify_handler(struct bt_conn *conn,
return BT_GATT_ITER_CONTINUE;
}
/* TODO: Work around for using CONTAINER_OF when re-using the
* read callbacks for notifications. The handling of the values
* should be put into separate functions instead of using the
* read callbacks
*/
read_params = &mcs_inst->read_params;
LOG_DBG("Notification, handle: %d", handle);
if (handle == mcs_inst->player_name_handle) {
LOG_DBG("Player Name notification");
mcc_read_player_name_cb(conn, 0, read_params, data, length);
mcc_player_name_cb(conn, 0, data, length);
} else if (handle == mcs_inst->track_changed_handle) {
/* The Track Changed characteristic can only be */
@ -1013,53 +1099,49 @@ static uint8_t mcs_notify_handler(struct bt_conn *conn,
} else if (handle == mcs_inst->track_title_handle) {
LOG_DBG("Track Title notification");
mcc_read_track_title_cb(conn, 0, read_params, data, length);
mcc_track_title_cb(conn, 0, data, length);
} else if (handle == mcs_inst->track_duration_handle) {
LOG_DBG("Track Duration notification");
mcc_read_track_duration_cb(conn, 0, read_params, data, length);
mcc_track_duration_cb(conn, 0, data, length);
} else if (handle == mcs_inst->track_position_handle) {
LOG_DBG("Track Position notification");
mcc_read_track_position_cb(conn, 0, read_params, data, length);
mcc_track_position_cb(conn, 0, data, length);
} else if (handle == mcs_inst->playback_speed_handle) {
LOG_DBG("Playback Speed notification");
mcc_read_playback_speed_cb(conn, 0, read_params, data, length);
mcc_playback_speed_cb(conn, 0, data, length);
} else if (handle == mcs_inst->seeking_speed_handle) {
LOG_DBG("Seeking Speed notification");
mcc_read_seeking_speed_cb(conn, 0, read_params, data, length);
mcc_seeking_speed_cb(conn, 0, data, length);
#ifdef CONFIG_BT_MCC_OTS
} else if (handle == mcs_inst->current_track_obj_id_handle) {
LOG_DBG("Current Track notification");
mcc_read_current_track_obj_id_cb(conn, 0, read_params, data,
length);
mcc_current_track_obj_id_cb(conn, 0, data, length);
} else if (handle == mcs_inst->next_track_obj_id_handle) {
LOG_DBG("Next Track notification");
mcc_read_next_track_obj_id_cb(conn, 0, read_params, data,
length);
mcc_next_track_obj_id_cb(conn, 0, data, length);
} else if (handle == mcs_inst->parent_group_obj_id_handle) {
LOG_DBG("Parent Group notification");
mcc_read_parent_group_obj_id_cb(conn, 0, read_params, data,
length);
mcc_parent_group_obj_id_cb(conn, 0, data, length);
} else if (handle == mcs_inst->current_group_obj_id_handle) {
LOG_DBG("Current Group notification");
mcc_read_current_group_obj_id_cb(conn, 0, read_params, data,
length);
mcc_current_group_obj_id_cb(conn, 0, data, length);
#endif /* CONFIG_BT_MCC_OTS */
} else if (handle == mcs_inst->playing_order_handle) {
LOG_DBG("Playing Order notification");
mcc_read_playing_order_cb(conn, 0, read_params, data, length);
mcc_playing_order_cb(conn, 0, data, length);
} else if (handle == mcs_inst->media_state_handle) {
LOG_DBG("Media State notification");
mcc_read_media_state_cb(conn, 0, read_params, data, length);
mcc_media_state_cb(conn, 0, data, length);
} else if (handle == mcs_inst->cp_handle) {
/* The control point is is a special case - only */
@ -1085,8 +1167,7 @@ static uint8_t mcs_notify_handler(struct bt_conn *conn,
} else if (handle == mcs_inst->opcodes_supported_handle) {
LOG_DBG("Opcodes Supported notification");
mcc_read_opcodes_supported_cb(conn, 0, read_params, data,
length);
mcc_opcodes_supported_cb(conn, 0, data, length);
#ifdef CONFIG_BT_MCC_OTS
} else if (handle == mcs_inst->scp_handle) {
@ -1110,8 +1191,7 @@ static uint8_t mcs_notify_handler(struct bt_conn *conn,
} else if (handle == mcs_inst->search_results_obj_id_handle) {
LOG_DBG("Search Results notification");
mcc_read_search_results_obj_id_cb(conn, 0, read_params, data,
length);
mcc_search_results_obj_id_cb(conn, 0, data, length);
#endif /* CONFIG_BT_MCC_OTS */
} else {
LOG_DBG("Unknown handle: %d (0x%04X)", handle, handle);

View file

@ -15,6 +15,7 @@
#include <zephyr/init.h>
#include <stdio.h>
#include <zephyr/types.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/util.h>
#include <zephyr/bluetooth/bluetooth.h>
@ -44,10 +45,38 @@ static void notify(const struct bt_uuid *uuid, const void *data, uint16_t len);
static struct media_proxy_sctrl_cbs cbs;
enum {
FLAG_PLAYER_NAME_CHANGED,
FLAG_ICON_URL_CHANGED,
FLAG_TRACK_CHANGED,
FLAG_TRACK_TITLE_CHANGED,
FLAG_TRACK_DURATION_CHANGED,
FLAG_TRACK_POSITION_CHANGED,
FLAG_PLAYBACK_SPEED_CHANGED,
FLAG_SEEKING_SPEED_CHANGED,
FLAG_PLAYING_ORDER_CHANGED,
FLAG_MEDIA_STATE_CHANGED,
FLAG_MEDIA_CONTROL_OPCODES_CHANGED,
FLAG_MEDIA_CONTROL_POINT_BUSY,
FLAG_MEDIA_CONTROL_POINT_RESULT,
#if defined(CONFIG_BT_OTS)
FLAG_CURRENT_TRACK_OBJ_ID_CHANGED,
FLAG_NEXT_TRACK_OBJ_ID_CHANGED,
FLAG_PARENT_GROUP_OBJ_ID_CHANGED,
FLAG_CURRENT_GROUP_OBJ_ID_CHANGED,
FLAG_SEARCH_RESULTS_OBJ_ID_CHANGED,
FLAG_SEARCH_CONTROL_POINT_BUSY,
FLAG_SEARCH_CONTROL_POINT_RESULT,
#endif /* CONFIG_BT_OTS */
FLAG_NUM,
};
static struct client_state {
bool player_name_changed;
bool icon_url_changed;
bool track_title_changed;
ATOMIC_DEFINE(flags, FLAG_NUM);
struct mpl_cmd_ntf cmd_ntf;
#if defined(CONFIG_BT_OTS)
uint8_t search_control_point_result;
#endif /* CONFIG_BT_OTS */
} clients[CONFIG_BT_MAX_CONN];
static void disconnected(struct bt_conn *conn, uint8_t reason)
@ -74,8 +103,8 @@ static ssize_t read_player_name(struct bt_conn *conn,
LOG_DBG("Player name read: %s (offset %u)", name, offset);
if (offset == 0) {
client->player_name_changed = false;
} else if (client->player_name_changed) {
atomic_clear_bit(client->flags, FLAG_PLAYER_NAME_CHANGED);
} else if (atomic_test_bit(client->flags, FLAG_PLAYER_NAME_CHANGED)) {
return BT_GATT_ERR(BT_MCS_ERR_LONG_VAL_CHANGED);
}
@ -112,8 +141,8 @@ static ssize_t read_icon_url(struct bt_conn *conn,
LOG_DBG("Icon URL read, offset: %d, len:%d, URL: %s", offset, len, url);
if (offset == 0) {
client->icon_url_changed = false;
} else if (client->icon_url_changed) {
atomic_clear_bit(client->flags, FLAG_ICON_URL_CHANGED);
} else if (atomic_test_bit(client->flags, FLAG_ICON_URL_CHANGED)) {
return BT_GATT_ERR(BT_MCS_ERR_LONG_VAL_CHANGED);
}
@ -136,8 +165,8 @@ static ssize_t read_track_title(struct bt_conn *conn,
LOG_DBG("Track title read, offset: %d, len:%d, title: %s", offset, len, title);
if (offset == 0) {
client->track_title_changed = false;
} else if (client->track_title_changed) {
atomic_clear_bit(client->flags, FLAG_TRACK_TITLE_CHANGED);
} else if (atomic_test_bit(client->flags, FLAG_TRACK_TITLE_CHANGED)) {
return BT_GATT_ERR(BT_MCS_ERR_LONG_VAL_CHANGED);
}
@ -155,10 +184,13 @@ static ssize_t read_track_duration(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct client_state *client = &clients[bt_conn_index(conn)];
int32_t duration = media_proxy_sctrl_get_track_duration();
LOG_DBG("Track duration read: %d (0x%08x)", duration, duration);
atomic_clear_bit(client->flags, FLAG_TRACK_DURATION_CHANGED);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &duration,
sizeof(duration));
}
@ -173,10 +205,13 @@ static ssize_t read_track_position(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct client_state *client = &clients[bt_conn_index(conn)];
int32_t position = media_proxy_sctrl_get_track_position();
LOG_DBG("Track position read: %d (0x%08x)", position, position);
atomic_clear_bit(client->flags, FLAG_TRACK_POSITION_CHANGED);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &position,
sizeof(position));
}
@ -214,10 +249,13 @@ static ssize_t read_playback_speed(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct client_state *client = &clients[bt_conn_index(conn)];
int8_t speed = media_proxy_sctrl_get_playback_speed();
LOG_DBG("Playback speed read: %d", speed);
atomic_clear_bit(client->flags, FLAG_PLAYBACK_SPEED_CHANGED);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &speed,
sizeof(speed));
}
@ -255,10 +293,13 @@ static ssize_t read_seeking_speed(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct client_state *client = &clients[bt_conn_index(conn)];
int8_t speed = media_proxy_sctrl_get_seeking_speed();
LOG_DBG("Seeking speed read: %d", speed);
atomic_clear_bit(client->flags, FLAG_SEEKING_SPEED_CHANGED);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &speed,
sizeof(speed));
}
@ -285,9 +326,13 @@ static ssize_t read_current_track_id(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct client_state *client = &clients[bt_conn_index(conn)];
uint64_t track_id = media_proxy_sctrl_get_current_track_id();
LOG_DBG_OBJ_ID("Current track ID read: ", track_id);
atomic_clear_bit(client->flags, FLAG_CURRENT_TRACK_OBJ_ID_CHANGED);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &track_id,
BT_OTS_OBJ_ID_SIZE);
}
@ -332,8 +377,11 @@ static ssize_t read_next_track_id(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct client_state *client = &clients[bt_conn_index(conn)];
uint64_t track_id = media_proxy_sctrl_get_next_track_id();
atomic_clear_bit(client->flags, FLAG_NEXT_TRACK_OBJ_ID_CHANGED);
if (track_id == MPL_NO_TRACK_ID) {
LOG_DBG("Next track read, but it is empty");
/* "If the media player has no next track, the length of the */
@ -386,9 +434,13 @@ static ssize_t read_parent_group_id(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct client_state *client = &clients[bt_conn_index(conn)];
uint64_t group_id = media_proxy_sctrl_get_parent_group_id();
LOG_DBG_OBJ_ID("Parent group read: ", group_id);
atomic_clear_bit(client->flags, FLAG_PARENT_GROUP_OBJ_ID_CHANGED);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &group_id,
BT_OTS_OBJ_ID_SIZE);
}
@ -403,9 +455,13 @@ static ssize_t read_current_group_id(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct client_state *client = &clients[bt_conn_index(conn)];
uint64_t group_id = media_proxy_sctrl_get_current_group_id();
LOG_DBG_OBJ_ID("Current group read: ", group_id);
atomic_clear_bit(client->flags, FLAG_CURRENT_GROUP_OBJ_ID_CHANGED);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &group_id,
BT_OTS_OBJ_ID_SIZE);
}
@ -451,10 +507,13 @@ static ssize_t read_playing_order(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct client_state *client = &clients[bt_conn_index(conn)];
uint8_t order = media_proxy_sctrl_get_playing_order();
LOG_DBG("Playing order read: %d (0x%02x)", order, order);
atomic_clear_bit(client->flags, FLAG_PLAYING_ORDER_CHANGED);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &order,
sizeof(order));
}
@ -506,10 +565,13 @@ static ssize_t read_media_state(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct client_state *client = &clients[bt_conn_index(conn)];
uint8_t state = media_proxy_sctrl_get_media_state();
LOG_DBG("Media state read: %d", state);
atomic_clear_bit(client->flags, FLAG_MEDIA_STATE_CHANGED);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &state,
sizeof(state));
}
@ -525,6 +587,7 @@ static ssize_t write_control_point(struct bt_conn *conn,
const void *buf, uint16_t len, uint16_t offset,
uint8_t flags)
{
struct client_state *client = &clients[bt_conn_index(conn)];
struct mpl_cmd command;
if (offset != 0) {
@ -553,6 +616,17 @@ static ssize_t write_control_point(struct bt_conn *conn,
notify(BT_UUID_MCS_MEDIA_CONTROL_POINT, &cmd_ntf, sizeof(cmd_ntf));
return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
} else if (atomic_test_and_set_bit(client->flags, FLAG_MEDIA_CONTROL_POINT_BUSY)) {
const struct mpl_cmd_ntf cmd_ntf = {
.requested_opcode = command.opcode,
.result_code = BT_MCS_OPC_NTF_CANNOT_BE_COMPLETED,
};
LOG_DBG("Busy with other operation");
notify(BT_UUID_MCS_MEDIA_CONTROL_POINT, &cmd_ntf, sizeof(cmd_ntf));
return BT_GATT_ERR(BT_ATT_ERR_PROCEDURE_IN_PROGRESS);
}
if (len == sizeof(command.opcode) + sizeof(command.param)) {
@ -578,10 +652,13 @@ static ssize_t read_opcodes_supported(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
struct client_state *client = &clients[bt_conn_index(conn)];
uint32_t opcodes = media_proxy_sctrl_get_commands_supported();
LOG_DBG("Opcodes_supported read: %d (0x%08x)", opcodes, opcodes);
atomic_clear_bit(client->flags, FLAG_MEDIA_CONTROL_OPCODES_CHANGED);
return bt_gatt_attr_read(conn, attr, buf, len, offset,
&opcodes, BT_MCS_OPCODES_SUPPORTED_LEN);
}
@ -598,6 +675,7 @@ static ssize_t write_search_control_point(struct bt_conn *conn,
const void *buf, uint16_t len,
uint16_t offset, uint8_t flags)
{
struct client_state *client = &clients[bt_conn_index(conn)];
struct mpl_search search = {0};
if (offset != 0) {
@ -608,6 +686,16 @@ static ssize_t write_search_control_point(struct bt_conn *conn,
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
}
if (atomic_test_and_set_bit(client->flags, FLAG_SEARCH_CONTROL_POINT_BUSY)) {
const uint8_t result_code = BT_MCS_SCP_NTF_FAILURE;
LOG_DBG("Busy with other operation");
notify(BT_UUID_MCS_SEARCH_CONTROL_POINT, &result_code, sizeof(result_code));
return BT_GATT_ERR(BT_ATT_ERR_PROCEDURE_IN_PROGRESS);
}
memcpy(&search.search, (char *)buf, len);
search.len = len;
LOG_DBG("Search length: %d", len);
@ -628,10 +716,13 @@ static ssize_t read_search_results_id(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
struct client_state *client = &clients[bt_conn_index(conn)];
uint64_t search_id = media_proxy_sctrl_get_search_results_id();
LOG_DBG_OBJ_ID("Search results id read: ", search_id);
atomic_clear_bit(client->flags, FLAG_SEARCH_RESULTS_OBJ_ID_CHANGED);
/* TODO: The permanent solution here should be that the call to */
/* mpl should fill the UUID in a pointed-to value, and return a */
/* length or an error code, to indicate whether this ID has a */
@ -830,66 +921,23 @@ static void notify(const struct bt_uuid *uuid, const void *data, uint16_t len)
}
}
struct string_ntf {
const struct bt_uuid *uuid;
const char *str;
};
static void notify_string_conn_cb(struct bt_conn *conn, void *data)
static void notify_string(struct bt_conn *conn, const struct bt_uuid *uuid, const char *str)
{
struct client_state *client = &clients[bt_conn_index(conn)];
const struct string_ntf *ntf = (struct string_ntf *)data;
const uint8_t att_header_size = 3; /* opcode + handle */
struct bt_conn_info info;
uint16_t att_mtu;
uint16_t maxlen;
int err;
err = bt_conn_get_info(conn, &info);
if (err != 0) {
LOG_ERR("Failed to get conn info: %d", err);
return;
}
if (info.state != BT_CONN_STATE_CONNECTED) {
/* Not connected */
return;
}
att_mtu = bt_gatt_get_mtu(conn);
__ASSERT(att_mtu > att_header_size, "Could not get valid ATT MTU");
maxlen = att_mtu - att_header_size; /* Subtract opcode and handle */
/* Send notifcation potentially truncated to the MTU */
err = bt_gatt_notify_uuid(conn, ntf->uuid, mcs.attrs, (void *)ntf->str,
MIN(strlen(ntf->str), maxlen));
err = bt_gatt_notify_uuid(conn, uuid, mcs.attrs, (void *)str,
MIN(strlen(str), maxlen));
if (err != 0) {
LOG_ERR("Notification error: %d", err);
}
if (bt_uuid_cmp(ntf->uuid, BT_UUID_MCS_TRACK_TITLE) == 0) {
client->track_title_changed = true;
} else if (bt_uuid_cmp(ntf->uuid, BT_UUID_MCS_PLAYER_NAME) == 0) {
client->player_name_changed = true;
} /* icon URL is handled separately as that cannot be notified */
}
/* Helper function to notify UTF8 string values
* Will truncate string to fit within notification if required.
* The string must be null-terminated.
*/
static void notify_string(const struct bt_uuid *uuid, const char *str)
{
struct string_ntf ntf = { .uuid = uuid, .str = str };
bt_conn_foreach(BT_CONN_TYPE_LE, notify_string_conn_cb, &ntf);
}
void media_proxy_sctrl_player_name_cb(const char *name)
{
LOG_DBG("Notifying player name: %s", name);
notify_string(BT_UUID_MCS_PLAYER_NAME, name);
}
static void mark_icon_url_changed_cb(struct bt_conn *conn, void *data)
@ -909,122 +957,341 @@ static void mark_icon_url_changed_cb(struct bt_conn *conn, void *data)
return;
}
client->icon_url_changed = true;
atomic_set_bit(client->flags, FLAG_ICON_URL_CHANGED);
}
void media_proxy_sctrl_icon_url_cb(const char *name)
static void notify_cb(struct bt_conn *conn, void *data)
{
struct client_state *client = &clients[bt_conn_index(conn)];
struct bt_conn_info info;
int err;
bt_conn_foreach(BT_CONN_TYPE_LE, mark_icon_url_changed_cb, NULL);
err = bt_conn_get_info(conn, &info);
if (err != 0) {
LOG_ERR("Failed to get conn info: %d", err);
return;
}
void media_proxy_sctrl_track_changed_cb(void)
{
LOG_DBG("Notifying track change");
notify(BT_UUID_MCS_TRACK_CHANGED, NULL, 0);
if (info.state != BT_CONN_STATE_CONNECTED) {
/* Not connected */
return;
}
void media_proxy_sctrl_track_title_cb(const char *title)
{
if (atomic_test_and_clear_bit(client->flags, FLAG_PLAYER_NAME_CHANGED)) {
const char *name = media_proxy_sctrl_get_player_name();
LOG_DBG("Notifying player name: %s", name);
notify_string(conn, BT_UUID_MCS_PLAYER_NAME, name);
}
if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_TITLE_CHANGED)) {
const char *title = media_proxy_sctrl_get_track_title();
LOG_DBG("Notifying track title: %s", title);
notify_string(BT_UUID_MCS_TRACK_TITLE, title);
notify_string(conn, BT_UUID_MCS_TRACK_TITLE, title);
}
void media_proxy_sctrl_track_position_cb(int32_t position)
{
LOG_DBG("Notifying track position: %d", position);
notify(BT_UUID_MCS_TRACK_POSITION, &position, sizeof(position));
}
if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_DURATION_CHANGED)) {
int32_t duration = media_proxy_sctrl_get_track_duration();
void media_proxy_sctrl_track_duration_cb(int32_t duration)
{
LOG_DBG("Notifying track duration: %d", duration);
notify(BT_UUID_MCS_TRACK_DURATION, &duration, sizeof(duration));
}
void media_proxy_sctrl_playback_speed_cb(int8_t speed)
{
if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_POSITION_CHANGED)) {
int32_t position = media_proxy_sctrl_get_track_position();
LOG_DBG("Notifying track position: %d", position);
notify(BT_UUID_MCS_TRACK_POSITION, &position, sizeof(position));
}
if (atomic_test_and_clear_bit(client->flags, FLAG_PLAYBACK_SPEED_CHANGED)) {
int8_t speed = media_proxy_sctrl_get_playback_speed();
LOG_DBG("Notifying playback speed: %d", speed);
notify(BT_UUID_MCS_PLAYBACK_SPEED, &speed, sizeof(speed));
}
void media_proxy_sctrl_seeking_speed_cb(int8_t speed)
{
if (atomic_test_and_clear_bit(client->flags, FLAG_SEEKING_SPEED_CHANGED)) {
int8_t speed = media_proxy_sctrl_get_seeking_speed();
LOG_DBG("Notifying seeking speed: %d", speed);
notify(BT_UUID_MCS_SEEKING_SPEED, &speed, sizeof(speed));
}
#if defined(CONFIG_BT_OTS)
if (atomic_test_and_clear_bit(client->flags, FLAG_CURRENT_TRACK_OBJ_ID_CHANGED)) {
uint64_t track_id = media_proxy_sctrl_get_current_track_id();
LOG_DBG_OBJ_ID("Notifying current track ID: ", track_id);
notify(BT_UUID_MCS_CURRENT_TRACK_OBJ_ID, &track_id, BT_OTS_OBJ_ID_SIZE);
}
if (atomic_test_and_clear_bit(client->flags, FLAG_NEXT_TRACK_OBJ_ID_CHANGED)) {
uint64_t track_id = media_proxy_sctrl_get_next_track_id();
if (track_id == MPL_NO_TRACK_ID) {
/* "If the media player has no next track, the length of the
* characteristic shall be zero."
*/
LOG_DBG_OBJ_ID("Notifying EMPTY next track ID: ", track_id);
notify(BT_UUID_MCS_NEXT_TRACK_OBJ_ID, NULL, 0);
} else {
LOG_DBG_OBJ_ID("Notifying next track ID: ", track_id);
notify(BT_UUID_MCS_NEXT_TRACK_OBJ_ID, &track_id, BT_OTS_OBJ_ID_SIZE);
}
}
if (atomic_test_and_clear_bit(client->flags, FLAG_PARENT_GROUP_OBJ_ID_CHANGED)) {
uint64_t group_id = media_proxy_sctrl_get_parent_group_id();
LOG_DBG_OBJ_ID("Notifying parent group ID: ", group_id);
notify(BT_UUID_MCS_PARENT_GROUP_OBJ_ID, &group_id, BT_OTS_OBJ_ID_SIZE);
}
if (atomic_test_and_clear_bit(client->flags, FLAG_CURRENT_GROUP_OBJ_ID_CHANGED)) {
uint64_t group_id = media_proxy_sctrl_get_current_group_id();
LOG_DBG_OBJ_ID("Notifying current group ID: ", group_id);
notify(BT_UUID_MCS_CURRENT_GROUP_OBJ_ID, &group_id, BT_OTS_OBJ_ID_SIZE);
}
#endif /* CONFIG_BT_OTS */
if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_CHANGED)) {
LOG_DBG("Notifying track change");
notify(BT_UUID_MCS_TRACK_CHANGED, NULL, 0);
}
if (atomic_test_and_clear_bit(client->flags, FLAG_PLAYING_ORDER_CHANGED)) {
uint8_t order = media_proxy_sctrl_get_playing_order();
LOG_DBG("Notifying playing order: %d", order);
notify(BT_UUID_MCS_PLAYING_ORDER, &order, sizeof(order));
}
if (atomic_test_and_clear_bit(client->flags, FLAG_MEDIA_STATE_CHANGED)) {
uint8_t state = media_proxy_sctrl_get_media_state();
LOG_DBG("Notifying media state: %d", state);
notify(BT_UUID_MCS_MEDIA_STATE, &state, sizeof(state));
}
if (atomic_test_and_clear_bit(client->flags, FLAG_MEDIA_CONTROL_OPCODES_CHANGED)) {
uint32_t opcodes = media_proxy_sctrl_get_commands_supported();
LOG_DBG("Notifying command opcodes supported: %d (0x%08x)", opcodes, opcodes);
notify(BT_UUID_MCS_MEDIA_CONTROL_OPCODES, &opcodes, BT_MCS_OPCODES_SUPPORTED_LEN);
}
#if defined(CONFIG_BT_OTS)
if (atomic_test_and_clear_bit(client->flags, FLAG_SEARCH_RESULTS_OBJ_ID_CHANGED)) {
uint64_t search_id = media_proxy_sctrl_get_search_results_id();
LOG_DBG_OBJ_ID("Notifying search results ID: ", search_id);
notify(BT_UUID_MCS_SEARCH_RESULTS_OBJ_ID, &search_id, BT_OTS_OBJ_ID_SIZE);
}
if (atomic_test_and_clear_bit(client->flags, FLAG_SEARCH_CONTROL_POINT_RESULT)) {
uint8_t result_code = client->search_control_point_result;
LOG_DBG("Notifying search control point - result: %d", result_code);
notify(BT_UUID_MCS_SEARCH_CONTROL_POINT, &result_code, sizeof(result_code));
}
#endif /* CONFIG_BT_OTS */
if (atomic_test_and_clear_bit(client->flags, FLAG_MEDIA_CONTROL_POINT_RESULT)) {
LOG_DBG("Notifying control point command - opcode: %d, result: %d",
client->cmd_ntf.requested_opcode, client->cmd_ntf.result_code);
notify(BT_UUID_MCS_MEDIA_CONTROL_POINT, &client->cmd_ntf, sizeof(client->cmd_ntf));
}
}
static void deferred_nfy_work_handler(struct k_work *work)
{
bt_conn_foreach(BT_CONN_TYPE_LE, notify_cb, NULL);
}
static K_WORK_DEFINE(deferred_nfy_work, deferred_nfy_work_handler);
static void defer_value_ntf(struct bt_conn *conn, void *data)
{
struct client_state *client = &clients[bt_conn_index(conn)];
struct bt_conn_info info;
int err;
err = bt_conn_get_info(conn, &info);
if (err != 0) {
LOG_ERR("Failed to get conn info: %d", err);
return;
}
if (info.state != BT_CONN_STATE_CONNECTED) {
/* Not connected */
return;
}
atomic_set_bit(client->flags, POINTER_TO_UINT(data));
k_work_submit(&deferred_nfy_work);
}
static void media_proxy_sctrl_player_name_cb(const char *name)
{
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_PLAYER_NAME_CHANGED));
}
void media_proxy_sctrl_icon_url_cb(const char *name)
{
bt_conn_foreach(BT_CONN_TYPE_LE, mark_icon_url_changed_cb, NULL);
}
void media_proxy_sctrl_track_changed_cb(void)
{
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_TRACK_CHANGED));
}
void media_proxy_sctrl_track_title_cb(const char *title)
{
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_TRACK_TITLE_CHANGED));
}
void media_proxy_sctrl_track_position_cb(int32_t position)
{
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_TRACK_POSITION_CHANGED));
}
void media_proxy_sctrl_track_duration_cb(int32_t duration)
{
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_TRACK_DURATION_CHANGED));
}
void media_proxy_sctrl_playback_speed_cb(int8_t speed)
{
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_PLAYBACK_SPEED_CHANGED));
}
void media_proxy_sctrl_seeking_speed_cb(int8_t speed)
{
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_SEEKING_SPEED_CHANGED));
}
#if defined(CONFIG_BT_OTS)
void media_proxy_sctrl_current_track_id_cb(uint64_t id)
{
LOG_DBG_OBJ_ID("Notifying current track ID: ", id);
notify(BT_UUID_MCS_CURRENT_TRACK_OBJ_ID, &id, BT_OTS_OBJ_ID_SIZE);
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_CURRENT_TRACK_OBJ_ID_CHANGED));
}
void media_proxy_sctrl_next_track_id_cb(uint64_t id)
{
if (id == MPL_NO_TRACK_ID) {
/* "If the media player has no next track, the length of the */
/* characteristic shall be zero." */
LOG_DBG_OBJ_ID("Notifying EMPTY next track ID: ", id);
notify(BT_UUID_MCS_NEXT_TRACK_OBJ_ID, NULL, 0);
} else {
LOG_DBG_OBJ_ID("Notifying next track ID: ", id);
notify(BT_UUID_MCS_NEXT_TRACK_OBJ_ID, &id, BT_OTS_OBJ_ID_SIZE);
}
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_NEXT_TRACK_OBJ_ID_CHANGED));
}
void media_proxy_sctrl_parent_group_id_cb(uint64_t id)
{
LOG_DBG_OBJ_ID("Notifying parent group ID: ", id);
notify(BT_UUID_MCS_PARENT_GROUP_OBJ_ID, &id, BT_OTS_OBJ_ID_SIZE);
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_PARENT_GROUP_OBJ_ID_CHANGED));
}
void media_proxy_sctrl_current_group_id_cb(uint64_t id)
{
LOG_DBG_OBJ_ID("Notifying current group ID: ", id);
notify(BT_UUID_MCS_CURRENT_GROUP_OBJ_ID, &id, BT_OTS_OBJ_ID_SIZE);
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_CURRENT_GROUP_OBJ_ID_CHANGED));
}
#endif /* CONFIG_BT_OTS */
void media_proxy_sctrl_playing_order_cb(uint8_t order)
{
LOG_DBG("Notifying playing order: %d", order);
notify(BT_UUID_MCS_PLAYING_ORDER, &order, sizeof(order));
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_PLAYING_ORDER_CHANGED));
}
void media_proxy_sctrl_media_state_cb(uint8_t state)
{
LOG_DBG("Notifying media state: %d", state);
notify(BT_UUID_MCS_MEDIA_STATE, &state, sizeof(state));
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_MEDIA_STATE_CHANGED));
}
static void defer_media_control_point_ntf(struct bt_conn *conn, void *data)
{
struct client_state *client = &clients[bt_conn_index(conn)];
const struct mpl_cmd_ntf *cmd_ntf = data;
struct bt_conn_info info;
int err;
err = bt_conn_get_info(conn, &info);
if (err != 0) {
LOG_ERR("Failed to get conn info: %d", err);
return;
}
if (info.state != BT_CONN_STATE_CONNECTED) {
/* Not connected */
return;
}
if (atomic_test_and_clear_bit(client->flags, FLAG_MEDIA_CONTROL_POINT_BUSY)) {
client->cmd_ntf = *cmd_ntf;
atomic_set_bit(client->flags, FLAG_MEDIA_CONTROL_POINT_RESULT);
k_work_submit(&deferred_nfy_work);
}
}
void media_proxy_sctrl_command_cb(const struct mpl_cmd_ntf *cmd_ntf)
{
LOG_DBG("Notifying control point command - opcode: %d, result: %d",
cmd_ntf->requested_opcode, cmd_ntf->result_code);
notify(BT_UUID_MCS_MEDIA_CONTROL_POINT, cmd_ntf, sizeof(*cmd_ntf));
/* FIXME: Control Point notification shall be sent to operation initiator only */
bt_conn_foreach(BT_CONN_TYPE_LE, defer_media_control_point_ntf, (void *)cmd_ntf);
}
void media_proxy_sctrl_commands_supported_cb(uint32_t opcodes)
{
LOG_DBG("Notifying command opcodes supported: %d (0x%08x)", opcodes, opcodes);
notify(BT_UUID_MCS_MEDIA_CONTROL_OPCODES, &opcodes,
BT_MCS_OPCODES_SUPPORTED_LEN);
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_MEDIA_CONTROL_OPCODES_CHANGED));
}
#if defined(CONFIG_BT_OTS)
static void defer_search_control_point_ntf(struct bt_conn *conn, void *data)
{
struct client_state *client = &clients[bt_conn_index(conn)];
struct bt_conn_info info;
int err;
err = bt_conn_get_info(conn, &info);
if (err != 0) {
LOG_ERR("Failed to get conn info: %d", err);
return;
}
if (info.state != BT_CONN_STATE_CONNECTED) {
/* Not connected */
return;
}
if (atomic_test_and_clear_bit(client->flags, FLAG_SEARCH_CONTROL_POINT_BUSY)) {
client->search_control_point_result = POINTER_TO_UINT(data);
atomic_set_bit(client->flags, FLAG_SEARCH_CONTROL_POINT_RESULT);
k_work_submit(&deferred_nfy_work);
}
}
void media_proxy_sctrl_search_cb(uint8_t result_code)
{
LOG_DBG("Notifying search control point - result: %d", result_code);
notify(BT_UUID_MCS_SEARCH_CONTROL_POINT, &result_code,
sizeof(result_code));
/* FIXME: Control Point notification shall be sent to operation initiator only */
bt_conn_foreach(BT_CONN_TYPE_LE, defer_search_control_point_ntf,
UINT_TO_POINTER(result_code));
}
void media_proxy_sctrl_search_results_id_cb(uint64_t id)
{
LOG_DBG_OBJ_ID("Notifying search results ID: ", id);
notify(BT_UUID_MCS_SEARCH_RESULTS_OBJ_ID, &id, BT_OTS_OBJ_ID_SIZE);
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
UINT_TO_POINTER(FLAG_SEARCH_RESULTS_OBJ_ID_CHANGED));
}
#endif /* CONFIG_BT_OTS */