• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 HIMSA II K/S - www.himsa.com.
3  * Represented by EHIMA - www.ehima.com
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include "has_ctp.h"
19 
20 #include <bluetooth/log.h>
21 
22 #include <cstdint>
23 #include <cstring>
24 #include <optional>
25 #include <ostream>
26 #include <type_traits>
27 #include <variant>
28 #include <vector>
29 
30 #include "has_preset.h"
31 #include "stack/include/bt_types.h"
32 #include "types/raw_address.h"
33 
34 using namespace bluetooth;
35 
36 namespace bluetooth::le_audio {
37 namespace has {
38 
ParsePresetGenericUpdate(uint16_t & len,const uint8_t * value,HasCtpNtf & ntf)39 static bool ParsePresetGenericUpdate(uint16_t& len, const uint8_t* value, HasCtpNtf& ntf) {
40   if (len < sizeof(ntf.prev_index) + HasPreset::kCharValueMinSize) {
41     log::error("Invalid preset value length={} for generic update.", len);
42     return false;
43   }
44 
45   STREAM_TO_UINT8(ntf.index, value);
46   len -= 1;
47 
48   ntf.preset = HasPreset::FromCharacteristicValue(len, value);
49   return true;
50 }
51 
ParsePresetIndex(uint16_t & len,const uint8_t * value,HasCtpNtf & ntf)52 static bool ParsePresetIndex(uint16_t& len, const uint8_t* value, HasCtpNtf& ntf) {
53   if (len < sizeof(ntf.index)) {
54     log::error("Invalid preset value length={} for generic update.", len);
55     return false;
56   }
57 
58   STREAM_TO_UINT8(ntf.index, value);
59   len -= 1;
60   return true;
61 }
62 
ParsePresetReadResponse(uint16_t & len,const uint8_t * value,HasCtpNtf & ntf)63 static bool ParsePresetReadResponse(uint16_t& len, const uint8_t* value, HasCtpNtf& ntf) {
64   if (len < sizeof(ntf.is_last) + HasPreset::kCharValueMinSize) {
65     log::error("Invalid preset value length={}", len);
66     return false;
67   }
68 
69   STREAM_TO_UINT8(ntf.is_last, value);
70   len -= 1;
71 
72   ntf.preset = HasPreset::FromCharacteristicValue(len, value);
73   return true;
74 }
75 
ParsePresetChanged(uint16_t len,const uint8_t * value,HasCtpNtf & ntf)76 static bool ParsePresetChanged(uint16_t len, const uint8_t* value, HasCtpNtf& ntf) {
77   if (len < sizeof(ntf.is_last) + sizeof(ntf.change_id)) {
78     log::error("Invalid preset value length={}", len);
79     return false;
80   }
81 
82   uint8_t change_id;
83   STREAM_TO_UINT8(change_id, value);
84   len -= 1;
85   if (change_id >
86       static_cast<std::underlying_type_t<PresetCtpChangeId>>(PresetCtpChangeId::CHANGE_ID_MAX_)) {
87     log::error("Invalid preset chenge_id={}", change_id);
88     return false;
89   }
90   ntf.change_id = PresetCtpChangeId(change_id);
91   STREAM_TO_UINT8(ntf.is_last, value);
92   len -= 1;
93 
94   switch (ntf.change_id) {
95     case PresetCtpChangeId::PRESET_GENERIC_UPDATE:
96       return ParsePresetGenericUpdate(len, value, ntf);
97     case PresetCtpChangeId::PRESET_AVAILABLE:
98       return ParsePresetIndex(len, value, ntf);
99     case PresetCtpChangeId::PRESET_UNAVAILABLE:
100       return ParsePresetIndex(len, value, ntf);
101     case PresetCtpChangeId::PRESET_DELETED:
102       return ParsePresetIndex(len, value, ntf);
103     default:
104       return false;
105   }
106 
107   return true;
108 }
109 
FromCharacteristicValue(uint16_t len,const uint8_t * value)110 std::optional<HasCtpNtf> HasCtpNtf::FromCharacteristicValue(uint16_t len, const uint8_t* value) {
111   if (len < 3) {
112     log::error("Invalid Cp notification.");
113     return std::nullopt;
114   }
115 
116   uint8_t op;
117   STREAM_TO_UINT8(op, value);
118   --len;
119 
120   if ((op != static_cast<std::underlying_type_t<PresetCtpOpcode>>(
121                      PresetCtpOpcode::READ_PRESET_RESPONSE)) &&
122       (op !=
123        static_cast<std::underlying_type_t<PresetCtpOpcode>>(PresetCtpOpcode::PRESET_CHANGED))) {
124     log::error("Received invalid opcode in control point notification: {}", op);
125     return std::nullopt;
126   }
127 
128   HasCtpNtf ntf;
129   ntf.opcode = PresetCtpOpcode(op);
130   if (ntf.opcode == bluetooth::le_audio::has::PresetCtpOpcode::PRESET_CHANGED) {
131     if (!ParsePresetChanged(len, value, ntf)) {
132       return std::nullopt;
133     }
134 
135   } else if (ntf.opcode == bluetooth::le_audio::has::PresetCtpOpcode::READ_PRESET_RESPONSE) {
136     if (!ParsePresetReadResponse(len, value, ntf)) {
137       return std::nullopt;
138     }
139   }
140 
141   return ntf;
142 }
143 
144 uint16_t HasCtpOp::last_op_id_ = 0;
145 
ToCharacteristicValue() const146 std::vector<uint8_t> HasCtpOp::ToCharacteristicValue() const {
147   std::vector<uint8_t> value;
148   auto* pp = value.data();
149 
150   switch (opcode) {
151     case PresetCtpOpcode::READ_PRESETS:
152       value.resize(3);
153       pp = value.data();
154       UINT8_TO_STREAM(pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
155       UINT8_TO_STREAM(pp, index);
156       UINT8_TO_STREAM(pp, num_of_indices);
157       break;
158     case PresetCtpOpcode::SET_ACTIVE_PRESET:
159     case PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC:
160       value.resize(2);
161       pp = value.data();
162       UINT8_TO_STREAM(pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
163       UINT8_TO_STREAM(pp, index);
164       break;
165 
166     case PresetCtpOpcode::SET_NEXT_PRESET:
167     case PresetCtpOpcode::SET_NEXT_PRESET_SYNC:
168     case PresetCtpOpcode::SET_PREV_PRESET:
169     case PresetCtpOpcode::SET_PREV_PRESET_SYNC:
170       value.resize(1);
171       pp = value.data();
172       UINT8_TO_STREAM(pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
173       break;
174 
175     case PresetCtpOpcode::WRITE_PRESET_NAME: {
176       auto name_str = name.value_or("");
177       value.resize(2 + name_str.length());
178       pp = value.data();
179 
180       UINT8_TO_STREAM(pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
181       UINT8_TO_STREAM(pp, index);
182       memcpy(pp, name_str.c_str(), name_str.length());
183     } break;
184 
185     default:
186       log::fatal("Bad control point operation!");
187       break;
188   }
189 
190   return value;
191 }
192 
193 #define CASE_SET_PTR_TO_TOKEN_STR(en) \
194   case (en):                          \
195     ch = #en;                         \
196     break;
197 
operator <<(std::ostream & out,const PresetCtpChangeId value)198 std::ostream& operator<<(std::ostream& out, const PresetCtpChangeId value) {
199   const char* ch = 0;
200   switch (value) {
201     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_GENERIC_UPDATE);
202     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_DELETED);
203     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_AVAILABLE);
204     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_UNAVAILABLE);
205     default:
206       ch = "INVALID_CHANGE_ID";
207       break;
208   }
209   return out << ch;
210 }
211 
operator <<(std::ostream & out,const PresetCtpOpcode value)212 std::ostream& operator<<(std::ostream& out, const PresetCtpOpcode value) {
213   const char* ch = 0;
214   switch (value) {
215     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::READ_PRESETS);
216     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::READ_PRESET_RESPONSE);
217     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::PRESET_CHANGED);
218     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::WRITE_PRESET_NAME);
219     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_ACTIVE_PRESET);
220     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_NEXT_PRESET);
221     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_PREV_PRESET);
222     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC);
223     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_NEXT_PRESET_SYNC);
224     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_PREV_PRESET_SYNC);
225     default:
226       ch = "NOT_A_VALID_OPCODE";
227       break;
228   }
229   return out << ch;
230 }
231 #undef SET_CH_TO_TOKENIZED
232 
operator <<(std::ostream & out,const HasCtpOp & op)233 std::ostream& operator<<(std::ostream& out, const HasCtpOp& op) {
234   out << "\"HasCtpOp\": {";
235   if (std::holds_alternative<int>(op.addr_or_group)) {
236     out << "\"group_id\": " << std::get<int>(op.addr_or_group);
237   } else if (std::holds_alternative<RawAddress>(op.addr_or_group)) {
238     out << "\"address\": \"" << std::get<RawAddress>(op.addr_or_group).ToRedactedStringForLogging()
239         << "\"";
240   } else {
241     out << "\"bad value\"";
242   }
243   out << ", \"id\": " << op.op_id << ", \"opcode\": \"" << op.opcode << "\""
244       << ", \"index\": " << +op.index << ", \"name\": \"" << op.name.value_or("<none>") << "\""
245       << "}";
246   return out;
247 }
248 
operator <<(std::ostream & out,const HasCtpNtf & ntf)249 std::ostream& operator<<(std::ostream& out, const HasCtpNtf& ntf) {
250   out << "\"HasCtpNtf\": {";
251   out << "\"opcode\": \"" << ntf.opcode << "\"";
252 
253   if (ntf.opcode == PresetCtpOpcode::READ_PRESET_RESPONSE) {
254     out << ", \"is_last\": " << (ntf.is_last ? "\"True\"" : "\"False\"");
255     if (ntf.preset.has_value()) {
256       out << ", \"preset\": " << ntf.preset.value();
257     } else {
258       out << ", \"preset\": \"None\"";
259     }
260 
261   } else if (ntf.opcode == PresetCtpOpcode::PRESET_CHANGED) {
262     out << ", \"change_id\": " << ntf.change_id;
263     out << ", \"is_last\": " << (ntf.is_last ? "\"True\"" : "\"False\"");
264     switch (ntf.change_id) {
265       case PresetCtpChangeId::PRESET_GENERIC_UPDATE:
266         out << ", \"prev_index\": " << +ntf.prev_index;
267         if (ntf.preset.has_value()) {
268           out << ", \"preset\": {" << ntf.preset.value() << "}";
269         } else {
270           out << ", \"preset\": \"None\"";
271         }
272         break;
273       case PresetCtpChangeId::PRESET_DELETED:
274       case PresetCtpChangeId::PRESET_AVAILABLE:
275       case PresetCtpChangeId::PRESET_UNAVAILABLE:
276         out << ", \"index\": " << +ntf.index;
277         break;
278       default:
279         break;
280     }
281   }
282   out << "}";
283 
284   return out;
285 }
286 
287 }  // namespace has
288 }  // namespace bluetooth::le_audio
289