• 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 namespace le_audio {
21 namespace has {
22 
ParsePresetGenericUpdate(uint16_t & len,const uint8_t * value,HasCtpNtf & ntf)23 static bool ParsePresetGenericUpdate(uint16_t& len, const uint8_t* value,
24                                      HasCtpNtf& ntf) {
25   if (len < sizeof(ntf.prev_index) + HasPreset::kCharValueMinSize) {
26     LOG(ERROR) << "Invalid preset value length=" << +len
27                << " for generic update.";
28     return false;
29   }
30 
31   STREAM_TO_UINT8(ntf.index, value);
32   len -= 1;
33 
34   ntf.preset = HasPreset::FromCharacteristicValue(len, value);
35   return true;
36 }
37 
ParsePresetIndex(uint16_t & len,const uint8_t * value,HasCtpNtf & ntf)38 static bool ParsePresetIndex(uint16_t& len, const uint8_t* value,
39                              HasCtpNtf& ntf) {
40   if (len < sizeof(ntf.index)) {
41     LOG(ERROR) << __func__ << "Invalid preset value length=" << +len
42                << " for generic update.";
43     return false;
44   }
45 
46   STREAM_TO_UINT8(ntf.index, value);
47   len -= 1;
48   return true;
49 }
50 
ParsePresetReadResponse(uint16_t & len,const uint8_t * value,HasCtpNtf & ntf)51 static bool ParsePresetReadResponse(uint16_t& len, const uint8_t* value,
52                                     HasCtpNtf& ntf) {
53   if (len < sizeof(ntf.is_last) + HasPreset::kCharValueMinSize) {
54     LOG(ERROR) << "Invalid preset value length=" << +len;
55     return false;
56   }
57 
58   STREAM_TO_UINT8(ntf.is_last, value);
59   len -= 1;
60 
61   ntf.preset = HasPreset::FromCharacteristicValue(len, value);
62   return true;
63 }
64 
ParsePresetChanged(uint16_t len,const uint8_t * value,HasCtpNtf & ntf)65 static bool ParsePresetChanged(uint16_t len, const uint8_t* value,
66                                HasCtpNtf& ntf) {
67   if (len < sizeof(ntf.is_last) + sizeof(ntf.change_id)) {
68     LOG(ERROR) << __func__ << "Invalid preset value length=" << +len;
69     return false;
70   }
71 
72   uint8_t change_id;
73   STREAM_TO_UINT8(change_id, value);
74   len -= 1;
75   if (change_id > static_cast<std::underlying_type_t<PresetCtpChangeId>>(
76                       PresetCtpChangeId::CHANGE_ID_MAX_)) {
77     LOG(ERROR) << __func__ << "Invalid preset chenge_id=" << change_id;
78     return false;
79   }
80   ntf.change_id = PresetCtpChangeId(change_id);
81   STREAM_TO_UINT8(ntf.is_last, value);
82   len -= 1;
83 
84   switch (ntf.change_id) {
85     case PresetCtpChangeId::PRESET_GENERIC_UPDATE:
86       return ParsePresetGenericUpdate(len, value, ntf);
87     case PresetCtpChangeId::PRESET_AVAILABLE:
88       return ParsePresetIndex(len, value, ntf);
89     case PresetCtpChangeId::PRESET_UNAVAILABLE:
90       return ParsePresetIndex(len, value, ntf);
91     case PresetCtpChangeId::PRESET_DELETED:
92       return ParsePresetIndex(len, value, ntf);
93     default:
94       return false;
95   }
96 
97   return true;
98 }
99 
FromCharacteristicValue(uint16_t len,const uint8_t * value)100 std::optional<HasCtpNtf> HasCtpNtf::FromCharacteristicValue(
101     uint16_t len, const uint8_t* value) {
102   if (len < 3) {
103     LOG(ERROR) << __func__ << " Invalid Cp notification.";
104     return std::nullopt;
105   }
106 
107   uint8_t op;
108   STREAM_TO_UINT8(op, value);
109   --len;
110 
111   if ((op != static_cast<std::underlying_type_t<PresetCtpOpcode>>(
112                  PresetCtpOpcode::READ_PRESET_RESPONSE)) &&
113       (op != static_cast<std::underlying_type_t<PresetCtpOpcode>>(
114                  PresetCtpOpcode::PRESET_CHANGED))) {
115     LOG(ERROR) << __func__
116                << ": Received invalid opcode in control point notification: "
117                << ++op;
118     return std::nullopt;
119   }
120 
121   HasCtpNtf ntf;
122   ntf.opcode = PresetCtpOpcode(op);
123   if (ntf.opcode == le_audio::has::PresetCtpOpcode::PRESET_CHANGED) {
124     if (!ParsePresetChanged(len, value, ntf)) return std::nullopt;
125 
126   } else if (ntf.opcode ==
127              le_audio::has::PresetCtpOpcode::READ_PRESET_RESPONSE) {
128     if (!ParsePresetReadResponse(len, value, ntf)) return std::nullopt;
129   }
130 
131   return ntf;
132 }
133 
134 uint16_t HasCtpOp::last_op_id_ = 0;
135 
ToCharacteristicValue() const136 std::vector<uint8_t> HasCtpOp::ToCharacteristicValue() const {
137   std::vector<uint8_t> value;
138   auto* pp = value.data();
139 
140   switch (opcode) {
141     case PresetCtpOpcode::READ_PRESETS:
142       value.resize(3);
143       pp = value.data();
144       UINT8_TO_STREAM(
145           pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
146       UINT8_TO_STREAM(pp, index);
147       UINT8_TO_STREAM(pp, num_of_indices);
148       break;
149     case PresetCtpOpcode::SET_ACTIVE_PRESET:
150     case PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC:
151       value.resize(2);
152       pp = value.data();
153       UINT8_TO_STREAM(
154           pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
155       UINT8_TO_STREAM(pp, index);
156       break;
157 
158     case PresetCtpOpcode::SET_NEXT_PRESET:
159     case PresetCtpOpcode::SET_NEXT_PRESET_SYNC:
160     case PresetCtpOpcode::SET_PREV_PRESET:
161     case PresetCtpOpcode::SET_PREV_PRESET_SYNC:
162       value.resize(1);
163       pp = value.data();
164       UINT8_TO_STREAM(
165           pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
166       break;
167 
168     case PresetCtpOpcode::WRITE_PRESET_NAME: {
169       auto name_str = name.value_or("");
170       value.resize(2 + name_str.length());
171       pp = value.data();
172 
173       UINT8_TO_STREAM(
174           pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
175       UINT8_TO_STREAM(pp, index);
176       memcpy(pp, name_str.c_str(), name_str.length());
177     } break;
178 
179     default:
180       LOG_ASSERT(false) << __func__ << "Bad control point operation!";
181       break;
182   }
183 
184   return value;
185 }
186 
187 #define CASE_SET_PTR_TO_TOKEN_STR(en) \
188   case (en):                          \
189     ch = #en;                         \
190     break;
191 
operator <<(std::ostream & out,const PresetCtpChangeId value)192 std::ostream& operator<<(std::ostream& out, const PresetCtpChangeId value) {
193   const char* ch = 0;
194   switch (value) {
195     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_GENERIC_UPDATE);
196     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_DELETED);
197     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_AVAILABLE);
198     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_UNAVAILABLE);
199     default:
200       ch = "INVALID_CHANGE_ID";
201       break;
202   }
203   return out << ch;
204 }
205 
operator <<(std::ostream & out,const PresetCtpOpcode value)206 std::ostream& operator<<(std::ostream& out, const PresetCtpOpcode value) {
207   const char* ch = 0;
208   switch (value) {
209     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::READ_PRESETS);
210     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::READ_PRESET_RESPONSE);
211     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::PRESET_CHANGED);
212     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::WRITE_PRESET_NAME);
213     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_ACTIVE_PRESET);
214     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_NEXT_PRESET);
215     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_PREV_PRESET);
216     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC);
217     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_NEXT_PRESET_SYNC);
218     CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_PREV_PRESET_SYNC);
219     default:
220       ch = "NOT_A_VALID_OPCODE";
221       break;
222   }
223   return out << ch;
224 }
225 #undef SET_CH_TO_TOKENIZED
226 
operator <<(std::ostream & out,const HasCtpOp & op)227 std::ostream& operator<<(std::ostream& out, const HasCtpOp& op) {
228   out << "\"HasCtpOp\": {";
229   if (std::holds_alternative<int>(op.addr_or_group)) {
230     out << "\"group_id\": " << std::get<int>(op.addr_or_group);
231   } else if (std::holds_alternative<RawAddress>(op.addr_or_group)) {
232     out << "\"address\": \"" << std::get<RawAddress>(op.addr_or_group) << "\"";
233   } else {
234     out << "\"bad value\"";
235   }
236   out << ", \"id\": " << op.op_id << ", \"opcode\": \"" << op.opcode << "\""
237       << ", \"index\": " << +op.index << ", \"name\": \""
238       << op.name.value_or("<none>") << "\""
239       << "}";
240   return out;
241 }
242 
operator <<(std::ostream & out,const HasCtpNtf & ntf)243 std::ostream& operator<<(std::ostream& out, const HasCtpNtf& ntf) {
244   out << "\"HasCtpNtf\": {";
245   out << "\"opcode\": \"" << ntf.opcode << "\"";
246 
247   if (ntf.opcode == PresetCtpOpcode::READ_PRESET_RESPONSE) {
248     out << ", \"is_last\": " << (ntf.is_last ? "\"True\"" : "\"False\"");
249     if (ntf.preset.has_value()) {
250       out << ", \"preset\": " << ntf.preset.value();
251     } else {
252       out << ", \"preset\": \"None\"";
253     }
254 
255   } else if (ntf.opcode == PresetCtpOpcode::PRESET_CHANGED) {
256     out << ", \"change_id\": " << ntf.change_id;
257     out << ", \"is_last\": " << (ntf.is_last ? "\"True\"" : "\"False\"");
258     switch (ntf.change_id) {
259       case PresetCtpChangeId::PRESET_GENERIC_UPDATE:
260         out << ", \"prev_index\": " << +ntf.prev_index;
261         if (ntf.preset.has_value()) {
262           out << ", \"preset\": {" << ntf.preset.value() << "}";
263         } else {
264           out << ", \"preset\": \"None\"";
265         }
266         break;
267       case PresetCtpChangeId::PRESET_DELETED:
268         FALLTHROUGH;
269       case PresetCtpChangeId::PRESET_AVAILABLE:
270         FALLTHROUGH;
271       case PresetCtpChangeId::PRESET_UNAVAILABLE:
272         out << ", \"index\": " << +ntf.index;
273         break;
274       default:
275         break;
276     }
277   }
278   out << "}";
279 
280   return out;
281 }
282 
283 }  // namespace has
284 }  // namespace le_audio
285