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:
parent
94853e310a
commit
84fd596062
2 changed files with 544 additions and 197 deletions
|
@ -171,15 +171,11 @@ static struct mcs_instance_t *lookup_inst_by_conn(struct bt_conn *conn)
|
||||||
return &mcs_inst;
|
return &mcs_inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t mcc_read_player_name_cb(struct bt_conn *conn, uint8_t err,
|
static void mcc_player_name_cb(struct bt_conn *conn, uint8_t err, const void *data, uint16_t length)
|
||||||
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);
|
|
||||||
int cb_err = err;
|
int cb_err = err;
|
||||||
char name[CONFIG_BT_MCC_MEDIA_PLAYER_NAME_MAX];
|
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);
|
LOG_DBG("err: 0x%02x, length: %d, data: %p", err, length, data);
|
||||||
|
|
||||||
if (err) {
|
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) {
|
if (mcc_cb && mcc_cb->read_player_name) {
|
||||||
mcc_cb->read_player_name(conn, cb_err, 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;
|
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;
|
return BT_GATT_ITER_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t mcc_read_track_title_cb(struct bt_conn *conn, uint8_t err,
|
static void mcc_track_title_cb(struct bt_conn *conn, uint8_t err, const void *data, uint16_t length)
|
||||||
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);
|
|
||||||
int cb_err = err;
|
int cb_err = err;
|
||||||
char title[CONFIG_BT_MCC_TRACK_TITLE_MAX];
|
char title[CONFIG_BT_MCC_TRACK_TITLE_MAX];
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if (!data) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_track_title) {
|
||||||
mcc_cb->read_track_title(conn, cb_err, title);
|
mcc_cb->read_track_title(conn, cb_err, title);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return BT_GATT_ITER_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t mcc_read_track_duration_cb(struct bt_conn *conn, uint8_t err,
|
static void mcc_track_duration_cb(struct bt_conn *conn, uint8_t err, const void *data,
|
||||||
struct bt_gatt_read_params *params,
|
uint16_t length)
|
||||||
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;
|
int cb_err = err;
|
||||||
int32_t dur = 0;
|
int32_t dur = 0;
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if ((!data) || (length != sizeof(dur))) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_track_duration) {
|
||||||
mcc_cb->read_track_duration(conn, cb_err, dur);
|
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,
|
struct bt_gatt_read_params *params,
|
||||||
const void *data, uint16_t length)
|
const void *data, uint16_t length)
|
||||||
{
|
{
|
||||||
struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
|
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;
|
int cb_err = err;
|
||||||
int32_t pos = 0;
|
int32_t pos = 0;
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if ((!data) || (length != sizeof(pos))) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_track_position) {
|
||||||
mcc_cb->read_track_position(conn, cb_err, pos);
|
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;
|
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,
|
static void mcc_playback_speed_cb(struct bt_conn *conn, uint8_t err, const void *data,
|
||||||
struct bt_gatt_read_params *params,
|
uint16_t length)
|
||||||
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;
|
int cb_err = err;
|
||||||
int8_t speed = 0;
|
int8_t speed = 0;
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if ((!data) || (length != sizeof(speed))) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_playback_speed) {
|
||||||
mcc_cb->read_playback_speed(conn, cb_err, 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;
|
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,
|
static void mcc_seeking_speed_cb(struct bt_conn *conn, uint8_t err, const void *data,
|
||||||
struct bt_gatt_read_params *params,
|
uint16_t length)
|
||||||
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;
|
int cb_err = err;
|
||||||
int8_t speed = 0;
|
int8_t speed = 0;
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if ((!data) || (length != sizeof(speed))) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_seeking_speed) {
|
||||||
mcc_cb->read_seeking_speed(conn, cb_err, 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;
|
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;
|
return BT_GATT_ITER_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t mcc_read_current_track_obj_id_cb(struct bt_conn *conn, uint8_t err,
|
static void mcc_current_track_obj_id_cb(struct bt_conn *conn, uint8_t err, const void *data,
|
||||||
struct bt_gatt_read_params *params,
|
uint16_t length)
|
||||||
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;
|
int cb_err = err;
|
||||||
uint8_t *pid = (uint8_t *)data;
|
uint8_t *pid = (uint8_t *)data;
|
||||||
uint64_t id = 0;
|
uint64_t id = 0;
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if ((!pid) || (length != BT_OTS_OBJ_ID_SIZE)) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_current_track_obj_id) {
|
||||||
mcc_cb->read_current_track_obj_id(conn, cb_err, 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;
|
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,
|
static void mcc_next_track_obj_id_cb(struct bt_conn *conn, uint8_t err, const void *data,
|
||||||
struct bt_gatt_read_params *params,
|
uint16_t length)
|
||||||
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;
|
int cb_err = err;
|
||||||
uint8_t *pid = (uint8_t *)data;
|
uint8_t *pid = (uint8_t *)data;
|
||||||
uint64_t id = 0;
|
uint64_t id = 0;
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if (length == 0) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_next_track_obj_id) {
|
||||||
mcc_cb->read_next_track_obj_id(conn, cb_err, 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;
|
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,
|
static void mcc_parent_group_obj_id_cb(struct bt_conn *conn, uint8_t err, const void *data,
|
||||||
struct bt_gatt_read_params *params,
|
uint16_t length)
|
||||||
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;
|
int cb_err = err;
|
||||||
uint8_t *pid = (uint8_t *)data;
|
uint8_t *pid = (uint8_t *)data;
|
||||||
uint64_t id = 0;
|
uint64_t id = 0;
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if (!pid || (length != BT_OTS_OBJ_ID_SIZE)) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_parent_group_obj_id) {
|
||||||
mcc_cb->read_parent_group_obj_id(conn, cb_err, id);
|
mcc_cb->read_parent_group_obj_id(conn, cb_err, id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return BT_GATT_ITER_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t mcc_read_current_group_obj_id_cb(struct bt_conn *conn, uint8_t err,
|
static void mcc_current_group_obj_id_cb(struct bt_conn *conn, uint8_t err, const void *data,
|
||||||
struct bt_gatt_read_params *params,
|
uint16_t length)
|
||||||
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;
|
int cb_err = err;
|
||||||
uint8_t *pid = (uint8_t *)data;
|
uint8_t *pid = (uint8_t *)data;
|
||||||
uint64_t id = 0;
|
uint64_t id = 0;
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if (!pid || (length != BT_OTS_OBJ_ID_SIZE)) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_current_group_obj_id) {
|
||||||
mcc_cb->read_current_group_obj_id(conn, cb_err, 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;
|
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 */
|
#endif /* CONFIG_BT_MCC_OTS */
|
||||||
|
|
||||||
static uint8_t mcc_read_playing_order_cb(struct bt_conn *conn, uint8_t err,
|
static void mcc_playing_order_cb(struct bt_conn *conn, uint8_t err, const void *data,
|
||||||
struct bt_gatt_read_params *params,
|
uint16_t length)
|
||||||
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;
|
int cb_err = err;
|
||||||
uint8_t order = 0;
|
uint8_t order = 0;
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if ((!data) || (length != sizeof(order))) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_playing_order) {
|
||||||
mcc_cb->read_playing_order(conn, cb_err, 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;
|
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;
|
return BT_GATT_ITER_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t mcc_read_media_state_cb(struct bt_conn *conn, uint8_t err,
|
static void mcc_media_state_cb(struct bt_conn *conn, uint8_t err, const void *data, uint16_t length)
|
||||||
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);
|
|
||||||
int cb_err = err;
|
int cb_err = err;
|
||||||
uint8_t state = 0;
|
uint8_t state = 0;
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if (!data || length != sizeof(state)) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_media_state) {
|
||||||
mcc_cb->read_media_state(conn, cb_err, 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;
|
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,
|
static void mcc_opcodes_supported_cb(struct bt_conn *conn, uint8_t err, const void *data,
|
||||||
struct bt_gatt_read_params *params,
|
uint16_t length)
|
||||||
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;
|
int cb_err = err;
|
||||||
int32_t operations = 0;
|
int32_t operations = 0;
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if ((!data) || (length != sizeof(operations))) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_opcodes_supported) {
|
||||||
mcc_cb->read_opcodes_supported(conn, cb_err, operations);
|
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;
|
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,
|
static void mcc_search_results_obj_id_cb(struct bt_conn *conn, uint8_t err,
|
||||||
struct bt_gatt_read_params *params,
|
const void *data, uint16_t length)
|
||||||
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;
|
int cb_err = err;
|
||||||
uint8_t *pid = (uint8_t *)data;
|
uint8_t *pid = (uint8_t *)data;
|
||||||
uint64_t id = 0;
|
uint64_t id = 0;
|
||||||
|
|
||||||
mcs_inst->busy = false;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("err: 0x%02x", err);
|
LOG_DBG("err: 0x%02x", err);
|
||||||
} else if (length == 0) {
|
} 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) {
|
if (mcc_cb && mcc_cb->read_search_results_obj_id) {
|
||||||
mcc_cb->read_search_results_obj_id(conn, cb_err, 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;
|
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,
|
struct bt_gatt_subscribe_params *params,
|
||||||
const void *data, uint16_t length)
|
const void *data, uint16_t length)
|
||||||
{
|
{
|
||||||
struct bt_gatt_read_params *read_params;
|
|
||||||
uint16_t handle = params->value_handle;
|
uint16_t handle = params->value_handle;
|
||||||
struct mcs_instance_t *mcs_inst;
|
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;
|
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);
|
LOG_DBG("Notification, handle: %d", handle);
|
||||||
|
|
||||||
if (handle == mcs_inst->player_name_handle) {
|
if (handle == mcs_inst->player_name_handle) {
|
||||||
LOG_DBG("Player Name notification");
|
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) {
|
} else if (handle == mcs_inst->track_changed_handle) {
|
||||||
/* The Track Changed characteristic can only be */
|
/* 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) {
|
} else if (handle == mcs_inst->track_title_handle) {
|
||||||
LOG_DBG("Track Title notification");
|
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) {
|
} else if (handle == mcs_inst->track_duration_handle) {
|
||||||
LOG_DBG("Track Duration notification");
|
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) {
|
} else if (handle == mcs_inst->track_position_handle) {
|
||||||
LOG_DBG("Track Position notification");
|
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) {
|
} else if (handle == mcs_inst->playback_speed_handle) {
|
||||||
LOG_DBG("Playback Speed notification");
|
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) {
|
} else if (handle == mcs_inst->seeking_speed_handle) {
|
||||||
LOG_DBG("Seeking Speed notification");
|
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
|
#ifdef CONFIG_BT_MCC_OTS
|
||||||
} else if (handle == mcs_inst->current_track_obj_id_handle) {
|
} else if (handle == mcs_inst->current_track_obj_id_handle) {
|
||||||
LOG_DBG("Current Track notification");
|
LOG_DBG("Current Track notification");
|
||||||
mcc_read_current_track_obj_id_cb(conn, 0, read_params, data,
|
mcc_current_track_obj_id_cb(conn, 0, data, length);
|
||||||
length);
|
|
||||||
|
|
||||||
} else if (handle == mcs_inst->next_track_obj_id_handle) {
|
} else if (handle == mcs_inst->next_track_obj_id_handle) {
|
||||||
LOG_DBG("Next Track notification");
|
LOG_DBG("Next Track notification");
|
||||||
mcc_read_next_track_obj_id_cb(conn, 0, read_params, data,
|
mcc_next_track_obj_id_cb(conn, 0, data, length);
|
||||||
length);
|
|
||||||
|
|
||||||
} else if (handle == mcs_inst->parent_group_obj_id_handle) {
|
} else if (handle == mcs_inst->parent_group_obj_id_handle) {
|
||||||
LOG_DBG("Parent Group notification");
|
LOG_DBG("Parent Group notification");
|
||||||
mcc_read_parent_group_obj_id_cb(conn, 0, read_params, data,
|
mcc_parent_group_obj_id_cb(conn, 0, data, length);
|
||||||
length);
|
|
||||||
|
|
||||||
} else if (handle == mcs_inst->current_group_obj_id_handle) {
|
} else if (handle == mcs_inst->current_group_obj_id_handle) {
|
||||||
LOG_DBG("Current Group notification");
|
LOG_DBG("Current Group notification");
|
||||||
mcc_read_current_group_obj_id_cb(conn, 0, read_params, data,
|
mcc_current_group_obj_id_cb(conn, 0, data, length);
|
||||||
length);
|
|
||||||
#endif /* CONFIG_BT_MCC_OTS */
|
#endif /* CONFIG_BT_MCC_OTS */
|
||||||
|
|
||||||
} else if (handle == mcs_inst->playing_order_handle) {
|
} else if (handle == mcs_inst->playing_order_handle) {
|
||||||
LOG_DBG("Playing Order notification");
|
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) {
|
} else if (handle == mcs_inst->media_state_handle) {
|
||||||
LOG_DBG("Media State notification");
|
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) {
|
} else if (handle == mcs_inst->cp_handle) {
|
||||||
/* The control point is is a special case - only */
|
/* 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) {
|
} else if (handle == mcs_inst->opcodes_supported_handle) {
|
||||||
LOG_DBG("Opcodes Supported notification");
|
LOG_DBG("Opcodes Supported notification");
|
||||||
mcc_read_opcodes_supported_cb(conn, 0, read_params, data,
|
mcc_opcodes_supported_cb(conn, 0, data, length);
|
||||||
length);
|
|
||||||
|
|
||||||
#ifdef CONFIG_BT_MCC_OTS
|
#ifdef CONFIG_BT_MCC_OTS
|
||||||
} else if (handle == mcs_inst->scp_handle) {
|
} 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) {
|
} else if (handle == mcs_inst->search_results_obj_id_handle) {
|
||||||
LOG_DBG("Search Results notification");
|
LOG_DBG("Search Results notification");
|
||||||
mcc_read_search_results_obj_id_cb(conn, 0, read_params, data,
|
mcc_search_results_obj_id_cb(conn, 0, data, length);
|
||||||
length);
|
|
||||||
#endif /* CONFIG_BT_MCC_OTS */
|
#endif /* CONFIG_BT_MCC_OTS */
|
||||||
} else {
|
} else {
|
||||||
LOG_DBG("Unknown handle: %d (0x%04X)", handle, handle);
|
LOG_DBG("Unknown handle: %d (0x%04X)", handle, handle);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <zephyr/init.h>
|
#include <zephyr/init.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <zephyr/types.h>
|
#include <zephyr/types.h>
|
||||||
|
#include <zephyr/sys/atomic.h>
|
||||||
#include <zephyr/sys/util.h>
|
#include <zephyr/sys/util.h>
|
||||||
|
|
||||||
#include <zephyr/bluetooth/bluetooth.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;
|
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 {
|
static struct client_state {
|
||||||
bool player_name_changed;
|
ATOMIC_DEFINE(flags, FLAG_NUM);
|
||||||
bool icon_url_changed;
|
struct mpl_cmd_ntf cmd_ntf;
|
||||||
bool track_title_changed;
|
#if defined(CONFIG_BT_OTS)
|
||||||
|
uint8_t search_control_point_result;
|
||||||
|
#endif /* CONFIG_BT_OTS */
|
||||||
} clients[CONFIG_BT_MAX_CONN];
|
} clients[CONFIG_BT_MAX_CONN];
|
||||||
|
|
||||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
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);
|
LOG_DBG("Player name read: %s (offset %u)", name, offset);
|
||||||
|
|
||||||
if (offset == 0) {
|
if (offset == 0) {
|
||||||
client->player_name_changed = false;
|
atomic_clear_bit(client->flags, FLAG_PLAYER_NAME_CHANGED);
|
||||||
} else if (client->player_name_changed) {
|
} else if (atomic_test_bit(client->flags, FLAG_PLAYER_NAME_CHANGED)) {
|
||||||
return BT_GATT_ERR(BT_MCS_ERR_LONG_VAL_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);
|
LOG_DBG("Icon URL read, offset: %d, len:%d, URL: %s", offset, len, url);
|
||||||
|
|
||||||
if (offset == 0) {
|
if (offset == 0) {
|
||||||
client->icon_url_changed = false;
|
atomic_clear_bit(client->flags, FLAG_ICON_URL_CHANGED);
|
||||||
} else if (client->icon_url_changed) {
|
} else if (atomic_test_bit(client->flags, FLAG_ICON_URL_CHANGED)) {
|
||||||
return BT_GATT_ERR(BT_MCS_ERR_LONG_VAL_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);
|
LOG_DBG("Track title read, offset: %d, len:%d, title: %s", offset, len, title);
|
||||||
|
|
||||||
if (offset == 0) {
|
if (offset == 0) {
|
||||||
client->track_title_changed = false;
|
atomic_clear_bit(client->flags, FLAG_TRACK_TITLE_CHANGED);
|
||||||
} else if (client->track_title_changed) {
|
} else if (atomic_test_bit(client->flags, FLAG_TRACK_TITLE_CHANGED)) {
|
||||||
return BT_GATT_ERR(BT_MCS_ERR_LONG_VAL_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,
|
const struct bt_gatt_attr *attr, void *buf,
|
||||||
uint16_t len, uint16_t offset)
|
uint16_t len, uint16_t offset)
|
||||||
{
|
{
|
||||||
|
struct client_state *client = &clients[bt_conn_index(conn)];
|
||||||
int32_t duration = media_proxy_sctrl_get_track_duration();
|
int32_t duration = media_proxy_sctrl_get_track_duration();
|
||||||
|
|
||||||
LOG_DBG("Track duration read: %d (0x%08x)", duration, 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,
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &duration,
|
||||||
sizeof(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,
|
const struct bt_gatt_attr *attr, void *buf,
|
||||||
uint16_t len, uint16_t offset)
|
uint16_t len, uint16_t offset)
|
||||||
{
|
{
|
||||||
|
struct client_state *client = &clients[bt_conn_index(conn)];
|
||||||
int32_t position = media_proxy_sctrl_get_track_position();
|
int32_t position = media_proxy_sctrl_get_track_position();
|
||||||
|
|
||||||
LOG_DBG("Track position read: %d (0x%08x)", position, 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,
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &position,
|
||||||
sizeof(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,
|
const struct bt_gatt_attr *attr, void *buf,
|
||||||
uint16_t len, uint16_t offset)
|
uint16_t len, uint16_t offset)
|
||||||
{
|
{
|
||||||
|
struct client_state *client = &clients[bt_conn_index(conn)];
|
||||||
int8_t speed = media_proxy_sctrl_get_playback_speed();
|
int8_t speed = media_proxy_sctrl_get_playback_speed();
|
||||||
|
|
||||||
LOG_DBG("Playback speed read: %d", 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,
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &speed,
|
||||||
sizeof(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,
|
const struct bt_gatt_attr *attr, void *buf,
|
||||||
uint16_t len, uint16_t offset)
|
uint16_t len, uint16_t offset)
|
||||||
{
|
{
|
||||||
|
struct client_state *client = &clients[bt_conn_index(conn)];
|
||||||
int8_t speed = media_proxy_sctrl_get_seeking_speed();
|
int8_t speed = media_proxy_sctrl_get_seeking_speed();
|
||||||
|
|
||||||
LOG_DBG("Seeking speed read: %d", 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,
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &speed,
|
||||||
sizeof(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,
|
const struct bt_gatt_attr *attr, void *buf,
|
||||||
uint16_t len, uint16_t offset)
|
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();
|
uint64_t track_id = media_proxy_sctrl_get_current_track_id();
|
||||||
|
|
||||||
LOG_DBG_OBJ_ID("Current track ID read: ", 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,
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &track_id,
|
||||||
BT_OTS_OBJ_ID_SIZE);
|
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,
|
const struct bt_gatt_attr *attr, void *buf,
|
||||||
uint16_t len, uint16_t offset)
|
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();
|
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) {
|
if (track_id == MPL_NO_TRACK_ID) {
|
||||||
LOG_DBG("Next track read, but it is empty");
|
LOG_DBG("Next track read, but it is empty");
|
||||||
/* "If the media player has no next track, the length of the */
|
/* "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,
|
const struct bt_gatt_attr *attr, void *buf,
|
||||||
uint16_t len, uint16_t offset)
|
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();
|
uint64_t group_id = media_proxy_sctrl_get_parent_group_id();
|
||||||
|
|
||||||
LOG_DBG_OBJ_ID("Parent group read: ", 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,
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &group_id,
|
||||||
BT_OTS_OBJ_ID_SIZE);
|
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,
|
const struct bt_gatt_attr *attr, void *buf,
|
||||||
uint16_t len, uint16_t offset)
|
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();
|
uint64_t group_id = media_proxy_sctrl_get_current_group_id();
|
||||||
|
|
||||||
LOG_DBG_OBJ_ID("Current group read: ", 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,
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &group_id,
|
||||||
BT_OTS_OBJ_ID_SIZE);
|
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,
|
const struct bt_gatt_attr *attr, void *buf,
|
||||||
uint16_t len, uint16_t offset)
|
uint16_t len, uint16_t offset)
|
||||||
{
|
{
|
||||||
|
struct client_state *client = &clients[bt_conn_index(conn)];
|
||||||
uint8_t order = media_proxy_sctrl_get_playing_order();
|
uint8_t order = media_proxy_sctrl_get_playing_order();
|
||||||
|
|
||||||
LOG_DBG("Playing order read: %d (0x%02x)", order, 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,
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &order,
|
||||||
sizeof(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,
|
const struct bt_gatt_attr *attr, void *buf,
|
||||||
uint16_t len, uint16_t offset)
|
uint16_t len, uint16_t offset)
|
||||||
{
|
{
|
||||||
|
struct client_state *client = &clients[bt_conn_index(conn)];
|
||||||
uint8_t state = media_proxy_sctrl_get_media_state();
|
uint8_t state = media_proxy_sctrl_get_media_state();
|
||||||
|
|
||||||
LOG_DBG("Media state read: %d", 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,
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &state,
|
||||||
sizeof(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,
|
const void *buf, uint16_t len, uint16_t offset,
|
||||||
uint8_t flags)
|
uint8_t flags)
|
||||||
{
|
{
|
||||||
|
struct client_state *client = &clients[bt_conn_index(conn)];
|
||||||
struct mpl_cmd command;
|
struct mpl_cmd command;
|
||||||
|
|
||||||
if (offset != 0) {
|
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));
|
notify(BT_UUID_MCS_MEDIA_CONTROL_POINT, &cmd_ntf, sizeof(cmd_ntf));
|
||||||
|
|
||||||
return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
|
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)) {
|
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,
|
const struct bt_gatt_attr *attr,
|
||||||
void *buf, uint16_t len, uint16_t offset)
|
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();
|
uint32_t opcodes = media_proxy_sctrl_get_commands_supported();
|
||||||
|
|
||||||
LOG_DBG("Opcodes_supported read: %d (0x%08x)", opcodes, opcodes);
|
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,
|
return bt_gatt_attr_read(conn, attr, buf, len, offset,
|
||||||
&opcodes, BT_MCS_OPCODES_SUPPORTED_LEN);
|
&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,
|
const void *buf, uint16_t len,
|
||||||
uint16_t offset, uint8_t flags)
|
uint16_t offset, uint8_t flags)
|
||||||
{
|
{
|
||||||
|
struct client_state *client = &clients[bt_conn_index(conn)];
|
||||||
struct mpl_search search = {0};
|
struct mpl_search search = {0};
|
||||||
|
|
||||||
if (offset != 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);
|
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);
|
memcpy(&search.search, (char *)buf, len);
|
||||||
search.len = len;
|
search.len = len;
|
||||||
LOG_DBG("Search length: %d", 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,
|
const struct bt_gatt_attr *attr,
|
||||||
void *buf, uint16_t len, uint16_t offset)
|
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();
|
uint64_t search_id = media_proxy_sctrl_get_search_results_id();
|
||||||
|
|
||||||
LOG_DBG_OBJ_ID("Search results id read: ", search_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 */
|
/* TODO: The permanent solution here should be that the call to */
|
||||||
/* mpl should fill the UUID in a pointed-to value, and return a */
|
/* 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 */
|
/* 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 {
|
static void notify_string(struct bt_conn *conn, const struct bt_uuid *uuid, const char *str)
|
||||||
const struct bt_uuid *uuid;
|
|
||||||
const char *str;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void notify_string_conn_cb(struct bt_conn *conn, void *data)
|
|
||||||
{
|
{
|
||||||
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 */
|
const uint8_t att_header_size = 3; /* opcode + handle */
|
||||||
struct bt_conn_info info;
|
|
||||||
uint16_t att_mtu;
|
uint16_t att_mtu;
|
||||||
uint16_t maxlen;
|
uint16_t maxlen;
|
||||||
int err;
|
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);
|
att_mtu = bt_gatt_get_mtu(conn);
|
||||||
__ASSERT(att_mtu > att_header_size, "Could not get valid ATT MTU");
|
__ASSERT(att_mtu > att_header_size, "Could not get valid ATT MTU");
|
||||||
maxlen = att_mtu - att_header_size; /* Subtract opcode and handle */
|
maxlen = att_mtu - att_header_size; /* Subtract opcode and handle */
|
||||||
|
|
||||||
/* Send notifcation potentially truncated to the MTU */
|
/* Send notifcation potentially truncated to the MTU */
|
||||||
err = bt_gatt_notify_uuid(conn, ntf->uuid, mcs.attrs, (void *)ntf->str,
|
err = bt_gatt_notify_uuid(conn, uuid, mcs.attrs, (void *)str,
|
||||||
MIN(strlen(ntf->str), maxlen));
|
MIN(strlen(str), maxlen));
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
LOG_ERR("Notification error: %d", err);
|
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)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
client->icon_url_changed = true;
|
atomic_set_bit(client->flags, FLAG_ICON_URL_CHANGED);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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_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(conn, BT_UUID_MCS_TRACK_TITLE, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_DURATION_CHANGED)) {
|
||||||
|
int32_t duration = media_proxy_sctrl_get_track_duration();
|
||||||
|
|
||||||
|
LOG_DBG("Notifying track duration: %d", duration);
|
||||||
|
notify(BT_UUID_MCS_TRACK_DURATION, &duration, sizeof(duration));
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
void media_proxy_sctrl_icon_url_cb(const char *name)
|
||||||
{
|
{
|
||||||
|
|
||||||
bt_conn_foreach(BT_CONN_TYPE_LE, mark_icon_url_changed_cb, NULL);
|
bt_conn_foreach(BT_CONN_TYPE_LE, mark_icon_url_changed_cb, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void media_proxy_sctrl_track_changed_cb(void)
|
void media_proxy_sctrl_track_changed_cb(void)
|
||||||
{
|
{
|
||||||
LOG_DBG("Notifying track change");
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify(BT_UUID_MCS_TRACK_CHANGED, NULL, 0);
|
UINT_TO_POINTER(FLAG_TRACK_CHANGED));
|
||||||
}
|
}
|
||||||
|
|
||||||
void media_proxy_sctrl_track_title_cb(const char *title)
|
void media_proxy_sctrl_track_title_cb(const char *title)
|
||||||
{
|
{
|
||||||
LOG_DBG("Notifying track title: %s", title);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify_string(BT_UUID_MCS_TRACK_TITLE, title);
|
UINT_TO_POINTER(FLAG_TRACK_TITLE_CHANGED));
|
||||||
}
|
}
|
||||||
|
|
||||||
void media_proxy_sctrl_track_position_cb(int32_t position)
|
void media_proxy_sctrl_track_position_cb(int32_t position)
|
||||||
{
|
{
|
||||||
LOG_DBG("Notifying track position: %d", position);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify(BT_UUID_MCS_TRACK_POSITION, &position, sizeof(position));
|
UINT_TO_POINTER(FLAG_TRACK_POSITION_CHANGED));
|
||||||
}
|
}
|
||||||
|
|
||||||
void media_proxy_sctrl_track_duration_cb(int32_t duration)
|
void media_proxy_sctrl_track_duration_cb(int32_t duration)
|
||||||
{
|
{
|
||||||
LOG_DBG("Notifying track duration: %d", duration);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify(BT_UUID_MCS_TRACK_DURATION, &duration, sizeof(duration));
|
UINT_TO_POINTER(FLAG_TRACK_DURATION_CHANGED));
|
||||||
}
|
}
|
||||||
|
|
||||||
void media_proxy_sctrl_playback_speed_cb(int8_t speed)
|
void media_proxy_sctrl_playback_speed_cb(int8_t speed)
|
||||||
{
|
{
|
||||||
LOG_DBG("Notifying playback speed: %d", speed);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify(BT_UUID_MCS_PLAYBACK_SPEED, &speed, sizeof(speed));
|
UINT_TO_POINTER(FLAG_PLAYBACK_SPEED_CHANGED));
|
||||||
}
|
}
|
||||||
|
|
||||||
void media_proxy_sctrl_seeking_speed_cb(int8_t speed)
|
void media_proxy_sctrl_seeking_speed_cb(int8_t speed)
|
||||||
{
|
{
|
||||||
LOG_DBG("Notifying seeking speed: %d", speed);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify(BT_UUID_MCS_SEEKING_SPEED, &speed, sizeof(speed));
|
UINT_TO_POINTER(FLAG_SEEKING_SPEED_CHANGED));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_BT_OTS)
|
#if defined(CONFIG_BT_OTS)
|
||||||
void media_proxy_sctrl_current_track_id_cb(uint64_t id)
|
void media_proxy_sctrl_current_track_id_cb(uint64_t id)
|
||||||
{
|
{
|
||||||
LOG_DBG_OBJ_ID("Notifying current track ID: ", id);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify(BT_UUID_MCS_CURRENT_TRACK_OBJ_ID, &id, BT_OTS_OBJ_ID_SIZE);
|
UINT_TO_POINTER(FLAG_CURRENT_TRACK_OBJ_ID_CHANGED));
|
||||||
}
|
}
|
||||||
|
|
||||||
void media_proxy_sctrl_next_track_id_cb(uint64_t id)
|
void media_proxy_sctrl_next_track_id_cb(uint64_t id)
|
||||||
{
|
{
|
||||||
if (id == MPL_NO_TRACK_ID) {
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
/* "If the media player has no next track, the length of the */
|
UINT_TO_POINTER(FLAG_NEXT_TRACK_OBJ_ID_CHANGED));
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void media_proxy_sctrl_parent_group_id_cb(uint64_t id)
|
void media_proxy_sctrl_parent_group_id_cb(uint64_t id)
|
||||||
{
|
{
|
||||||
LOG_DBG_OBJ_ID("Notifying parent group ID: ", id);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify(BT_UUID_MCS_PARENT_GROUP_OBJ_ID, &id, BT_OTS_OBJ_ID_SIZE);
|
UINT_TO_POINTER(FLAG_PARENT_GROUP_OBJ_ID_CHANGED));
|
||||||
}
|
}
|
||||||
|
|
||||||
void media_proxy_sctrl_current_group_id_cb(uint64_t id)
|
void media_proxy_sctrl_current_group_id_cb(uint64_t id)
|
||||||
{
|
{
|
||||||
LOG_DBG_OBJ_ID("Notifying current group ID: ", id);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify(BT_UUID_MCS_CURRENT_GROUP_OBJ_ID, &id, BT_OTS_OBJ_ID_SIZE);
|
UINT_TO_POINTER(FLAG_CURRENT_GROUP_OBJ_ID_CHANGED));
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BT_OTS */
|
#endif /* CONFIG_BT_OTS */
|
||||||
|
|
||||||
void media_proxy_sctrl_playing_order_cb(uint8_t order)
|
void media_proxy_sctrl_playing_order_cb(uint8_t order)
|
||||||
{
|
{
|
||||||
LOG_DBG("Notifying playing order: %d", order);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify(BT_UUID_MCS_PLAYING_ORDER, &order, sizeof(order));
|
UINT_TO_POINTER(FLAG_PLAYING_ORDER_CHANGED));
|
||||||
}
|
}
|
||||||
|
|
||||||
void media_proxy_sctrl_media_state_cb(uint8_t state)
|
void media_proxy_sctrl_media_state_cb(uint8_t state)
|
||||||
{
|
{
|
||||||
LOG_DBG("Notifying media state: %d", state);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify(BT_UUID_MCS_MEDIA_STATE, &state, sizeof(state));
|
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)
|
void media_proxy_sctrl_command_cb(const struct mpl_cmd_ntf *cmd_ntf)
|
||||||
{
|
{
|
||||||
LOG_DBG("Notifying control point command - opcode: %d, result: %d",
|
/* FIXME: Control Point notification shall be sent to operation initiator only */
|
||||||
cmd_ntf->requested_opcode, cmd_ntf->result_code);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_media_control_point_ntf, (void *)cmd_ntf);
|
||||||
notify(BT_UUID_MCS_MEDIA_CONTROL_POINT, cmd_ntf, sizeof(*cmd_ntf));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void media_proxy_sctrl_commands_supported_cb(uint32_t opcodes)
|
void media_proxy_sctrl_commands_supported_cb(uint32_t opcodes)
|
||||||
{
|
{
|
||||||
LOG_DBG("Notifying command opcodes supported: %d (0x%08x)", opcodes, opcodes);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify(BT_UUID_MCS_MEDIA_CONTROL_OPCODES, &opcodes,
|
UINT_TO_POINTER(FLAG_MEDIA_CONTROL_OPCODES_CHANGED));
|
||||||
BT_MCS_OPCODES_SUPPORTED_LEN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_BT_OTS)
|
#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)
|
void media_proxy_sctrl_search_cb(uint8_t result_code)
|
||||||
{
|
{
|
||||||
LOG_DBG("Notifying search control point - result: %d", result_code);
|
/* FIXME: Control Point notification shall be sent to operation initiator only */
|
||||||
notify(BT_UUID_MCS_SEARCH_CONTROL_POINT, &result_code,
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_search_control_point_ntf,
|
||||||
sizeof(result_code));
|
UINT_TO_POINTER(result_code));
|
||||||
}
|
}
|
||||||
|
|
||||||
void media_proxy_sctrl_search_results_id_cb(uint64_t id)
|
void media_proxy_sctrl_search_results_id_cb(uint64_t id)
|
||||||
{
|
{
|
||||||
LOG_DBG_OBJ_ID("Notifying search results ID: ", id);
|
bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
|
||||||
notify(BT_UUID_MCS_SEARCH_RESULTS_OBJ_ID, &id, BT_OTS_OBJ_ID_SIZE);
|
UINT_TO_POINTER(FLAG_SEARCH_RESULTS_OBJ_ID_CHANGED));
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BT_OTS */
|
#endif /* CONFIG_BT_OTS */
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue