Bluetooth: has: Add support for Write Preset Name operation
This handles Write Preset Name operation that is used to change the preset name. This covers as well changing the preset name by server. Fixes: HAS/SR/CP/BV-04-C Signed-off-by: Mariusz Skamra <mariusz.skamra@codecoup.pl>
This commit is contained in:
parent
8537e28f66
commit
cf30ac2bfc
3 changed files with 145 additions and 0 deletions
|
@ -231,6 +231,16 @@ struct bt_has_preset_ops {
|
||||||
* becomes active by calling @ref bt_has_preset_active_set.
|
* becomes active by calling @ref bt_has_preset_active_set.
|
||||||
*/
|
*/
|
||||||
int (*select)(uint8_t index, bool sync);
|
int (*select)(uint8_t index, bool sync);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Preset name changed callback
|
||||||
|
*
|
||||||
|
* This callback is called when the name of the preset identified by @p index has changed.
|
||||||
|
*
|
||||||
|
* @param index Preset index that name has been changed.
|
||||||
|
* @param name Preset current name.
|
||||||
|
*/
|
||||||
|
void (*name_changed)(uint8_t index, const char *name);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @brief Register structure for preset. */
|
/** @brief Register structure for preset. */
|
||||||
|
@ -374,6 +384,18 @@ static inline int bt_has_preset_active_clear(void)
|
||||||
return bt_has_preset_active_set(BT_HAS_PRESET_INDEX_NONE);
|
return bt_has_preset_active_set(BT_HAS_PRESET_INDEX_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Change the Preset Name.
|
||||||
|
*
|
||||||
|
* Change the name of the preset identified by @p index.
|
||||||
|
*
|
||||||
|
* @param index The index of the preset to change the name of.
|
||||||
|
* @param name Name to write.
|
||||||
|
*
|
||||||
|
* @return 0 in case of success or negative value in case of error.
|
||||||
|
*/
|
||||||
|
int bt_has_preset_name_change(uint8_t index, const char *name);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -539,6 +539,85 @@ static uint8_t handle_read_preset_req(struct bt_conn *conn, struct net_buf_simpl
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int set_preset_name(uint8_t index, const char *name, size_t len)
|
||||||
|
{
|
||||||
|
struct has_preset *preset = NULL;
|
||||||
|
|
||||||
|
BT_DBG("index %d name_len %zu", index, len);
|
||||||
|
|
||||||
|
if (len < BT_HAS_PRESET_NAME_MIN || len > BT_HAS_PRESET_NAME_MAX) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Abort if there is no preset in requested index range */
|
||||||
|
preset_foreach(index, BT_HAS_PRESET_INDEX_LAST, preset_found, &preset);
|
||||||
|
|
||||||
|
if (preset == NULL) {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(preset->properties & BT_HAS_PROP_WRITABLE)) {
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
IF_ENABLED(CONFIG_BT_HAS_PRESET_NAME_DYNAMIC, (
|
||||||
|
__ASSERT(len < ARRAY_SIZE(preset->name), "No space for name");
|
||||||
|
|
||||||
|
(void)memcpy(preset->name, name, len);
|
||||||
|
|
||||||
|
/* NULL-terminate string */
|
||||||
|
preset->name[len] = '\0';
|
||||||
|
|
||||||
|
/* Properly truncate a NULL-terminated UTF-8 string */
|
||||||
|
utf8_trunc(preset->name);
|
||||||
|
));
|
||||||
|
|
||||||
|
if (preset->ops->name_changed) {
|
||||||
|
preset->ops->name_changed(index, preset->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bt_has_cp_generic_update(preset, BT_HAS_IS_LAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t handle_write_preset_name(struct bt_conn *conn, struct net_buf_simple *buf)
|
||||||
|
{
|
||||||
|
const struct bt_has_cp_write_preset_name *req;
|
||||||
|
struct has_client *client;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (buf->len < sizeof(*req)) {
|
||||||
|
return BT_HAS_ERR_INVALID_PARAM_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* As per HAS_v1.0 Client Characteristic Configuration Descriptor Improperly Configured
|
||||||
|
* shall be returned if client writes Write Preset Name opcode but is not registered for
|
||||||
|
* indications.
|
||||||
|
*/
|
||||||
|
if (!bt_gatt_is_subscribed(conn, PRESET_CONTROL_POINT_ATTR, BT_GATT_CCC_INDICATE)) {
|
||||||
|
return BT_ATT_ERR_CCC_IMPROPER_CONF;
|
||||||
|
}
|
||||||
|
|
||||||
|
client = client_get(conn);
|
||||||
|
if (!client) {
|
||||||
|
return BT_ATT_ERR_UNLIKELY;
|
||||||
|
}
|
||||||
|
|
||||||
|
req = net_buf_simple_pull_mem(buf, sizeof(*req));
|
||||||
|
|
||||||
|
err = set_preset_name(req->index, req->name, buf->len);
|
||||||
|
if (err == -EINVAL) {
|
||||||
|
return BT_HAS_ERR_INVALID_PARAM_LEN;
|
||||||
|
} else if (err == -ENOENT) {
|
||||||
|
return BT_ATT_ERR_OUT_OF_RANGE;
|
||||||
|
} else if (err == -EPERM) {
|
||||||
|
return BT_HAS_ERR_WRITE_NAME_NOT_ALLOWED;
|
||||||
|
} else if (err) {
|
||||||
|
return BT_ATT_ERR_UNLIKELY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BT_ATT_ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static void active_preset_work_process(struct k_work *work)
|
static void active_preset_work_process(struct k_work *work)
|
||||||
{
|
{
|
||||||
const uint8_t active_index = bt_has_preset_active_get();
|
const uint8_t active_index = bt_has_preset_active_get();
|
||||||
|
@ -695,6 +774,11 @@ static uint8_t handle_control_point_op(struct bt_conn *conn, struct net_buf_simp
|
||||||
switch (hdr->opcode) {
|
switch (hdr->opcode) {
|
||||||
case BT_HAS_OP_READ_PRESET_REQ:
|
case BT_HAS_OP_READ_PRESET_REQ:
|
||||||
return handle_read_preset_req(conn, buf);
|
return handle_read_preset_req(conn, buf);
|
||||||
|
case BT_HAS_OP_WRITE_PRESET_NAME:
|
||||||
|
if (IS_ENABLED(CONFIG_BT_HAS_PRESET_NAME_DYNAMIC)) {
|
||||||
|
return handle_write_preset_name(conn, buf);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case BT_HAS_OP_SET_ACTIVE_PRESET:
|
case BT_HAS_OP_SET_ACTIVE_PRESET:
|
||||||
return handle_set_active_preset(buf, false);
|
return handle_set_active_preset(buf, false);
|
||||||
case BT_HAS_OP_SET_NEXT_PRESET:
|
case BT_HAS_OP_SET_NEXT_PRESET:
|
||||||
|
@ -945,6 +1029,19 @@ uint8_t bt_has_preset_active_get(void)
|
||||||
{
|
{
|
||||||
return has.active_index;
|
return has.active_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bt_has_preset_name_change(uint8_t index, const char *name)
|
||||||
|
{
|
||||||
|
CHECKIF(name == NULL) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_BT_HAS_PRESET_NAME_DYNAMIC)) {
|
||||||
|
return set_preset_name(index, name, strlen(name));
|
||||||
|
} else {
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
|
#endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
|
||||||
|
|
||||||
static int has_init(const struct device *dev)
|
static int has_init(const struct device *dev)
|
||||||
|
|
|
@ -22,8 +22,14 @@ static int preset_select(uint8_t index, bool sync)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void preset_name_changed(uint8_t index, const char *name)
|
||||||
|
{
|
||||||
|
shell_print(ctx_shell, "Preset name changed index %u name %s", index, name);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct bt_has_preset_ops preset_ops = {
|
static const struct bt_has_preset_ops preset_ops = {
|
||||||
.select = preset_select,
|
.select = preset_select,
|
||||||
|
.name_changed = preset_name_changed,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int cmd_preset_reg(const struct shell *sh, size_t argc, char **argv)
|
static int cmd_preset_reg(const struct shell *sh, size_t argc, char **argv)
|
||||||
|
@ -179,6 +185,25 @@ static int cmd_preset_active_clear(const struct shell *sh, size_t argc, char **a
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cmd_preset_name_set(const struct shell *sh, size_t argc, char **argv)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
const uint8_t index = shell_strtoul(argv[1], 16, &err);
|
||||||
|
|
||||||
|
if (err < 0) {
|
||||||
|
shell_print(sh, "Invalid command parameter (err %d)", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bt_has_preset_name_change(index, argv[2]);
|
||||||
|
if (err < 0) {
|
||||||
|
shell_print(sh, "Preset name change failed (err %d)", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int cmd_has(const struct shell *sh, size_t argc, char **argv)
|
static int cmd_has(const struct shell *sh, size_t argc, char **argv)
|
||||||
{
|
{
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
|
@ -204,6 +229,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE(has_cmds,
|
||||||
SHELL_CMD_ARG(preset-active-get, NULL, "Get active preset", cmd_preset_active_get, 1, 0),
|
SHELL_CMD_ARG(preset-active-get, NULL, "Get active preset", cmd_preset_active_get, 1, 0),
|
||||||
SHELL_CMD_ARG(preset-active-clear, NULL, "Clear selected preset",
|
SHELL_CMD_ARG(preset-active-clear, NULL, "Clear selected preset",
|
||||||
cmd_preset_active_clear, 1, 0),
|
cmd_preset_active_clear, 1, 0),
|
||||||
|
SHELL_CMD_ARG(set-name, NULL, "Set preset name <index> <name>", cmd_preset_name_set, 3, 0),
|
||||||
SHELL_SUBCMD_SET_END
|
SHELL_SUBCMD_SET_END
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue