1 /*  Bluetooth VCP */
2 
3 /*
4  * Copyright (c) 2018 Intel Corporation
5  * Copyright (c) 2019-2020 Bose Corporation
6  * Copyright (c) 2020-2022 Nordic Semiconductor ASA
7  *
8  * SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #include <errno.h>
12 #include <stdbool.h>
13 #include <stddef.h>
14 #include <stdint.h>
15 #include <sys/types.h>
16 
17 #include <zephyr/autoconf.h>
18 #include <zephyr/bluetooth/att.h>
19 #include <zephyr/bluetooth/audio/aics.h>
20 #include <zephyr/bluetooth/audio/vcp.h>
21 #include <zephyr/bluetooth/audio/vocs.h>
22 #include <zephyr/bluetooth/bluetooth.h>
23 #include <zephyr/bluetooth/conn.h>
24 #include <zephyr/bluetooth/gatt.h>
25 #include <zephyr/bluetooth/uuid.h>
26 #include <zephyr/device.h>
27 #include <zephyr/init.h>
28 #include <zephyr/kernel.h>
29 #include <zephyr/logging/log.h>
30 #include <zephyr/sys/__assert.h>
31 #include <zephyr/sys/atomic.h>
32 #include <zephyr/sys/byteorder.h>
33 #include <zephyr/sys/check.h>
34 #include <zephyr/sys/time_units.h>
35 #include <zephyr/sys/util.h>
36 #include <zephyr/sys/util_macro.h>
37 #include <zephyr/sys_clock.h>
38 
39 #include "audio_internal.h"
40 #include "vcp_internal.h"
41 
42 #define LOG_LEVEL CONFIG_BT_VCP_VOL_REND_LOG_LEVEL
43 
44 LOG_MODULE_REGISTER(bt_vcp_vol_rend);
45 
46 #define VOLUME_DOWN(current_vol) \
47 	((uint8_t)MAX(0, (int)current_vol - vol_rend.volume_step))
48 #define VOLUME_UP(current_vol) \
49 	((uint8_t)MIN(UINT8_MAX, (int)current_vol + vol_rend.volume_step))
50 
51 #define VALID_VCP_OPCODE(opcode) ((opcode) <= BT_VCP_OPCODE_MUTE)
52 
53 enum vol_rend_notify {
54 	NOTIFY_STATE,
55 	NOTIFY_FLAGS,
56 	NOTIFY_NUM,
57 };
58 
59 struct bt_vcp_vol_rend {
60 	struct vcs_state state;
61 	uint8_t flags;
62 	struct bt_vcp_vol_rend_cb *cb;
63 	uint8_t volume_step;
64 
65 	struct bt_gatt_service *service_p;
66 	struct bt_vocs *vocs_insts[CONFIG_BT_VCP_VOL_REND_VOCS_INSTANCE_COUNT];
67 	struct bt_aics *aics_insts[CONFIG_BT_VCP_VOL_REND_AICS_INSTANCE_COUNT];
68 
69 	ATOMIC_DEFINE(notify, NOTIFY_NUM);
70 	struct k_work_delayable notify_work;
71 };
72 
73 static struct bt_vcp_vol_rend vol_rend;
74 
volume_state_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)75 static void volume_state_cfg_changed(const struct bt_gatt_attr *attr,
76 				     uint16_t value)
77 {
78 	LOG_DBG("value 0x%04x", value);
79 }
80 
read_vol_state(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)81 static ssize_t read_vol_state(struct bt_conn *conn,
82 			      const struct bt_gatt_attr *attr, void *buf,
83 			      uint16_t len, uint16_t offset)
84 {
85 	LOG_DBG("Volume %u, mute %u, counter %u",
86 		vol_rend.state.volume, vol_rend.state.mute,
87 		vol_rend.state.change_counter);
88 
89 	return bt_gatt_attr_read(conn, attr, buf, len, offset,
90 				 &vol_rend.state, sizeof(vol_rend.state));
91 }
92 
vol_rend_notify_str(enum vol_rend_notify notify)93 static const char *vol_rend_notify_str(enum vol_rend_notify notify)
94 {
95 	switch (notify) {
96 	case NOTIFY_STATE:
97 		return "state";
98 	case NOTIFY_FLAGS:
99 		return "flags";
100 	default:
101 		return "unknown";
102 	}
103 }
104 
notify_work_reschedule(struct bt_vcp_vol_rend * inst,enum vol_rend_notify notify,k_timeout_t delay)105 static void notify_work_reschedule(struct bt_vcp_vol_rend *inst, enum vol_rend_notify notify,
106 				   k_timeout_t delay)
107 {
108 	int err;
109 
110 	atomic_set_bit(inst->notify, notify);
111 
112 	err = k_work_reschedule(&inst->notify_work, delay);
113 	if (err < 0) {
114 		LOG_ERR("Failed to reschedule %s notification err %d", vol_rend_notify_str(notify),
115 			err);
116 	} else if (!K_TIMEOUT_EQ(delay, K_NO_WAIT)) {
117 		LOG_DBG("%s notification scheduled in %dms", vol_rend_notify_str(notify),
118 			k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&inst->notify_work)));
119 	}
120 }
121 
notify(struct bt_vcp_vol_rend * inst,enum vol_rend_notify notify,const struct bt_uuid * uuid,const void * data,uint16_t len)122 static void notify(struct bt_vcp_vol_rend *inst, enum vol_rend_notify notify,
123 		   const struct bt_uuid *uuid, const void *data, uint16_t len)
124 {
125 	int err;
126 
127 	err = bt_gatt_notify_uuid(NULL, uuid, inst->service_p->attrs, data, len);
128 	if (err == -ENOMEM) {
129 		notify_work_reschedule(inst, notify, K_USEC(BT_AUDIO_NOTIFY_RETRY_DELAY_US));
130 	} else if (err < 0 && err != -ENOTCONN) {
131 		LOG_ERR("Notify %s err %d", vol_rend_notify_str(notify), err);
132 	}
133 }
134 
notify_work_handler(struct k_work * work)135 static void notify_work_handler(struct k_work *work)
136 {
137 	struct k_work_delayable *d_work = k_work_delayable_from_work(work);
138 	struct bt_vcp_vol_rend *inst = CONTAINER_OF(d_work, struct bt_vcp_vol_rend, notify_work);
139 
140 	if (atomic_test_and_clear_bit(inst->notify, NOTIFY_STATE)) {
141 		notify(inst, NOTIFY_STATE, BT_UUID_VCS_STATE, &inst->state, sizeof(inst->state));
142 	}
143 
144 	if (IS_ENABLED(CONFIG_BT_VCP_VOL_REND_VOL_FLAGS_NOTIFIABLE) &&
145 	    atomic_test_and_clear_bit(inst->notify, NOTIFY_FLAGS)) {
146 		notify(inst, NOTIFY_FLAGS, BT_UUID_VCS_FLAGS, &inst->flags, sizeof(inst->flags));
147 	}
148 }
149 
value_changed(struct bt_vcp_vol_rend * inst,enum vol_rend_notify notify)150 static void value_changed(struct bt_vcp_vol_rend *inst, enum vol_rend_notify notify)
151 {
152 	notify_work_reschedule(inst, notify, K_NO_WAIT);
153 }
154 
write_vcs_control(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)155 static ssize_t write_vcs_control(struct bt_conn *conn,
156 				 const struct bt_gatt_attr *attr,
157 				 const void *buf, uint16_t len, uint16_t offset,
158 				 uint8_t flags)
159 {
160 	const struct vcs_control_vol *cp_val = buf;
161 	bool notify = false;
162 	bool volume_change = false;
163 	uint8_t opcode;
164 
165 	if (offset > 0) {
166 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
167 	}
168 
169 	if (len == 0 || buf == NULL) {
170 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
171 	}
172 
173 	/* Check opcode before length */
174 	if (!VALID_VCP_OPCODE(cp_val->cp.opcode)) {
175 		LOG_DBG("Invalid opcode %u", cp_val->cp.opcode);
176 		return BT_GATT_ERR(BT_VCP_ERR_OP_NOT_SUPPORTED);
177 	}
178 
179 	if ((len < sizeof(struct vcs_control)) ||
180 	    (len == sizeof(struct vcs_control_vol) &&
181 	    cp_val->cp.opcode != BT_VCP_OPCODE_SET_ABS_VOL) ||
182 	    (len > sizeof(struct vcs_control_vol))) {
183 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
184 	}
185 
186 	opcode = cp_val->cp.opcode;
187 
188 	LOG_DBG("Opcode %u, counter %u", opcode, cp_val->cp.counter);
189 
190 	if (cp_val->cp.counter != vol_rend.state.change_counter) {
191 		return BT_GATT_ERR(BT_VCP_ERR_INVALID_COUNTER);
192 	}
193 
194 	switch (opcode) {
195 	case BT_VCP_OPCODE_REL_VOL_DOWN:
196 		LOG_DBG("Relative Volume Down (0x%x)", opcode);
197 		if (vol_rend.state.volume > 0) {
198 			vol_rend.state.volume = VOLUME_DOWN(vol_rend.state.volume);
199 			notify = true;
200 		}
201 		volume_change = true;
202 		break;
203 	case BT_VCP_OPCODE_REL_VOL_UP:
204 		LOG_DBG("Relative Volume Up (0x%x)", opcode);
205 		if (vol_rend.state.volume != UINT8_MAX) {
206 			vol_rend.state.volume = VOLUME_UP(vol_rend.state.volume);
207 			notify = true;
208 		}
209 		volume_change = true;
210 		break;
211 	case BT_VCP_OPCODE_UNMUTE_REL_VOL_DOWN:
212 		LOG_DBG("(Unmute) relative Volume Down (0x%x)", opcode);
213 		if (vol_rend.state.volume > 0) {
214 			vol_rend.state.volume = VOLUME_DOWN(vol_rend.state.volume);
215 			notify = true;
216 		}
217 		if (vol_rend.state.mute) {
218 			vol_rend.state.mute = BT_VCP_STATE_UNMUTED;
219 			notify = true;
220 		}
221 		volume_change = true;
222 		break;
223 	case BT_VCP_OPCODE_UNMUTE_REL_VOL_UP:
224 		LOG_DBG("(Unmute) relative Volume Up (0x%x)", opcode);
225 		if (vol_rend.state.volume != UINT8_MAX) {
226 			vol_rend.state.volume = VOLUME_UP(vol_rend.state.volume);
227 			notify = true;
228 		}
229 		if (vol_rend.state.mute) {
230 			vol_rend.state.mute = BT_VCP_STATE_UNMUTED;
231 			notify = true;
232 		}
233 		volume_change = true;
234 		break;
235 	case BT_VCP_OPCODE_SET_ABS_VOL:
236 		LOG_DBG("Set Absolute Volume (0x%x): Current volume %u",
237 			opcode, vol_rend.state.volume);
238 		if (vol_rend.state.volume != cp_val->volume) {
239 			vol_rend.state.volume = cp_val->volume;
240 			notify = true;
241 		}
242 		volume_change = true;
243 		break;
244 	case BT_VCP_OPCODE_UNMUTE:
245 		LOG_DBG("Unmute (0x%x)", opcode);
246 		if (vol_rend.state.mute) {
247 			vol_rend.state.mute = BT_VCP_STATE_UNMUTED;
248 			notify = true;
249 		}
250 		break;
251 	case BT_VCP_OPCODE_MUTE:
252 		LOG_DBG("Mute (0x%x)", opcode);
253 		if (vol_rend.state.mute == BT_VCP_STATE_UNMUTED) {
254 			vol_rend.state.mute = BT_VCP_STATE_MUTED;
255 			notify = true;
256 		}
257 		break;
258 	default:
259 		LOG_DBG("Unknown opcode (0x%x)", opcode);
260 		return BT_GATT_ERR(BT_VCP_ERR_OP_NOT_SUPPORTED);
261 	}
262 
263 	if (notify) {
264 		vol_rend.state.change_counter++;
265 		LOG_DBG("New state: volume %u, mute %u, counter %u",
266 			vol_rend.state.volume, vol_rend.state.mute,
267 			vol_rend.state.change_counter);
268 
269 		value_changed(&vol_rend, NOTIFY_STATE);
270 
271 		if (vol_rend.cb && vol_rend.cb->state) {
272 			vol_rend.cb->state(conn, 0, vol_rend.state.volume, vol_rend.state.mute);
273 		}
274 	}
275 
276 	if (volume_change && !vol_rend.flags) {
277 		vol_rend.flags = 1;
278 
279 		if (IS_ENABLED(CONFIG_BT_VCP_VOL_REND_VOL_FLAGS_NOTIFIABLE)) {
280 			value_changed(&vol_rend, NOTIFY_FLAGS);
281 		}
282 
283 		if (vol_rend.cb && vol_rend.cb->flags) {
284 			vol_rend.cb->flags(conn, 0, vol_rend.flags);
285 		}
286 	}
287 	return len;
288 }
289 
290 #if defined(CONFIG_BT_VCP_VOL_REND_VOL_FLAGS_NOTIFIABLE)
flags_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)291 static void flags_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
292 {
293 	LOG_DBG("value 0x%04x", value);
294 }
295 #endif /* CONFIG_BT_VCP_VOL_REND_VOL_FLAGS_NOTIFIABLE */
296 
read_flags(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)297 static ssize_t read_flags(struct bt_conn *conn, const struct bt_gatt_attr *attr,
298 			  void *buf, uint16_t len, uint16_t offset)
299 {
300 	LOG_DBG("0x%02x", vol_rend.flags);
301 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &vol_rend.flags,
302 				 sizeof(vol_rend.flags));
303 }
304 
305 #define DUMMY_INCLUDE(i, _) BT_GATT_INCLUDE_SERVICE(NULL),
306 #define VOCS_INCLUDES(cnt) LISTIFY(cnt, DUMMY_INCLUDE, ())
307 #define AICS_INCLUDES(cnt) LISTIFY(cnt, DUMMY_INCLUDE, ())
308 
309 /* Volume Control Service GATT Attributes */
310 static struct bt_gatt_attr vcs_attrs[] = {
311 	BT_GATT_PRIMARY_SERVICE(BT_UUID_VCS),
312 	VOCS_INCLUDES(CONFIG_BT_VCP_VOL_REND_VOCS_INSTANCE_COUNT)
313 	AICS_INCLUDES(CONFIG_BT_VCP_VOL_REND_AICS_INSTANCE_COUNT)
314 	BT_AUDIO_CHRC(BT_UUID_VCS_STATE,
315 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
316 		      BT_GATT_PERM_READ_ENCRYPT,
317 		      read_vol_state, NULL, NULL),
318 	BT_AUDIO_CCC(volume_state_cfg_changed),
319 	BT_AUDIO_CHRC(BT_UUID_VCS_CONTROL,
320 		      BT_GATT_CHRC_WRITE,
321 		      BT_GATT_PERM_WRITE_ENCRYPT,
322 		      NULL, write_vcs_control, NULL),
323 #if defined(CONFIG_BT_VCP_VOL_REND_VOL_FLAGS_NOTIFIABLE)
324 	BT_AUDIO_CHRC(BT_UUID_VCS_FLAGS,
325 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
326 		      BT_GATT_PERM_READ_ENCRYPT,
327 		      read_flags, NULL, NULL),
328 	BT_AUDIO_CCC(flags_cfg_changed)
329 #else
330 	BT_AUDIO_CHRC(BT_UUID_VCS_FLAGS,
331 		      BT_GATT_CHRC_READ,
332 		      BT_GATT_PERM_READ_ENCRYPT,
333 		      read_flags, NULL, NULL)
334 #endif /* CONFIG_BT_VCP_VOL_REND_VOL_FLAGS_NOTIFIABLE */
335 };
336 
337 static struct bt_gatt_service vcs_svc;
338 
prepare_vocs_inst(struct bt_vcp_vol_rend_register_param * param)339 static int prepare_vocs_inst(struct bt_vcp_vol_rend_register_param *param)
340 {
341 	int err;
342 	int j;
343 	int i;
344 
345 	if (CONFIG_BT_VCP_VOL_REND_VOCS_INSTANCE_COUNT == 0) {
346 		return 0;
347 	}
348 
349 	__ASSERT(param, "NULL param");
350 
351 	for (j = 0, i = 0; i < ARRAY_SIZE(vcs_attrs); i++) {
352 		if (bt_uuid_cmp(vcs_attrs[i].uuid, BT_UUID_GATT_INCLUDE) == 0 &&
353 		    !vcs_attrs[i].user_data) {
354 
355 			vol_rend.vocs_insts[j] = bt_vocs_free_instance_get();
356 
357 			if (vol_rend.vocs_insts[j] == NULL) {
358 				LOG_ERR("Could not get free VOCS instances[%d]",
359 					j);
360 				return -ENOMEM;
361 			}
362 
363 			err = bt_vocs_register(vol_rend.vocs_insts[j],
364 					       &param->vocs_param[j]);
365 			if (err != 0) {
366 				LOG_DBG("Could not register VOCS instance[%d]: %d",
367 					j, err);
368 				return err;
369 			}
370 
371 			vcs_attrs[i].user_data = bt_vocs_svc_decl_get(vol_rend.vocs_insts[j]);
372 			j++;
373 
374 			if (j == CONFIG_BT_VCP_VOL_REND_VOCS_INSTANCE_COUNT) {
375 				break;
376 			}
377 		}
378 	}
379 
380 	__ASSERT(j == CONFIG_BT_VCP_VOL_REND_VOCS_INSTANCE_COUNT,
381 		 "Invalid VOCS instance count");
382 
383 	return 0;
384 }
385 
prepare_aics_inst(struct bt_vcp_vol_rend_register_param * param)386 static int prepare_aics_inst(struct bt_vcp_vol_rend_register_param *param)
387 {
388 	int err;
389 	int j;
390 	int i;
391 
392 	if (CONFIG_BT_VCP_VOL_REND_AICS_INSTANCE_COUNT == 0) {
393 		return 0;
394 	}
395 
396 	__ASSERT(param, "NULL param");
397 
398 	for (j = 0, i = 0; i < ARRAY_SIZE(vcs_attrs); i++) {
399 		if (bt_uuid_cmp(vcs_attrs[i].uuid, BT_UUID_GATT_INCLUDE) == 0 &&
400 		    !vcs_attrs[i].user_data) {
401 			vol_rend.aics_insts[j] = bt_aics_free_instance_get();
402 
403 			if (vol_rend.aics_insts[j] == NULL) {
404 				LOG_ERR("Could not get free AICS instances[%d]",
405 					j);
406 				return -ENOMEM;
407 			}
408 
409 			err = bt_aics_register(vol_rend.aics_insts[j],
410 					       &param->aics_param[j]);
411 			if (err != 0) {
412 				LOG_DBG("Could not register AICS instance[%d]: %d",
413 					j, err);
414 				return err;
415 			}
416 
417 			vcs_attrs[i].user_data = bt_aics_svc_decl_get(vol_rend.aics_insts[j]);
418 			j++;
419 
420 			LOG_DBG("AICS P %p", vcs_attrs[i].user_data);
421 
422 			if (j == CONFIG_BT_VCP_VOL_REND_AICS_INSTANCE_COUNT) {
423 				break;
424 			}
425 		}
426 	}
427 
428 	__ASSERT(j == CONFIG_BT_VCP_VOL_REND_AICS_INSTANCE_COUNT,
429 		 "Invalid AICS instance count");
430 
431 	return 0;
432 }
433 
434 /****************************** PUBLIC API ******************************/
bt_vcp_vol_rend_register(struct bt_vcp_vol_rend_register_param * param)435 int bt_vcp_vol_rend_register(struct bt_vcp_vol_rend_register_param *param)
436 {
437 	static bool registered;
438 	int err;
439 
440 	CHECKIF(param == NULL) {
441 		LOG_DBG("param is NULL");
442 		return -EINVAL;
443 	}
444 
445 	CHECKIF(param->mute > BT_VCP_STATE_MUTED) {
446 		LOG_DBG("Invalid mute value: %u", param->mute);
447 		return -EINVAL;
448 	}
449 
450 	CHECKIF(param->step == 0) {
451 		LOG_DBG("Invalid step value: %u", param->step);
452 		return -EINVAL;
453 	}
454 
455 	if (registered) {
456 		return -EALREADY;
457 	}
458 
459 	vcs_svc = (struct bt_gatt_service)BT_GATT_SERVICE(vcs_attrs);
460 
461 	if (CONFIG_BT_VCP_VOL_REND_VOCS_INSTANCE_COUNT > 0) {
462 		err = prepare_vocs_inst(param);
463 
464 		if (err != 0) {
465 			return err;
466 		}
467 	}
468 
469 	if (CONFIG_BT_VCP_VOL_REND_AICS_INSTANCE_COUNT > 0) {
470 		err = prepare_aics_inst(param);
471 
472 		if (err != 0) {
473 			return err;
474 		}
475 	}
476 
477 
478 	vol_rend.state.volume = param->volume;
479 	vol_rend.state.mute = param->mute;
480 	vol_rend.volume_step = param->step;
481 	vol_rend.service_p = &vcs_svc;
482 
483 	err = bt_gatt_service_register(&vcs_svc);
484 	if (err != 0) {
485 		LOG_DBG("VCP service register failed: %d", err);
486 	}
487 
488 	vol_rend.cb = param->cb;
489 
490 	atomic_clear(vol_rend.notify);
491 	k_work_init_delayable(&vol_rend.notify_work, notify_work_handler);
492 
493 	registered = true;
494 
495 	return err;
496 }
497 
bt_vcp_vol_rend_included_get(struct bt_vcp_included * included)498 int bt_vcp_vol_rend_included_get(struct bt_vcp_included *included)
499 {
500 	if (included == NULL) {
501 		return -EINVAL;
502 	}
503 
504 	included->vocs_cnt = ARRAY_SIZE(vol_rend.vocs_insts);
505 	included->vocs = vol_rend.vocs_insts;
506 
507 	included->aics_cnt = ARRAY_SIZE(vol_rend.aics_insts);
508 	included->aics = vol_rend.aics_insts;
509 
510 	return 0;
511 }
512 
bt_vcp_vol_rend_set_step(uint8_t volume_step)513 int bt_vcp_vol_rend_set_step(uint8_t volume_step)
514 {
515 	if (volume_step > 0) {
516 		vol_rend.volume_step = volume_step;
517 		return 0;
518 	} else {
519 		return -EINVAL;
520 	}
521 }
522 
bt_vcp_vol_rend_get_state(void)523 int bt_vcp_vol_rend_get_state(void)
524 {
525 	if (vol_rend.cb && vol_rend.cb->state) {
526 		vol_rend.cb->state(NULL, 0, vol_rend.state.volume, vol_rend.state.mute);
527 	}
528 
529 	return 0;
530 }
531 
bt_vcp_vol_rend_get_flags(void)532 int bt_vcp_vol_rend_get_flags(void)
533 {
534 	if (vol_rend.cb && vol_rend.cb->flags) {
535 		vol_rend.cb->flags(NULL, 0, vol_rend.flags);
536 	}
537 
538 	return 0;
539 }
540 
bt_vcp_vol_rend_vol_down(void)541 int bt_vcp_vol_rend_vol_down(void)
542 {
543 	const struct vcs_control cp = {
544 		.opcode = BT_VCP_OPCODE_REL_VOL_DOWN,
545 		.counter = vol_rend.state.change_counter,
546 	};
547 	int err;
548 
549 	err = write_vcs_control(NULL, NULL, &cp, sizeof(cp), 0, 0);
550 
551 	return err > 0 ? 0 : err;
552 }
553 
bt_vcp_vol_rend_vol_up(void)554 int bt_vcp_vol_rend_vol_up(void)
555 {
556 	const struct vcs_control cp = {
557 		.opcode = BT_VCP_OPCODE_REL_VOL_UP,
558 		.counter = vol_rend.state.change_counter,
559 	};
560 	int err;
561 
562 	err = write_vcs_control(NULL, NULL, &cp, sizeof(cp), 0, 0);
563 
564 	return err > 0 ? 0 : err;
565 }
566 
bt_vcp_vol_rend_unmute_vol_down(void)567 int bt_vcp_vol_rend_unmute_vol_down(void)
568 {
569 	const struct vcs_control cp = {
570 		.opcode = BT_VCP_OPCODE_UNMUTE_REL_VOL_DOWN,
571 		.counter = vol_rend.state.change_counter,
572 	};
573 	int err;
574 
575 	err = write_vcs_control(NULL, NULL, &cp, sizeof(cp), 0, 0);
576 
577 	return err > 0 ? 0 : err;
578 }
579 
bt_vcp_vol_rend_unmute_vol_up(void)580 int bt_vcp_vol_rend_unmute_vol_up(void)
581 {
582 	const struct vcs_control cp = {
583 		.opcode = BT_VCP_OPCODE_UNMUTE_REL_VOL_UP,
584 		.counter = vol_rend.state.change_counter,
585 	};
586 	int err;
587 
588 	err = write_vcs_control(NULL, NULL, &cp, sizeof(cp), 0, 0);
589 
590 	return err > 0 ? 0 : err;
591 }
592 
bt_vcp_vol_rend_set_vol(uint8_t volume)593 int bt_vcp_vol_rend_set_vol(uint8_t volume)
594 {
595 	const struct vcs_control_vol cp = {
596 		.cp = {
597 			.opcode = BT_VCP_OPCODE_SET_ABS_VOL,
598 			.counter = vol_rend.state.change_counter
599 		},
600 		.volume = volume
601 	};
602 	int err;
603 
604 	err = write_vcs_control(NULL, NULL, &cp, sizeof(cp), 0, 0);
605 
606 	return err > 0 ? 0 : err;
607 }
608 
bt_vcp_vol_rend_unmute(void)609 int bt_vcp_vol_rend_unmute(void)
610 {
611 	const struct vcs_control cp = {
612 		.opcode = BT_VCP_OPCODE_UNMUTE,
613 		.counter = vol_rend.state.change_counter,
614 	};
615 	int err;
616 
617 	err = write_vcs_control(NULL, NULL, &cp, sizeof(cp), 0, 0);
618 
619 	return err > 0 ? 0 : err;
620 }
621 
bt_vcp_vol_rend_mute(void)622 int bt_vcp_vol_rend_mute(void)
623 {
624 	const struct vcs_control cp = {
625 		.opcode = BT_VCP_OPCODE_MUTE,
626 		.counter = vol_rend.state.change_counter,
627 	};
628 	int err;
629 
630 	err = write_vcs_control(NULL, NULL, &cp, sizeof(cp), 0, 0);
631 
632 	return err > 0 ? 0 : err;
633 }
634