/* btp_aics.c - Bluetooth AICS Tester */ /* * Copyright (c) 2023 Codecoup * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include "bap_endpoint.h" #include "btp/btp.h" #define LOG_MODULE_NAME bttester_aics LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL); #define BT_AICS_MAX_INPUT_DESCRIPTION_SIZE 16 #define BT_AICS_MAX_OUTPUT_DESCRIPTION_SIZE 16 struct btp_aics_instance aics_client_instance; struct btp_aics_instance aics_server_instance; static struct net_buf_simple *rx_ev_buf = NET_BUF_SIMPLE(BT_AICS_MAX_INPUT_DESCRIPTION_SIZE + sizeof(struct btp_aics_description_ev)); static uint8_t aics_supported_commands(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { struct btp_aics_read_supported_commands_rp *rp = rsp; /* octet 0 */ tester_set_bit(rp->data, BTP_AICS_READ_SUPPORTED_COMMANDS); tester_set_bit(rp->data, BTP_AICS_SET_GAIN); tester_set_bit(rp->data, BTP_AICS_MUTE); tester_set_bit(rp->data, BTP_AICS_UNMUTE); tester_set_bit(rp->data, BTP_AICS_MAN_GAIN_SET); tester_set_bit(rp->data, BTP_AICS_AUTO_GAIN_SET); tester_set_bit(rp->data, BTP_AICS_SET_MAN_GAIN_ONLY); /* octet 1 */ tester_set_bit(rp->data, BTP_AICS_SET_AUTO_GAIN_ONLY); tester_set_bit(rp->data, BTP_AICS_AUDIO_DESCRIPTION_SET); tester_set_bit(rp->data, BTP_AICS_MUTE_DISABLE); tester_set_bit(rp->data, BTP_AICS_GAIN_SETTING_PROP_GET); tester_set_bit(rp->data, BTP_AICS_TYPE_GET); tester_set_bit(rp->data, BTP_AICS_STATUS_GET); tester_set_bit(rp->data, BTP_AICS_STATE_GET); /* octet 2 */ tester_set_bit(rp->data, BTP_AICS_DESCRIPTION_GET); *rsp_len = sizeof(*rp) + 2; return BTP_STATUS_SUCCESS; } void btp_send_aics_state_ev(struct bt_conn *conn, uint8_t att_status, int8_t gain, uint8_t mute, uint8_t mode) { struct btp_aics_state_ev ev; bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); ev.att_status = att_status; ev.gain = gain; ev.mute = mute; ev.mode = mode; tester_event(BTP_SERVICE_ID_AICS, BTP_AICS_STATE_EV, &ev, sizeof(ev)); } void btp_send_gain_setting_properties_ev(struct bt_conn *conn, uint8_t att_status, uint8_t units, int8_t minimum, int8_t maximum) { struct btp_gain_setting_properties_ev ev; bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); ev.att_status = att_status; ev.units = units; ev.minimum = minimum; ev.maximum = maximum; tester_event(BTP_SERVICE_ID_AICS, BTP_GAIN_SETTING_PROPERTIES_EV, &ev, sizeof(ev)); } void btp_send_aics_input_type_event(struct bt_conn *conn, uint8_t att_status, uint8_t input_type) { struct btp_aics_input_type_ev ev; bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); ev.att_status = att_status; ev.input_type = input_type; tester_event(BTP_SERVICE_ID_AICS, BTP_AICS_INPUT_TYPE_EV, &ev, sizeof(ev)); } void btp_send_aics_status_ev(struct bt_conn *conn, uint8_t att_status, bool active) { struct btp_aics_status_ev ev; bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); ev.att_status = att_status; ev.active = active; tester_event(BTP_SERVICE_ID_AICS, BTP_AICS_STATUS_EV, &ev, sizeof(ev)); } void btp_send_aics_description_ev(struct bt_conn *conn, uint8_t att_status, uint8_t data_len, char *description) { struct btp_aics_description_ev *ev; net_buf_simple_init(rx_ev_buf, 0); ev = net_buf_simple_add(rx_ev_buf, sizeof(*ev)); bt_addr_le_copy(&ev->address, bt_conn_get_dst(conn)); ev->att_status = att_status; ev->data_len = data_len; memcpy(ev->data, description, data_len); tester_event(BTP_SERVICE_ID_AICS, BTP_AICS_DESCRIPTION_EV, ev, sizeof(*ev) + data_len); } void btp_send_aics_procedure_ev(struct bt_conn *conn, uint8_t att_status, uint8_t opcode) { struct btp_aics_procedure_ev ev; bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); ev.att_status = att_status; ev.opcode = opcode; tester_event(BTP_SERVICE_ID_AICS, BTP_AICS_PROCEDURE_EV, &ev, sizeof(ev)); } static uint8_t aics_set_gain(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { const struct btp_aics_set_gain_cmd *cp = cmd; LOG_DBG("AICS set gain %d", cp->gain); if (!bt_addr_le_eq(&cp->address, BT_ADDR_LE_ANY)) { if (bt_aics_gain_set(aics_client_instance.aics[0], cp->gain) != 0) { return BTP_STATUS_FAILED; } } else { for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_gain_set(aics_server_instance.aics[i], cp->gain) != 0) { return BTP_STATUS_FAILED; } } } return BTP_STATUS_SUCCESS; } static uint8_t aics_unmute(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { const struct btp_aics_unmute_cmd *cp = cmd; LOG_DBG("AICS Unmute"); if (!bt_addr_le_eq(&cp->address, BT_ADDR_LE_ANY)) { if (bt_aics_unmute(aics_client_instance.aics[0]) != 0) { return BTP_STATUS_FAILED; } } else { for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_unmute(aics_server_instance.aics[i]) != 0) { return BTP_STATUS_FAILED; } } } return BTP_STATUS_SUCCESS; } static uint8_t aics_mute(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { const struct btp_aics_mute_cmd *cp = cmd; LOG_DBG("AICS Mute"); if (!bt_addr_le_eq(&cp->address, BT_ADDR_LE_ANY)) { if (bt_aics_mute(aics_client_instance.aics[0]) != 0) { return BTP_STATUS_FAILED; } } else { for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_mute(aics_server_instance.aics[i]) != 0) { return BTP_STATUS_FAILED; } } } return BTP_STATUS_SUCCESS; } static uint8_t aics_state_get(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { const struct btp_aics_state_cmd *cp = cmd; LOG_DBG("AICS State"); if (!bt_addr_le_eq(&cp->address, BT_ADDR_LE_ANY)) { if (bt_aics_state_get(aics_client_instance.aics[0]) != 0) { return BTP_STATUS_FAILED; } } else { for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_state_get(aics_server_instance.aics[i]) != 0) { return BTP_STATUS_FAILED; } } } return BTP_STATUS_SUCCESS; } static uint8_t aics_type_get(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { const struct btp_aics_type_cmd *cp = cmd; LOG_DBG("AICS Type"); if (!bt_addr_le_eq(&cp->address, BT_ADDR_LE_ANY)) { if (bt_aics_type_get(aics_client_instance.aics[0]) != 0) { return BTP_STATUS_FAILED; } } else { for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_type_get(aics_server_instance.aics[i]) != 0) { return BTP_STATUS_FAILED; } } } return BTP_STATUS_SUCCESS; } static uint8_t aics_status_get(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { const struct btp_aics_status_cmd *cp = cmd; LOG_DBG("AICS Status"); if (!bt_addr_le_eq(&cp->address, BT_ADDR_LE_ANY)) { if (bt_aics_status_get(aics_client_instance.aics[0]) != 0) { return BTP_STATUS_FAILED; } } else { for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_status_get(aics_server_instance.aics[i]) != 0) { return BTP_STATUS_FAILED; } } } return BTP_STATUS_SUCCESS; } static uint8_t aics_gain_setting_prop_get(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { const struct btp_aics_gain_setting_prop_cmd *cp = cmd; LOG_DBG("AICS Gain settings properties"); if (!bt_addr_le_eq(&cp->address, BT_ADDR_LE_ANY)) { if (bt_aics_gain_setting_get(aics_client_instance.aics[0]) != 0) { return BTP_STATUS_FAILED; } } else { for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_gain_setting_get(aics_server_instance.aics[i]) != 0) { return BTP_STATUS_FAILED; } } } return BTP_STATUS_SUCCESS; } static uint8_t aics_man_gain_set(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { const struct btp_aics_manual_gain_cmd *cp = cmd; LOG_DBG("AICS set manual gain mode"); if (!bt_addr_le_eq(&cp->address, BT_ADDR_LE_ANY)) { if (bt_aics_manual_gain_set(aics_client_instance.aics[0]) != 0) { return BTP_STATUS_FAILED; } } else { for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_manual_gain_set(aics_server_instance.aics[i]) != 0) { return BTP_STATUS_FAILED; } } } return BTP_STATUS_SUCCESS; } static uint8_t aics_auto_gain_set(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { const struct btp_aics_auto_gain_cmd *cp = cmd; LOG_DBG("AICS set automatic gain mode"); if (!bt_addr_le_eq(&cp->address, BT_ADDR_LE_ANY)) { if (bt_aics_automatic_gain_set(aics_client_instance.aics[0]) != 0) { return BTP_STATUS_FAILED; } } else { for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_automatic_gain_set(aics_server_instance.aics[i]) != 0) { return BTP_STATUS_FAILED; } } } return BTP_STATUS_SUCCESS; } static uint8_t aics_set_man_gain_only(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { LOG_DBG("AICS manual gain only set"); for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_gain_set_manual_only(aics_server_instance.aics[i]) != 0) { return BTP_STATUS_FAILED; } } return BTP_STATUS_SUCCESS; } static uint8_t aics_set_auto_gain_only(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { LOG_DBG("AICS auto gain only set"); for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_gain_set_auto_only(aics_server_instance.aics[i]) != 0) { return BTP_STATUS_FAILED; } } return BTP_STATUS_SUCCESS; } static uint8_t aics_mute_disable(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { LOG_DBG("AICS disable mute"); for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_disable_mute(aics_server_instance.aics[i]) != 0) { return BTP_STATUS_FAILED; } } return BTP_STATUS_SUCCESS; } static uint8_t aics_desc_set(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { const struct btp_aics_audio_desc_cmd *cp = cmd; char description[BT_AICS_MAX_INPUT_DESCRIPTION_SIZE]; LOG_DBG("AICS set description"); if (cmd_len < sizeof(*cp) || cmd_len != sizeof(*cp) + cp->desc_len) { return BTP_STATUS_FAILED; } if (cp->desc_len >= sizeof(description)) { return BTP_STATUS_FAILED; } if (cp->desc_len > (BT_AICS_MAX_INPUT_DESCRIPTION_SIZE - 1)) { return BTP_STATUS_FAILED; } memcpy(description, cp->desc, cp->desc_len); description[cp->desc_len] = '\0'; for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_description_set(aics_server_instance.aics[i], description) != 0) { return BTP_STATUS_FAILED; } } return BTP_STATUS_SUCCESS; } static uint8_t aics_desc_get(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) { const struct btp_aics_desc_cmd *cp = cmd; LOG_DBG("AICS Description"); if (!bt_addr_le_eq(&cp->address, BT_ADDR_LE_ANY)) { if (bt_aics_description_get(aics_client_instance.aics[0]) != 0) { return BTP_STATUS_FAILED; } } else { for (uint8_t i = 0; i < aics_server_instance.aics_cnt; i++) { if (bt_aics_description_get(aics_server_instance.aics[i]) != 0) { return BTP_STATUS_FAILED; } } } return BTP_STATUS_SUCCESS; } static const struct btp_handler aics_handlers[] = { { .opcode = BTP_AICS_READ_SUPPORTED_COMMANDS, .index = BTP_INDEX_NONE, .expect_len = 0, .func = aics_supported_commands, }, { .opcode = BTP_AICS_SET_GAIN, .expect_len = sizeof(struct btp_aics_set_gain_cmd), .func = aics_set_gain, }, { .opcode = BTP_AICS_MUTE, .expect_len = sizeof(struct btp_aics_mute_cmd), .func = aics_mute, }, { .opcode = BTP_AICS_UNMUTE, .expect_len = sizeof(struct btp_aics_unmute_cmd), .func = aics_unmute, }, { .opcode = BTP_AICS_GAIN_SETTING_PROP_GET, .expect_len = sizeof(struct btp_aics_gain_setting_prop_cmd), .func = aics_gain_setting_prop_get, }, { .opcode = BTP_AICS_MUTE_DISABLE, .expect_len = 0, .func = aics_mute_disable, }, { .opcode = BTP_AICS_MAN_GAIN_SET, .expect_len = sizeof(struct btp_aics_manual_gain_cmd), .func = aics_man_gain_set, }, { .opcode = BTP_AICS_AUTO_GAIN_SET, .expect_len = sizeof(struct btp_aics_auto_gain_cmd), .func = aics_auto_gain_set, }, { .opcode = BTP_AICS_SET_AUTO_GAIN_ONLY, .expect_len = 0, .func = aics_set_auto_gain_only, }, { .opcode = BTP_AICS_SET_MAN_GAIN_ONLY, .expect_len = 0, .func = aics_set_man_gain_only, }, { .opcode = BTP_AICS_AUDIO_DESCRIPTION_SET, .expect_len = BTP_HANDLER_LENGTH_VARIABLE, .func = aics_desc_set, }, { .opcode = BTP_AICS_DESCRIPTION_GET, .expect_len = sizeof(struct btp_aics_desc_cmd), .func = aics_desc_get, }, { .opcode = BTP_AICS_TYPE_GET, .expect_len = sizeof(struct btp_aics_type_cmd), .func = aics_type_get, }, { .opcode = BTP_AICS_STATUS_GET, .expect_len = sizeof(struct btp_aics_status_cmd), .func = aics_status_get, }, { .opcode = BTP_AICS_STATE_GET, .expect_len = sizeof(struct btp_aics_state_cmd), .func = aics_state_get, }, }; static void aics_state_cb(struct bt_aics *inst, int err, int8_t gain, uint8_t mute, uint8_t mode) { struct bt_conn *conn; bt_aics_client_conn_get(inst, &conn); if (err) { if (err < 0) { err = BT_ATT_ERR_UNLIKELY; } btp_send_aics_state_ev(conn, err, 0, 0, 0); } else { btp_send_aics_state_ev(conn, 0, gain, mute, mode); } LOG_DBG("AICS state callback (%d)", err); } static void aics_gain_setting_cb(struct bt_aics *inst, int err, uint8_t units, int8_t minimum, int8_t maximum) { struct bt_conn *conn; bt_aics_client_conn_get(inst, &conn); btp_send_gain_setting_properties_ev(conn, err, units, minimum, maximum); LOG_DBG("AICS gain setting callback (%d)", err); } static void aics_input_type_cb(struct bt_aics *inst, int err, uint8_t input_type) { struct bt_conn *conn; bt_aics_client_conn_get(inst, &conn); btp_send_aics_input_type_event(conn, err, input_type); LOG_DBG("AICS input type callback (%d)", err); } static void aics_status_cb(struct bt_aics *inst, int err, bool active) { struct bt_conn *conn; bt_aics_client_conn_get(inst, &conn); btp_send_aics_status_ev(conn, err, active); LOG_DBG("AICS status callback (%d)", err); } static void aics_description_cb(struct bt_aics *inst, int err, char *description) { struct bt_conn *conn; uint8_t data_len = strlen(description); bt_aics_client_conn_get(inst, &conn); btp_send_aics_description_ev(conn, err, data_len, description); LOG_DBG("AICS description callback (%d)", err); } static void aics_set_gain_cb(struct bt_aics *inst, int err) { struct bt_conn *conn; bt_aics_client_conn_get(inst, &conn); btp_send_aics_procedure_ev(conn, err, BTP_AICS_SET_GAIN); LOG_DBG("AICS set gain cb (%d)", err); } static void aics_mute_cb(struct bt_aics *inst, int err) { struct bt_conn *conn; bt_aics_client_conn_get(inst, &conn); btp_send_aics_procedure_ev(conn, err, BTP_AICS_MUTE); LOG_DBG("AICS mute cb (%d)", err); } static void aics_unmute_cb(struct bt_aics *inst, int err) { struct bt_conn *conn; bt_aics_client_conn_get(inst, &conn); btp_send_aics_procedure_ev(conn, err, BTP_AICS_UNMUTE); LOG_DBG("AICS unmute cb (%d)", err); } static void aics_set_man_gain_cb(struct bt_aics *inst, int err) { struct bt_conn *conn; bt_aics_client_conn_get(inst, &conn); btp_send_aics_procedure_ev(conn, err, BTP_AICS_MAN_GAIN_SET); LOG_DBG("AICS set manual gain cb (%d)", err); } static void aics_set_auto_gain_cb(struct bt_aics *inst, int err) { struct bt_conn *conn; bt_aics_client_conn_get(inst, &conn); btp_send_aics_procedure_ev(conn, err, BTP_AICS_AUTO_GAIN_SET); LOG_DBG("AICS set automatic gain cb (%d)", err); } struct bt_aics_cb aics_client_cb = { .state = aics_state_cb, .gain_setting = aics_gain_setting_cb, .type = aics_input_type_cb, .status = aics_status_cb, .description = aics_description_cb, #if defined(CONFIG_BT_AICS_CLIENT) .set_gain = aics_set_gain_cb, .unmute = aics_unmute_cb, .mute = aics_mute_cb, .set_manual_mode = aics_set_man_gain_cb, .set_auto_mode = aics_set_auto_gain_cb #endif /* CONFIG_BT_AICS_CLIENT */ }; uint8_t tester_init_aics(void) { tester_register_command_handlers(BTP_SERVICE_ID_AICS, aics_handlers, ARRAY_SIZE(aics_handlers)); return BTP_STATUS_SUCCESS; } uint8_t tester_unregister_aics(void) { return BTP_STATUS_SUCCESS; }