1 /*
2  * Copyright 2020 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 #pragma once
19 
20 #include <queue>
21 #include <vector>
22 
23 #include "bta/include/bta_groups.h"
24 #include "osi/include/alarm.h"
25 #include "raw_address.h"
26 #include "types/bluetooth/uuid.h"
27 
28 namespace bluetooth {
29 namespace vc {
30 namespace internal {
31 
32 /* clang-format off */
33 /* Volume control point opcodes */
34 static constexpr uint8_t kControlPointOpcodeVolumeDown         = 0x00;
35 static constexpr uint8_t kControlPointOpcodeVolumeUp           = 0x01;
36 static constexpr uint8_t kControlPointOpcodeUnmuteVolumeDown   = 0x02;
37 static constexpr uint8_t kControlPointOpcodeUnmuteVolumeUp     = 0x03;
38 static constexpr uint8_t kControlPointOpcodeSetAbsoluteVolume  = 0x04;
39 static constexpr uint8_t kControlPointOpcodeUnmute             = 0x05;
40 static constexpr uint8_t kControlPointOpcodeMute               = 0x06;
41 
42 /* Volume offset control point opcodes */
43 static constexpr uint8_t kVolumeOffsetControlPointOpcodeSet                 = 0x01;
44 
45 /* Volume input control point opcodes */
46 static constexpr uint8_t kVolumeInputControlPointOpcodeSetGain              = 0x01;
47 static constexpr uint8_t kVolumeInputControlPointOpcodeUnmute               = 0x02;
48 static constexpr uint8_t kVolumeInputControlPointOpcodeMute                 = 0x03;
49 static constexpr uint8_t kVolumeInputControlPointOpcodeSetManualGainMode    = 0x04;
50 static constexpr uint8_t kVolumeInputControlPointOpcodeSetAutoGainMode      = 0x05;
51 
52 static const Uuid kVolumeControlUuid                  = Uuid::From16Bit(0x1844);
53 static const Uuid kVolumeControlStateUuid             = Uuid::From16Bit(0x2B7D);
54 static const Uuid kVolumeControlPointUuid             = Uuid::From16Bit(0x2B7E);
55 static const Uuid kVolumeFlagsUuid                    = Uuid::From16Bit(0x2B7F);
56 
57 static const Uuid kVolumeOffsetUuid                   = Uuid::From16Bit(0x1845);
58 static const Uuid kVolumeOffsetStateUuid              = Uuid::From16Bit(0x2B80);
59 static const Uuid kVolumeOffsetLocationUuid           = Uuid::From16Bit(0x2B81);
60 static const Uuid kVolumeOffsetControlPointUuid       = Uuid::From16Bit(0x2B82);
61 static const Uuid kVolumeOffsetOutputDescriptionUuid  = Uuid::From16Bit(0x2B83);
62 /* clang-format on */
63 
64 struct VolumeOperation {
65   int operation_id_;
66   int group_id_;
67 
68   bool started_;
69   bool is_autonomous_;
70 
71   uint8_t opcode_;
72   std::vector<uint8_t> arguments_;
73 
74   std::vector<RawAddress> devices_;
75   alarm_t* operation_timeout_;
76 
VolumeOperationVolumeOperation77   VolumeOperation(int operation_id, int group_id, bool is_autonomous, uint8_t opcode,
78                   std::vector<uint8_t> arguments,
79                   std::vector<RawAddress> devices)
80       : operation_id_(operation_id),
81         group_id_(group_id),
82         is_autonomous_(is_autonomous),
83         opcode_(opcode),
84         arguments_(arguments),
85         devices_(devices) {
86     auto name = "operation_timeout_" + std::to_string(operation_id);
87     operation_timeout_ = alarm_new(name.c_str());
88     started_ = false;
89   };
90 
~VolumeOperationVolumeOperation91   ~VolumeOperation() {
92     if (operation_timeout_ == nullptr) {
93       log::warn("operation_timeout_ should not be null, id {}, device cnt {}",
94                 operation_id_, devices_.size());
95       return;
96     }
97 
98     if (alarm_is_scheduled(operation_timeout_))
99       alarm_cancel(operation_timeout_);
100 
101     alarm_free(operation_timeout_);
102     operation_timeout_ = nullptr;
103   }
104 
IsGroupOperationVolumeOperation105   bool IsGroupOperation(void) {
106     return (group_id_ != bluetooth::groups::kGroupUnknown);
107   }
108 
IsStartedVolumeOperation109   bool IsStarted(void) { return started_; };
StartVolumeOperation110   void Start(void) { started_ = true; }
111 };
112 
113 struct VolumeOffset {
114   uint8_t id;
115   uint8_t change_counter;
116   int16_t offset;
117   uint32_t location;
118   uint16_t service_handle;
119   uint16_t state_handle;
120   uint16_t state_ccc_handle;
121   uint16_t audio_location_handle;
122   uint16_t audio_location_ccc_handle;
123   uint16_t audio_descr_handle;
124   uint16_t audio_descr_ccc_handle;
125   uint16_t control_point_handle;
126   bool audio_location_writable;
127   bool audio_descr_writable;
128 
VolumeOffsetVolumeOffset129   VolumeOffset(uint16_t service_handle)
130       : id(0),
131         change_counter(0),
132         offset(0),
133         location(0),
134         service_handle(service_handle),
135         state_handle(0),
136         state_ccc_handle(0),
137         audio_location_handle(0),
138         audio_location_ccc_handle(0),
139         audio_descr_handle(0),
140         audio_descr_ccc_handle(0),
141         control_point_handle(0),
142         audio_location_writable(false),
143         audio_descr_writable(false) {}
144 };
145 
146 class VolumeOffsets {
147  public:
Add(VolumeOffset & offset)148   void Add(VolumeOffset& offset) {
149     offset.id = (uint8_t)Size() + 1;
150     volume_offsets.push_back(offset);
151   }
152 
FindByLocation(uint8_t location)153   VolumeOffset* FindByLocation(uint8_t location) {
154     auto iter = std::find_if(volume_offsets.begin(), volume_offsets.end(),
155                              [&location](const VolumeOffset& item) {
156                                return item.location == location;
157                              });
158 
159     return (iter == volume_offsets.end()) ? nullptr : &(*iter);
160   }
161 
FindByServiceHandle(uint16_t service_handle)162   VolumeOffset* FindByServiceHandle(uint16_t service_handle) {
163     auto iter = std::find_if(volume_offsets.begin(), volume_offsets.end(),
164                              [&service_handle](const VolumeOffset& item) {
165                                return item.service_handle == service_handle;
166                              });
167 
168     return (iter == volume_offsets.end()) ? nullptr : &(*iter);
169   }
170 
FindById(uint16_t id)171   VolumeOffset* FindById(uint16_t id) {
172     auto iter =
173         std::find_if(volume_offsets.begin(), volume_offsets.end(),
174                      [&id](const VolumeOffset& item) { return item.id == id; });
175 
176     return (iter == volume_offsets.end()) ? nullptr : &(*iter);
177   }
178 
Clear()179   void Clear() { volume_offsets.clear(); }
180 
Size()181   size_t Size() { return volume_offsets.size(); }
182 
Dump(int fd)183   void Dump(int fd) {
184     std::stringstream stream;
185     int n = Size();
186     stream << "     == number of offsets: " << n << " == \n";
187 
188     for (int i = 0; i < n; i++) {
189       auto v = volume_offsets[i];
190       stream << "   id: " << +v.id << "\n"
191              << "    offset: " << +v.offset << "\n"
192              << "    changeCnt: " << +v.change_counter << "\n"
193              << "    location: " << +v.location << "\n"
194              << "    service_handle: " << +v.service_handle << "\n"
195              << "    audio_location_writable " << v.audio_location_writable
196              << "\n"
197              << "    audio_descr_writable: " << v.audio_descr_writable << "\n";
198     }
199     dprintf(fd, "%s", stream.str().c_str());
200   }
201 
202   std::vector<VolumeOffset> volume_offsets;
203 };
204 
205 }  // namespace internal
206 }  // namespace vc
207 }  // namespace bluetooth
208