1 /** @file
2  *  @brief Bluetooth Volume Control Profile (VCP) Volume Renderer role.
3  *
4  *  Copyright (c) 2020 Bose Corporation
5  *  Copyright (c) 2020-2022 Nordic Semiconductor ASA
6  *  Copyright (c) 2022 Codecoup
7  *
8  *  SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/sys/printk.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 
16 #include <zephyr/bluetooth/conn.h>
17 #include <zephyr/bluetooth/audio/vcp.h>
18 
19 static struct bt_vcp_included vcp_included;
20 
vcs_state_cb(struct bt_conn * conn,int err,uint8_t volume,uint8_t mute)21 static void vcs_state_cb(struct bt_conn *conn, int err, uint8_t volume, uint8_t mute)
22 {
23 	if (err) {
24 		printk("VCS state get failed (%d)\n", err);
25 	} else {
26 		printk("VCS volume %u, mute %u\n", volume, mute);
27 	}
28 }
29 
vcs_flags_cb(struct bt_conn * conn,int err,uint8_t flags)30 static void vcs_flags_cb(struct bt_conn *conn, int err, uint8_t flags)
31 {
32 	if (err) {
33 		printk("VCS flags get failed (%d)\n", err);
34 	} else {
35 		printk("VCS flags 0x%02X\n", flags);
36 	}
37 }
38 
aics_state_cb(struct bt_aics * inst,int err,int8_t gain,uint8_t mute,uint8_t mode)39 static void aics_state_cb(struct bt_aics *inst, int err, int8_t gain, uint8_t mute, uint8_t mode)
40 {
41 	if (err) {
42 		printk("AICS state get failed (%d) for inst %p\n", err, inst);
43 	} else {
44 		printk("AICS inst %p state gain %d, mute %u, mode %u\n",
45 		       inst, gain, mute, mode);
46 	}
47 }
48 
aics_gain_setting_cb(struct bt_aics * inst,int err,uint8_t units,int8_t minimum,int8_t maximum)49 static void aics_gain_setting_cb(struct bt_aics *inst, int err, uint8_t units, int8_t minimum,
50 				 int8_t maximum)
51 {
52 	if (err) {
53 		printk("AICS gain settings get failed (%d) for inst %p\n", err, inst);
54 	} else {
55 		printk("AICS inst %p gain settings units %u, min %d, max %d\n",
56 		       inst, units, minimum, maximum);
57 	}
58 }
59 
aics_input_type_cb(struct bt_aics * inst,int err,uint8_t input_type)60 static void aics_input_type_cb(struct bt_aics *inst, int err, uint8_t input_type)
61 {
62 	if (err) {
63 		printk("AICS input type get failed (%d) for inst %p\n", err, inst);
64 	} else {
65 		printk("AICS inst %p input type %u\n", inst, input_type);
66 	}
67 }
68 
aics_status_cb(struct bt_aics * inst,int err,bool active)69 static void aics_status_cb(struct bt_aics *inst, int err, bool active)
70 {
71 	if (err) {
72 		printk("AICS status get failed (%d) for inst %p\n", err, inst);
73 	} else {
74 		printk("AICS inst %p status %s\n", inst, active ? "active" : "inactive");
75 	}
76 
77 }
aics_description_cb(struct bt_aics * inst,int err,char * description)78 static void aics_description_cb(struct bt_aics *inst, int err, char *description)
79 {
80 	if (err) {
81 		printk("AICS description get failed (%d) for inst %p\n", err, inst);
82 	} else {
83 		printk("AICS inst %p description %s\n", inst, description);
84 	}
85 }
vocs_state_cb(struct bt_vocs * inst,int err,int16_t offset)86 static void vocs_state_cb(struct bt_vocs *inst, int err, int16_t offset)
87 {
88 	if (err) {
89 		printk("VOCS state get failed (%d) for inst %p\n", err, inst);
90 	} else {
91 		printk("VOCS inst %p offset %d\n", inst, offset);
92 	}
93 }
94 
vocs_location_cb(struct bt_vocs * inst,int err,uint32_t location)95 static void vocs_location_cb(struct bt_vocs *inst, int err, uint32_t location)
96 {
97 	if (err) {
98 		printk("VOCS location get failed (%d) for inst %p\n", err, inst);
99 	} else {
100 		printk("VOCS inst %p location %u\n", inst, location);
101 	}
102 }
103 
vocs_description_cb(struct bt_vocs * inst,int err,char * description)104 static void vocs_description_cb(struct bt_vocs *inst, int err, char *description)
105 {
106 	if (err) {
107 		printk("VOCS description get failed (%d) for inst %p\n", err, inst);
108 	} else {
109 		printk("VOCS inst %p description %s\n", inst, description);
110 	}
111 }
112 
113 static struct bt_vcp_vol_rend_cb vcp_cbs = {
114 	.state = vcs_state_cb,
115 	.flags = vcs_flags_cb,
116 };
117 
118 static struct bt_aics_cb aics_cbs = {
119 	.state = aics_state_cb,
120 	.gain_setting = aics_gain_setting_cb,
121 	.type = aics_input_type_cb,
122 	.status = aics_status_cb,
123 	.description = aics_description_cb
124 };
125 
126 static struct bt_vocs_cb vocs_cbs = {
127 	.state = vocs_state_cb,
128 	.location = vocs_location_cb,
129 	.description = vocs_description_cb
130 };
131 
vcp_vol_renderer_init(void)132 int vcp_vol_renderer_init(void)
133 {
134 	int err;
135 	struct bt_vcp_vol_rend_register_param vcp_register_param;
136 	char input_desc[CONFIG_BT_VCP_VOL_REND_AICS_INSTANCE_COUNT][16];
137 	char output_desc[CONFIG_BT_VCP_VOL_REND_VOCS_INSTANCE_COUNT][16];
138 
139 	memset(&vcp_register_param, 0, sizeof(vcp_register_param));
140 
141 	for (int i = 0; i < ARRAY_SIZE(vcp_register_param.vocs_param); i++) {
142 		vcp_register_param.vocs_param[i].location_writable = true;
143 		vcp_register_param.vocs_param[i].desc_writable = true;
144 		snprintf(output_desc[i], sizeof(output_desc[i]), "Output %d", i + 1);
145 		vcp_register_param.vocs_param[i].output_desc = output_desc[i];
146 		vcp_register_param.vocs_param[i].cb = &vocs_cbs;
147 	}
148 
149 	for (int i = 0; i < ARRAY_SIZE(vcp_register_param.aics_param); i++) {
150 		vcp_register_param.aics_param[i].desc_writable = true;
151 		snprintf(input_desc[i], sizeof(input_desc[i]), "Input %d", i + 1);
152 		vcp_register_param.aics_param[i].description = input_desc[i];
153 		vcp_register_param.aics_param[i].type = BT_AICS_INPUT_TYPE_UNSPECIFIED;
154 		vcp_register_param.aics_param[i].status = true;
155 		vcp_register_param.aics_param[i].gain_mode = BT_AICS_MODE_MANUAL;
156 		vcp_register_param.aics_param[i].units = 1;
157 		vcp_register_param.aics_param[i].min_gain = -100;
158 		vcp_register_param.aics_param[i].max_gain = 100;
159 		vcp_register_param.aics_param[i].cb = &aics_cbs;
160 	}
161 
162 	vcp_register_param.step = 1;
163 	vcp_register_param.mute = BT_VCP_STATE_UNMUTED;
164 	vcp_register_param.volume = 100;
165 	vcp_register_param.cb = &vcp_cbs;
166 
167 	err = bt_vcp_vol_rend_register(&vcp_register_param);
168 	if (err) {
169 		return err;
170 	}
171 
172 	err = bt_vcp_vol_rend_included_get(&vcp_included);
173 	if (err != 0) {
174 		return err;
175 	}
176 
177 	return 0;
178 }
179