1 /* btp_hap.c - Bluetooth HAP Tester */
2 
3 /*
4  * Copyright (c) 2023 Codecoup
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 #include <zephyr/sys/byteorder.h>
9 #include <zephyr/bluetooth/audio/audio.h>
10 #include <zephyr/bluetooth/audio/bap.h>
11 #include <zephyr/bluetooth/audio/has.h>
12 #include <zephyr/bluetooth/audio/pacs.h>
13 #include <zephyr/bluetooth/services/ias.h>
14 
15 #include "../bluetooth/audio/has_internal.h"
16 #include "btp/btp.h"
17 
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(bttester_hap, CONFIG_BTTESTER_LOG_LEVEL);
20 
read_supported_commands(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)21 static uint8_t read_supported_commands(const void *cmd, uint16_t cmd_len, void *rsp,
22 				       uint16_t *rsp_len)
23 {
24 	struct btp_hap_read_supported_commands_rp *rp = rsp;
25 
26 	tester_set_bit(rp->data, BTP_HAP_READ_SUPPORTED_COMMANDS);
27 	tester_set_bit(rp->data, BTP_HAP_HA_INIT);
28 	tester_set_bit(rp->data, BTP_HAP_HAUC_INIT);
29 	tester_set_bit(rp->data, BTP_HAP_IAC_INIT);
30 	tester_set_bit(rp->data, BTP_HAP_IAC_DISCOVER);
31 	tester_set_bit(rp->data, BTP_HAP_IAC_SET_ALERT);
32 	tester_set_bit(rp->data, BTP_HAP_HAUC_DISCOVER);
33 
34 	*rsp_len = sizeof(*rp) + 1;
35 
36 	return BTP_STATUS_SUCCESS;
37 }
38 
ha_init(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)39 static uint8_t ha_init(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len)
40 {
41 	const struct btp_hap_ha_init_cmd *cp = cmd;
42 	struct bt_has_features_param params;
43 	const uint16_t opts = sys_le16_to_cpu(cp->opts);
44 	const bool presets_sync = (opts & BTP_HAP_HA_OPT_PRESETS_SYNC) > 0;
45 	const bool presets_independent = (opts & BTP_HAP_HA_OPT_PRESETS_INDEPENDENT) > 0;
46 	const bool presets_writable = (opts & BTP_HAP_HA_OPT_PRESETS_WRITABLE) > 0;
47 	const bool presets_dynamic = (opts & BTP_HAP_HA_OPT_PRESETS_DYNAMIC) > 0;
48 	int err;
49 
50 	if (!IS_ENABLED(CONFIG_BT_HAS_PRESET_SUPPORT) &&
51 	    (presets_sync || presets_independent || presets_writable || presets_dynamic)) {
52 		return BTP_STATUS_VAL(-ENOTSUP);
53 	}
54 
55 	/* Only dynamic presets are supported */
56 	if (!presets_dynamic) {
57 		return BTP_STATUS_VAL(-ENOTSUP);
58 	}
59 
60 	/* Preset name writable support mismatch */
61 	if (presets_writable != IS_ENABLED(CONFIG_BT_HAS_PRESET_NAME_DYNAMIC)) {
62 		return BTP_STATUS_VAL(-ENOTSUP);
63 	}
64 
65 	params.type = cp->type;
66 	params.preset_sync_support = presets_sync;
67 	params.independent_presets = presets_independent;
68 
69 	if (cp->type == BT_HAS_HEARING_AID_TYPE_BANDED) {
70 		err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, BT_AUDIO_LOCATION_FRONT_LEFT |
71 							      BT_AUDIO_LOCATION_FRONT_RIGHT);
72 	} else {
73 		err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, BT_AUDIO_LOCATION_FRONT_LEFT);
74 	}
75 
76 	if (err != 0) {
77 		return BTP_STATUS_VAL(err);
78 	}
79 
80 	err = bt_has_register(&params);
81 	if (err != 0) {
82 		return BTP_STATUS_VAL(err);
83 	}
84 
85 	return BTP_STATUS_SUCCESS;
86 }
87 
has_client_discover_cb(struct bt_conn * conn,int err,struct bt_has * has,enum bt_has_hearing_aid_type type,enum bt_has_capabilities caps)88 static void has_client_discover_cb(struct bt_conn *conn, int err, struct bt_has *has,
89 				   enum bt_has_hearing_aid_type type, enum bt_has_capabilities caps)
90 {
91 	struct btp_hap_hauc_discovery_complete_ev ev = { 0 };
92 
93 	LOG_DBG("conn %p err %d", (void *)conn, err);
94 
95 	bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn));
96 	ev.status = BTP_STATUS_VAL(err);
97 
98 	if (err != 0 && err != BT_ATT_ERR_ATTRIBUTE_NOT_FOUND) {
99 		LOG_DBG("Client discovery failed: %d", err);
100 	} else {
101 		struct bt_has_client *inst = CONTAINER_OF(has, struct bt_has_client, has);
102 
103 		ev.has_hearing_aid_features_handle = inst->features_subscription.value_handle;
104 		ev.has_control_point_handle = inst->control_point_subscription.value_handle;
105 		ev.has_active_preset_index_handle = inst->active_index_subscription.value_handle;
106 	}
107 
108 	tester_event(BTP_SERVICE_ID_HAP, BT_HAP_EV_HAUC_DISCOVERY_COMPLETE, &ev, sizeof(ev));
109 }
110 
has_client_preset_switch_cb(struct bt_has * has,int err,uint8_t index)111 static void has_client_preset_switch_cb(struct bt_has *has, int err, uint8_t index)
112 {
113 
114 }
115 
116 static const struct bt_has_client_cb has_client_cb = {
117 	.discover = has_client_discover_cb,
118 	.preset_switch = has_client_preset_switch_cb,
119 };
120 
hauc_init(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)121 static uint8_t hauc_init(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len)
122 {
123 	int err;
124 
125 	err = bt_has_client_cb_register(&has_client_cb);
126 	if (err != 0) {
127 		LOG_DBG("Failed to register client callbacks: %d", err);
128 		return BTP_STATUS_FAILED;
129 	}
130 
131 	return BTP_STATUS_SUCCESS;
132 }
133 
ias_client_discover_cb(struct bt_conn * conn,int err)134 static void ias_client_discover_cb(struct bt_conn *conn, int err)
135 {
136 	struct btp_hap_iac_discovery_complete_ev ev;
137 	struct bt_conn_info info;
138 
139 	bt_conn_get_info(conn, &info);
140 
141 	bt_addr_le_copy(&ev.address, info.le.dst);
142 	if (err < 0) {
143 		ev.status = BT_ATT_ERR_UNLIKELY;
144 	} else {
145 		ev.status = (uint8_t)err;
146 	}
147 
148 	tester_event(BTP_SERVICE_ID_HAP, BT_HAP_EV_IAC_DISCOVERY_COMPLETE, &ev, sizeof(ev));
149 }
150 
151 static const struct bt_ias_client_cb ias_client_cb = {
152 	.discover = ias_client_discover_cb,
153 };
154 
iac_init(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)155 static uint8_t iac_init(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len)
156 {
157 	int err;
158 
159 	err = bt_ias_client_cb_register(&ias_client_cb);
160 	if (err != 0) {
161 		return BTP_STATUS_VAL(err);
162 	}
163 
164 	return BTP_STATUS_SUCCESS;
165 }
166 
iac_discover(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)167 static uint8_t iac_discover(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len)
168 {
169 	const struct btp_hap_iac_discover_cmd *cp = cmd;
170 	struct bt_conn *conn;
171 	int err;
172 
173 	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
174 	if (!conn) {
175 		LOG_ERR("Unknown connection");
176 		return BTP_STATUS_FAILED;
177 	}
178 
179 	err = bt_ias_discover(conn);
180 
181 	bt_conn_unref(conn);
182 
183 	return BTP_STATUS_VAL(err);
184 }
185 
iac_set_alert(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)186 static uint8_t iac_set_alert(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len)
187 {
188 	const struct btp_hap_iac_set_alert_cmd *cp = cmd;
189 	struct bt_conn *conn;
190 	int err;
191 
192 	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
193 	if (!conn) {
194 		LOG_ERR("Unknown connection");
195 		return BTP_STATUS_FAILED;
196 	}
197 
198 	err = bt_ias_client_alert_write(conn, (enum bt_ias_alert_lvl)cp->alert);
199 
200 	bt_conn_unref(conn);
201 
202 	return BTP_STATUS_VAL(err);
203 }
204 
hauc_discover(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)205 static uint8_t hauc_discover(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len)
206 {
207 	const struct btp_hap_hauc_discover_cmd *cp = cmd;
208 	struct bt_conn *conn;
209 	int err;
210 
211 	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
212 	if (!conn) {
213 		LOG_ERR("Unknown connection");
214 		return BTP_STATUS_FAILED;
215 	}
216 
217 	err = bt_has_client_discover(conn);
218 	if (err != 0) {
219 		LOG_DBG("Failed to discover remote HAS: %d", err);
220 	}
221 
222 	bt_conn_unref(conn);
223 
224 	return BTP_STATUS_VAL(err);
225 }
226 
227 static const struct btp_handler hap_handlers[] = {
228 	{
229 		.opcode = BTP_HAP_READ_SUPPORTED_COMMANDS,
230 		.index = BTP_INDEX_NONE,
231 		.expect_len = 0,
232 		.func = read_supported_commands,
233 	},
234 	{
235 		.opcode = BTP_HAP_HA_INIT,
236 		.expect_len = sizeof(struct btp_hap_ha_init_cmd),
237 		.func = ha_init,
238 	},
239 	{
240 		.opcode = BTP_HAP_HAUC_INIT,
241 		.expect_len = 0,
242 		.func = hauc_init,
243 	},
244 	{
245 		.opcode = BTP_HAP_IAC_INIT,
246 		.expect_len = 0,
247 		.func = iac_init,
248 	},
249 	{
250 		.opcode = BTP_HAP_IAC_DISCOVER,
251 		.expect_len = sizeof(struct btp_hap_iac_discover_cmd),
252 		.func = iac_discover,
253 	},
254 	{
255 		.opcode = BTP_HAP_IAC_SET_ALERT,
256 		.expect_len = sizeof(struct btp_hap_iac_set_alert_cmd),
257 		.func = iac_set_alert,
258 	},
259 	{
260 		.opcode = BTP_HAP_HAUC_DISCOVER,
261 		.expect_len = sizeof(struct btp_hap_hauc_discover_cmd),
262 		.func = hauc_discover,
263 	},
264 };
265 
tester_init_hap(void)266 uint8_t tester_init_hap(void)
267 {
268 	tester_register_command_handlers(BTP_SERVICE_ID_HAP, hap_handlers,
269 					 ARRAY_SIZE(hap_handlers));
270 
271 	return BTP_STATUS_SUCCESS;
272 }
273 
tester_unregister_hap(void)274 uint8_t tester_unregister_hap(void)
275 {
276 	return BTP_STATUS_SUCCESS;
277 }
278