1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/common/device_class.h"
16
17 #include <algorithm>
18 #include <climits>
19
20 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
21
22 namespace bt {
23
24 namespace {
25
bit_no_to_service_class(uint8_t bit_no)26 DeviceClass::ServiceClass bit_no_to_service_class(uint8_t bit_no) {
27 PW_DCHECK(bit_no >= 13);
28 PW_DCHECK(bit_no < 24);
29 switch (bit_no) {
30 case 13:
31 return DeviceClass::ServiceClass::kLimitedDiscoverableMode;
32 case 14:
33 return DeviceClass::ServiceClass::kLEAudio;
34 case 15:
35 return DeviceClass::ServiceClass::kReserved;
36 case 16:
37 return DeviceClass::ServiceClass::kPositioning;
38 case 17:
39 return DeviceClass::ServiceClass::kNetworking;
40 case 18:
41 return DeviceClass::ServiceClass::kRendering;
42 case 19:
43 return DeviceClass::ServiceClass::kCapturing;
44 case 20:
45 return DeviceClass::ServiceClass::kObjectTransfer;
46 case 21:
47 return DeviceClass::ServiceClass::kAudio;
48 case 22:
49 return DeviceClass::ServiceClass::kTelephony;
50 case 23:
51 return DeviceClass::ServiceClass::kInformation;
52 };
53 // Should be unreachable.
54 return DeviceClass::ServiceClass::kInformation;
55 }
56
service_class_to_string(const DeviceClass::ServiceClass & serv)57 std::string service_class_to_string(const DeviceClass::ServiceClass& serv) {
58 switch (serv) {
59 case DeviceClass::ServiceClass::kLimitedDiscoverableMode:
60 return "Limited Discoverable Mode";
61 case DeviceClass::ServiceClass::kLEAudio:
62 return "LE Audio";
63 case DeviceClass::ServiceClass::kReserved:
64 return "Reserved";
65 case DeviceClass::ServiceClass::kPositioning:
66 return "Positioning";
67 case DeviceClass::ServiceClass::kNetworking:
68 return "Networking";
69 case DeviceClass::ServiceClass::kRendering:
70 return "Rendering";
71 case DeviceClass::ServiceClass::kCapturing:
72 return "Capturing";
73 case DeviceClass::ServiceClass::kObjectTransfer:
74 return "Object Transfer";
75 case DeviceClass::ServiceClass::kAudio:
76 return "Audio";
77 case DeviceClass::ServiceClass::kTelephony:
78 return "Telephony";
79 case DeviceClass::ServiceClass::kInformation:
80 return "Information";
81 }
82 }
83
84 } // namespace
85
DeviceClass()86 DeviceClass::DeviceClass() : DeviceClass(MajorClass::kUnspecified) {}
87
DeviceClass(MajorClass major_class)88 DeviceClass::DeviceClass(MajorClass major_class)
89 : bytes_{0x00, static_cast<uint8_t>(major_class), 0x00} {}
90
DeviceClass(std::initializer_list<uint8_t> bytes)91 DeviceClass::DeviceClass(std::initializer_list<uint8_t> bytes) {
92 PW_DCHECK(bytes.size() == bytes_.size());
93 std::copy(bytes.begin(), bytes.end(), bytes_.begin());
94 }
95
DeviceClass(uint32_t value)96 DeviceClass::DeviceClass(uint32_t value) {
97 PW_DCHECK(value < 1 << 24); // field should only populate 24 bits
98 bytes_ = {
99 static_cast<uint8_t>((value >> 0) & 0xFF),
100 static_cast<uint8_t>((value >> 8) & 0xFF),
101 static_cast<uint8_t>((value >> 16) & 0xFF),
102 };
103 }
104
to_int() const105 uint32_t DeviceClass::to_int() const {
106 uint32_t out = 0;
107 out |= bytes_[0];
108 out |= bytes_[1] << CHAR_BIT;
109 out |= bytes_[2] << 2 * CHAR_BIT;
110 return out;
111 }
112
SetServiceClasses(const std::unordered_set<ServiceClass> & classes)113 void DeviceClass::SetServiceClasses(
114 const std::unordered_set<ServiceClass>& classes) {
115 for (const auto& c : classes) {
116 uint8_t bit = static_cast<uint8_t>(c);
117 if (bit >= 16) {
118 bytes_[2] |= 0x01 << (bit - 16);
119 } else if (bit >= 8) {
120 bytes_[1] |= 0x01 << (bit - 8);
121 }
122 }
123 }
124
GetServiceClasses() const125 std::unordered_set<DeviceClass::ServiceClass> DeviceClass::GetServiceClasses()
126 const {
127 std::unordered_set<ServiceClass> classes;
128 for (uint8_t bit_no = 13; bit_no < 16; bit_no++) {
129 if (bytes_[1] & (0x01 << (bit_no - 8))) {
130 classes.emplace(bit_no_to_service_class(bit_no));
131 }
132 }
133 for (uint8_t bit_no = 16; bit_no < 24; bit_no++) {
134 if (bytes_[2] & (0x01 << (bit_no - 16))) {
135 classes.emplace(bit_no_to_service_class(bit_no));
136 }
137 }
138 return classes;
139 }
140
ToString() const141 std::string DeviceClass::ToString() const {
142 std::string service_desc;
143 auto classes = GetServiceClasses();
144 if (!classes.empty()) {
145 auto it = classes.begin();
146 service_desc = " (" + service_class_to_string(*it);
147 ++it;
148 for (; it != classes.end(); ++it) {
149 service_desc += ", " + service_class_to_string(*it);
150 }
151 service_desc = service_desc + ")";
152 }
153 switch (major_class()) {
154 case MajorClass::kMiscellaneous:
155 return "Miscellaneous" + service_desc;
156 case MajorClass::kComputer:
157 return "Computer" + service_desc;
158 case MajorClass::kPhone:
159 return "Phone" + service_desc;
160 case MajorClass::kLAN:
161 return "LAN" + service_desc;
162 case MajorClass::kAudioVideo:
163 return "A/V" + service_desc;
164 case MajorClass::kPeripheral:
165 return "Peripheral" + service_desc;
166 case MajorClass::kImaging:
167 return "Imaging" + service_desc;
168 case MajorClass::kWearable:
169 return "Wearable" + service_desc;
170 case MajorClass::kToy:
171 return "Toy" + service_desc;
172 case MajorClass::kHealth:
173 return "Health Device" + service_desc;
174 case MajorClass::kUnspecified:
175 return "Unspecified" + service_desc;
176 };
177
178 return "(unknown)";
179 }
180
181 } // namespace bt
182