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 "pw_bluetooth_sapphire/internal/host/common/assert.h"
18
19 namespace bt {
20
21 namespace {
22
bit_no_to_service_class(uint8_t bit_no)23 DeviceClass::ServiceClass bit_no_to_service_class(uint8_t bit_no) {
24 BT_DEBUG_ASSERT(bit_no >= 13);
25 BT_DEBUG_ASSERT(bit_no < 24);
26 switch (bit_no) {
27 case 13:
28 return DeviceClass::ServiceClass::kLimitedDiscoverableMode;
29 case 14:
30 return DeviceClass::ServiceClass::kLEAudio;
31 case 15:
32 return DeviceClass::ServiceClass::kReserved;
33 case 16:
34 return DeviceClass::ServiceClass::kPositioning;
35 case 17:
36 return DeviceClass::ServiceClass::kNetworking;
37 case 18:
38 return DeviceClass::ServiceClass::kRendering;
39 case 19:
40 return DeviceClass::ServiceClass::kCapturing;
41 case 20:
42 return DeviceClass::ServiceClass::kObjectTransfer;
43 case 21:
44 return DeviceClass::ServiceClass::kAudio;
45 case 22:
46 return DeviceClass::ServiceClass::kTelephony;
47 case 23:
48 return DeviceClass::ServiceClass::kInformation;
49 };
50 // Should be unreachable.
51 return DeviceClass::ServiceClass::kInformation;
52 }
53
service_class_to_string(const DeviceClass::ServiceClass & serv)54 std::string service_class_to_string(const DeviceClass::ServiceClass& serv) {
55 switch (serv) {
56 case DeviceClass::ServiceClass::kLimitedDiscoverableMode:
57 return "Limited Discoverable Mode";
58 case DeviceClass::ServiceClass::kLEAudio:
59 return "LE Audio";
60 case DeviceClass::ServiceClass::kReserved:
61 return "Reserved";
62 case DeviceClass::ServiceClass::kPositioning:
63 return "Positioning";
64 case DeviceClass::ServiceClass::kNetworking:
65 return "Networking";
66 case DeviceClass::ServiceClass::kRendering:
67 return "Rendering";
68 case DeviceClass::ServiceClass::kCapturing:
69 return "Capturing";
70 case DeviceClass::ServiceClass::kObjectTransfer:
71 return "Object Transfer";
72 case DeviceClass::ServiceClass::kAudio:
73 return "Audio";
74 case DeviceClass::ServiceClass::kTelephony:
75 return "Telephony";
76 case DeviceClass::ServiceClass::kInformation:
77 return "Information";
78 }
79 }
80
81 } // namespace
82
DeviceClass()83 DeviceClass::DeviceClass() : DeviceClass(MajorClass::kUnspecified) {}
84
DeviceClass(MajorClass major_class)85 DeviceClass::DeviceClass(MajorClass major_class)
86 : bytes_{0x00, static_cast<uint8_t>(major_class), 0x00} {}
87
DeviceClass(std::initializer_list<uint8_t> bytes)88 DeviceClass::DeviceClass(std::initializer_list<uint8_t> bytes) {
89 BT_DEBUG_ASSERT(bytes.size() == bytes_.size());
90 std::copy(bytes.begin(), bytes.end(), bytes_.begin());
91 }
92
DeviceClass(uint32_t value)93 DeviceClass::DeviceClass(uint32_t value) {
94 BT_DEBUG_ASSERT(value < 1 << 24); // field should only populate 24 bits
95 bytes_ = {
96 static_cast<uint8_t>((value >> 0) & 0xFF),
97 static_cast<uint8_t>((value >> 8) & 0xFF),
98 static_cast<uint8_t>((value >> 16) & 0xFF),
99 };
100 }
101
to_int() const102 uint32_t DeviceClass::to_int() const {
103 uint32_t out = 0;
104 out |= bytes_[0];
105 out |= bytes_[1] << CHAR_BIT;
106 out |= bytes_[2] << 2 * CHAR_BIT;
107 return out;
108 }
109
SetServiceClasses(const std::unordered_set<ServiceClass> & classes)110 void DeviceClass::SetServiceClasses(
111 const std::unordered_set<ServiceClass>& classes) {
112 for (const auto& c : classes) {
113 uint8_t bit = static_cast<uint8_t>(c);
114 if (bit >= 16) {
115 bytes_[2] |= 0x01 << (bit - 16);
116 } else if (bit >= 8) {
117 bytes_[1] |= 0x01 << (bit - 8);
118 }
119 }
120 }
121
GetServiceClasses() const122 std::unordered_set<DeviceClass::ServiceClass> DeviceClass::GetServiceClasses()
123 const {
124 std::unordered_set<ServiceClass> classes;
125 for (uint8_t bit_no = 13; bit_no < 16; bit_no++) {
126 if (bytes_[1] & (0x01 << (bit_no - 8))) {
127 classes.emplace(bit_no_to_service_class(bit_no));
128 }
129 }
130 for (uint8_t bit_no = 16; bit_no < 24; bit_no++) {
131 if (bytes_[2] & (0x01 << (bit_no - 16))) {
132 classes.emplace(bit_no_to_service_class(bit_no));
133 }
134 }
135 return classes;
136 }
137
ToString() const138 std::string DeviceClass::ToString() const {
139 std::string service_desc;
140 auto classes = GetServiceClasses();
141 if (!classes.empty()) {
142 auto it = classes.begin();
143 service_desc = " (" + service_class_to_string(*it);
144 ++it;
145 for (; it != classes.end(); ++it) {
146 service_desc += ", " + service_class_to_string(*it);
147 }
148 service_desc = service_desc + ")";
149 }
150 switch (major_class()) {
151 case MajorClass::kMiscellaneous:
152 return "Miscellaneous" + service_desc;
153 case MajorClass::kComputer:
154 return "Computer" + service_desc;
155 case MajorClass::kPhone:
156 return "Phone" + service_desc;
157 case MajorClass::kLAN:
158 return "LAN" + service_desc;
159 case MajorClass::kAudioVideo:
160 return "A/V" + service_desc;
161 case MajorClass::kPeripheral:
162 return "Peripheral" + service_desc;
163 case MajorClass::kImaging:
164 return "Imaging" + service_desc;
165 case MajorClass::kWearable:
166 return "Wearable" + service_desc;
167 case MajorClass::kToy:
168 return "Toy" + service_desc;
169 case MajorClass::kHealth:
170 return "Health Device" + service_desc;
171 case MajorClass::kUnspecified:
172 return "Unspecified" + service_desc;
173 };
174
175 return "(unknown)";
176 }
177
178 } // namespace bt
179