Bluetooth: has: Add Preset Changed operation support
This extends implementation with sending Preset Changed notification/indication when preset changes its availability or is added or deleted. Signed-off-by: Mariusz Skamra <mariusz.skamra@codecoup.pl>
This commit is contained in:
parent
a5d6aafdb2
commit
2314aa2977
3 changed files with 165 additions and 1 deletions
|
@ -181,6 +181,30 @@ int bt_has_preset_register(const struct bt_has_preset_register_param *param);
|
||||||
*/
|
*/
|
||||||
int bt_has_preset_unregister(uint8_t index);
|
int bt_has_preset_unregister(uint8_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the preset as available.
|
||||||
|
*
|
||||||
|
* Set the @ref BT_HAS_PROP_AVAILABLE property bit. This will notify preset availability
|
||||||
|
* to peer devices. Only available preset can be selected as active preset.
|
||||||
|
*
|
||||||
|
* @param index The index of preset that's became available.
|
||||||
|
*
|
||||||
|
* @return 0 in case of success or negative value in case of error.
|
||||||
|
*/
|
||||||
|
int bt_has_preset_available(uint8_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the preset as unavailable.
|
||||||
|
*
|
||||||
|
* Clear the @ref BT_HAS_PROP_AVAILABLE property bit. This will notify preset availability
|
||||||
|
* to peer devices. Unavailable preset cannot be selected as active preset.
|
||||||
|
*
|
||||||
|
* @param index The index of preset that's became unavailable.
|
||||||
|
*
|
||||||
|
* @return 0 in case of success or negative value in case of error.
|
||||||
|
*/
|
||||||
|
int bt_has_preset_unavailable(uint8_t index);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
BT_HAS_PRESET_ITER_STOP = 0,
|
BT_HAS_PRESET_ITER_STOP = 0,
|
||||||
BT_HAS_PRESET_ITER_CONTINUE,
|
BT_HAS_PRESET_ITER_CONTINUE,
|
||||||
|
|
|
@ -326,6 +326,33 @@ static int control_point_send(struct has_client *client, struct net_buf_simple *
|
||||||
return -ECANCELED;
|
return -ECANCELED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int control_point_send_all(struct net_buf_simple *buf)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(has_client_list); i++) {
|
||||||
|
struct has_client *client = &has_client_list[i];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!client->conn) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bt_gatt_is_subscribed(client->conn, PRESET_CONTROL_POINT_ATTR,
|
||||||
|
BT_GATT_CCC_NOTIFY | BT_GATT_CCC_INDICATE)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = control_point_send(client, buf);
|
||||||
|
if (err) {
|
||||||
|
result = err;
|
||||||
|
/* continue anyway */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static int bt_has_cp_read_preset_rsp(struct has_client *client, const struct has_preset *preset,
|
static int bt_has_cp_read_preset_rsp(struct has_client *client, const struct has_preset *preset,
|
||||||
bool is_last)
|
bool is_last)
|
||||||
{
|
{
|
||||||
|
@ -393,6 +420,54 @@ static void process_control_point_work(struct k_work *work)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t get_prev_preset_index(struct has_preset *preset)
|
||||||
|
{
|
||||||
|
const struct has_preset *prev = NULL;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(has_preset_list); i++) {
|
||||||
|
const struct has_preset *tmp = &has_preset_list[i];
|
||||||
|
|
||||||
|
if (tmp->index == BT_HAS_PRESET_INDEX_NONE || tmp == preset) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev ? prev->index : BT_HAS_PRESET_INDEX_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void preset_changed_prepare(struct net_buf_simple *buf, uint8_t change_id, uint8_t is_last)
|
||||||
|
{
|
||||||
|
struct bt_has_cp_hdr *hdr;
|
||||||
|
struct bt_has_cp_preset_changed *preset_changed;
|
||||||
|
|
||||||
|
hdr = net_buf_simple_add(buf, sizeof(*hdr));
|
||||||
|
hdr->opcode = BT_HAS_OP_PRESET_CHANGED;
|
||||||
|
preset_changed = net_buf_simple_add(buf, sizeof(*preset_changed));
|
||||||
|
preset_changed->change_id = change_id;
|
||||||
|
preset_changed->is_last = is_last;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bt_has_cp_generic_update(struct has_preset *preset, uint8_t is_last)
|
||||||
|
{
|
||||||
|
struct bt_has_cp_generic_update *generic_update;
|
||||||
|
|
||||||
|
NET_BUF_SIMPLE_DEFINE(buf, sizeof(struct bt_has_cp_hdr) +
|
||||||
|
sizeof(struct bt_has_cp_preset_changed) +
|
||||||
|
sizeof(struct bt_has_cp_generic_update) + BT_HAS_PRESET_NAME_MAX);
|
||||||
|
|
||||||
|
preset_changed_prepare(&buf, BT_HAS_CHANGE_ID_GENERIC_UPDATE, is_last);
|
||||||
|
|
||||||
|
generic_update = net_buf_simple_add(&buf, sizeof(*generic_update));
|
||||||
|
generic_update->prev_index = get_prev_preset_index(preset);
|
||||||
|
generic_update->index = preset->index;
|
||||||
|
generic_update->properties = preset->properties;
|
||||||
|
net_buf_simple_add_mem(&buf, preset->name, strlen(preset->name));
|
||||||
|
|
||||||
|
return control_point_send_all(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
static uint8_t handle_read_preset_req(struct bt_conn *conn, struct net_buf_simple *buf)
|
static uint8_t handle_read_preset_req(struct bt_conn *conn, struct net_buf_simple *buf)
|
||||||
{
|
{
|
||||||
const struct bt_has_cp_read_presets_req *req;
|
const struct bt_has_cp_read_presets_req *req;
|
||||||
|
@ -524,13 +599,16 @@ int bt_has_preset_register(const struct bt_has_preset_register_param *param)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return bt_has_cp_generic_update(preset, BT_HAS_IS_LAST);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bt_has_preset_unregister(uint8_t index)
|
int bt_has_preset_unregister(uint8_t index)
|
||||||
{
|
{
|
||||||
struct has_preset *preset = NULL;
|
struct has_preset *preset = NULL;
|
||||||
|
|
||||||
|
NET_BUF_SIMPLE_DEFINE(buf, sizeof(struct bt_has_cp_hdr) +
|
||||||
|
sizeof(struct bt_has_cp_preset_changed) + sizeof(uint8_t));
|
||||||
|
|
||||||
CHECKIF(index == BT_HAS_PRESET_INDEX_NONE) {
|
CHECKIF(index == BT_HAS_PRESET_INDEX_NONE) {
|
||||||
BT_ERR("index is invalid");
|
BT_ERR("index is invalid");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -541,8 +619,61 @@ int bt_has_preset_unregister(uint8_t index)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preset_changed_prepare(&buf, BT_HAS_CHANGE_ID_PRESET_DELETED, BT_HAS_IS_LAST);
|
||||||
|
net_buf_simple_add_u8(&buf, preset->index);
|
||||||
|
|
||||||
preset_free(preset);
|
preset_free(preset);
|
||||||
|
|
||||||
|
return control_point_send_all(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_has_preset_available(uint8_t index)
|
||||||
|
{
|
||||||
|
struct has_preset *preset = NULL;
|
||||||
|
|
||||||
|
CHECKIF(index == BT_HAS_PRESET_INDEX_NONE) {
|
||||||
|
BT_ERR("index is invalid");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* toggle property bit if needed */
|
||||||
|
if (!(preset->properties & BT_HAS_PROP_AVAILABLE)) {
|
||||||
|
NET_BUF_SIMPLE_DEFINE(buf, sizeof(struct bt_has_cp_hdr) +
|
||||||
|
sizeof(struct bt_has_cp_preset_changed) + sizeof(uint8_t));
|
||||||
|
|
||||||
|
preset->properties ^= BT_HAS_PROP_AVAILABLE;
|
||||||
|
|
||||||
|
preset_changed_prepare(&buf, BT_HAS_CHANGE_ID_PRESET_AVAILABLE, BT_HAS_IS_LAST);
|
||||||
|
net_buf_simple_add_u8(&buf, preset->index);
|
||||||
|
|
||||||
|
return control_point_send_all(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_has_preset_unavailable(uint8_t index)
|
||||||
|
{
|
||||||
|
struct has_preset *preset = NULL;
|
||||||
|
|
||||||
|
CHECKIF(index == BT_HAS_PRESET_INDEX_NONE) {
|
||||||
|
BT_ERR("index is invalid");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* toggle property bit if needed */
|
||||||
|
if (preset->properties & BT_HAS_PROP_AVAILABLE) {
|
||||||
|
NET_BUF_SIMPLE_DEFINE(buf, sizeof(struct bt_has_cp_hdr) +
|
||||||
|
sizeof(struct bt_has_cp_preset_changed) + sizeof(uint8_t));
|
||||||
|
|
||||||
|
preset->properties ^= BT_HAS_PROP_AVAILABLE;
|
||||||
|
|
||||||
|
preset_changed_prepare(&buf, BT_HAS_CHANGE_ID_PRESET_UNAVAILABLE, BT_HAS_IS_LAST);
|
||||||
|
net_buf_simple_add_u8(&buf, preset->index);
|
||||||
|
|
||||||
|
return control_point_send_all(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
#define BT_HAS_CHANGE_ID_PRESET_AVAILABLE 0x02
|
#define BT_HAS_CHANGE_ID_PRESET_AVAILABLE 0x02
|
||||||
#define BT_HAS_CHANGE_ID_PRESET_UNAVAILABLE 0x03
|
#define BT_HAS_CHANGE_ID_PRESET_UNAVAILABLE 0x03
|
||||||
|
|
||||||
|
#define BT_HAS_IS_LAST 0x01
|
||||||
|
|
||||||
struct bt_has {
|
struct bt_has {
|
||||||
/** Hearing Aid Features value */
|
/** Hearing Aid Features value */
|
||||||
uint8_t features;
|
uint8_t features;
|
||||||
|
@ -74,6 +76,13 @@ struct bt_has_cp_preset_changed {
|
||||||
uint8_t is_last;
|
uint8_t is_last;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct bt_has_cp_generic_update {
|
||||||
|
uint8_t prev_index;
|
||||||
|
uint8_t index;
|
||||||
|
uint8_t properties;
|
||||||
|
uint8_t name[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
struct bt_has_cp_write_preset_name {
|
struct bt_has_cp_write_preset_name {
|
||||||
uint8_t index;
|
uint8_t index;
|
||||||
uint8_t name[0];
|
uint8_t name[0];
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue