1 // Copyright 2022 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 #pragma once 15 16 #include <cstdint> 17 18 #include "pw_bluetooth/assigned_uuids.h" 19 #include "pw_bluetooth/gatt/error.h" 20 #include "pw_bluetooth/gatt/server.h" 21 #include "pw_bluetooth/gatt/types.h" 22 #include "pw_span/span.h" 23 24 namespace pw::bluetooth_profiles { 25 26 // Device information to be exposed by the Device Information Service, according 27 // to the DIS spec 1.1. All fields are optional. 28 struct DeviceInfo { 29 // Bitmask of the fields present in the DeviceInfoService, each one 30 // corresponding to one of the possible characteristics in the Device 31 // Information Service. 32 enum class Field : uint16_t { 33 kManufacturerName = 1u << 0, 34 kModelNumber = 1u << 1, 35 kSerialNumber = 1u << 2, 36 kHardwareRevision = 1u << 3, 37 kFirmwareRevision = 1u << 4, 38 kSoftwareRevision = 1u << 5, 39 kSystemId = 1u << 6, 40 kRegulatoryInformation = 1u << 7, 41 kPnpId = 1u << 8, 42 }; 43 44 // Manufacturer Name String 45 span<const std::byte> manufacturer_name; 46 47 // Model Number String 48 span<const std::byte> model_number; 49 50 // Serial Number String 51 span<const std::byte> serial_number; 52 53 // Hardware Revision String 54 span<const std::byte> hardware_revision; 55 56 // Firmware Revision String 57 span<const std::byte> firmware_revision; 58 59 // Software Revision String 60 span<const std::byte> software_revision; 61 62 // System ID 63 span<const std::byte> system_id; 64 65 // IEEE 11073-20601 Regulatory Certification Data List 66 span<const std::byte> regulatory_information; 67 68 // PnP ID 69 span<const std::byte> pnp_id; 70 }; 71 72 // Helper operator| to allow combining multiple DeviceInfo::Field values. 73 static inline constexpr DeviceInfo::Field operator|(DeviceInfo::Field left, 74 DeviceInfo::Field right) { 75 return static_cast<DeviceInfo::Field>(static_cast<uint16_t>(left) | 76 static_cast<uint16_t>(right)); 77 } 78 79 static inline constexpr bool operator&(DeviceInfo::Field left, 80 DeviceInfo::Field right) { 81 return (static_cast<uint16_t>(left) & static_cast<uint16_t>(right)) != 0; 82 } 83 84 // Shared implementation of the DeviceInfoService<> template class of elements 85 // that don't depend on the template parameters. 86 class DeviceInfoServiceImpl { 87 public: DeviceInfoServiceImpl(const bluetooth::gatt::LocalServiceInfo & service_info,span<const span<const std::byte>> values)88 DeviceInfoServiceImpl(const bluetooth::gatt::LocalServiceInfo& service_info, 89 span<const span<const std::byte>> values) 90 : service_info_(service_info), delegate_(values) {} 91 92 // Publish this service on the passed gatt::Server. The service may be 93 // published only on one Server at a time. 94 void PublishService( 95 bluetooth::gatt::Server* gatt_server, 96 Callback< 97 void(bluetooth::Result<bluetooth::gatt::Server::PublishServiceError>)> 98 result_callback); 99 100 protected: 101 using GattCharacteristicUuid = bluetooth::GattCharacteristicUuid; 102 103 // A struct for describing each one of the optional characteristics available. 104 struct FieldDescriptor { 105 DeviceInfo::Field field_value; 106 span<const std::byte> DeviceInfo::*field_pointer; 107 bluetooth::Uuid characteristic_type; 108 }; 109 110 // List of all the fields / characteristics available in the DIS, mapping the 111 // characteristic UUID type to the corresponding field in the DeviceInfo 112 // struct. 113 static constexpr size_t kNumFields = 9; 114 static constexpr std::array<FieldDescriptor, kNumFields> 115 kCharacteristicFields = {{ 116 {DeviceInfo::Field::kManufacturerName, 117 &DeviceInfo::manufacturer_name, 118 GattCharacteristicUuid::kManufacturerNameString}, 119 {DeviceInfo::Field::kModelNumber, 120 &DeviceInfo::model_number, 121 GattCharacteristicUuid::kModelNumberString}, 122 {DeviceInfo::Field::kSerialNumber, 123 &DeviceInfo::serial_number, 124 GattCharacteristicUuid::kSerialNumberString}, 125 {DeviceInfo::Field::kHardwareRevision, 126 &DeviceInfo::hardware_revision, 127 GattCharacteristicUuid::kHardwareRevisionString}, 128 {DeviceInfo::Field::kFirmwareRevision, 129 &DeviceInfo::firmware_revision, 130 GattCharacteristicUuid::kFirmwareRevisionString}, 131 {DeviceInfo::Field::kSoftwareRevision, 132 &DeviceInfo::software_revision, 133 GattCharacteristicUuid::kSoftwareRevisionString}, 134 {DeviceInfo::Field::kSystemId, 135 &DeviceInfo::system_id, 136 GattCharacteristicUuid::kSystemId}, 137 {DeviceInfo::Field::kRegulatoryInformation, 138 &DeviceInfo::regulatory_information, 139 GattCharacteristicUuid:: 140 kIeee1107320601RegulatoryCertificationDataList}, 141 {DeviceInfo::Field::kPnpId, 142 &DeviceInfo::pnp_id, 143 GattCharacteristicUuid::kPnpId}, 144 }}; 145 146 private: 147 class Delegate : public bluetooth::gatt::LocalServiceDelegate { 148 public: Delegate(span<const span<const std::byte>> values)149 explicit Delegate(span<const span<const std::byte>> values) 150 : values_(values) {} 151 ~Delegate() override = default; 152 SetServicePtr(bluetooth::gatt::LocalService::Ptr service)153 void SetServicePtr(bluetooth::gatt::LocalService::Ptr service) { 154 local_service_ = std::move(service); 155 } 156 157 // LocalServiceDelegate overrides 158 void OnError(bluetooth::gatt::Error error) override; 159 160 void ReadValue(bluetooth::PeerId peer_id, 161 bluetooth::gatt::Handle handle, 162 uint32_t offset, 163 Function<void(bluetooth::Result<bluetooth::gatt::Error, 164 span<const std::byte>>)>&& 165 result_callback) override; 166 WriteValue(bluetooth::PeerId,bluetooth::gatt::Handle,uint32_t,span<const std::byte>,Function<void (bluetooth::Result<bluetooth::gatt::Error>)> && status_callback)167 void WriteValue(bluetooth::PeerId /* peer_id */, 168 bluetooth::gatt::Handle /* handle */, 169 uint32_t /* offset */, 170 span<const std::byte> /* value */, 171 Function<void(bluetooth::Result<bluetooth::gatt::Error>)>&& 172 status_callback) override { 173 status_callback(bluetooth::gatt::Error::kUnlikelyError); 174 } 175 CharacteristicConfiguration(bluetooth::PeerId,bluetooth::gatt::Handle,bool,bool)176 void CharacteristicConfiguration(bluetooth::PeerId /* peer_id */, 177 bluetooth::gatt::Handle /* handle */, 178 bool /* notify */, 179 bool /* indicate */) override { 180 // No indications or notifications are supported by this service. 181 } 182 MtuUpdate(bluetooth::PeerId,uint16_t)183 void MtuUpdate(bluetooth::PeerId /* peer_id*/, 184 uint16_t /* mtu */) override { 185 // MTU is ignored. 186 } 187 188 private: 189 // LocalService smart pointer returned by the pw_bluetooth API once the 190 // service is published. This field is unused since we don't generate any 191 // Notification or Indication, but deleting this object unpublishes the 192 // service. 193 bluetooth::gatt::LocalService::Ptr local_service_; 194 195 // Device information values for the service_info_ characteristics. The 196 // characteristic Handle is the index into the values_ span. 197 span<const span<const std::byte>> values_; 198 }; 199 200 // GATT service info. 201 const bluetooth::gatt::LocalServiceInfo& service_info_; 202 203 // Callback to be called after the service is published. 204 Callback<void( 205 bluetooth::Result<bluetooth::gatt::Server::PublishServiceError>)> 206 publish_service_callback_; 207 208 // The LocalServiceDelegate implementation. 209 Delegate delegate_; 210 }; 211 212 // Device Information Service exposing only the subset of characteristics 213 // specified by the bitmask kPresentFields. 214 template <DeviceInfo::Field kPresentFields, 215 bluetooth::gatt::Handle kServiceHandle> 216 class DeviceInfoService : public DeviceInfoServiceImpl { 217 public: 218 // Handle used to reference this service from other services. 219 static constexpr bluetooth::gatt::Handle kHandle = kServiceHandle; 220 221 // Construct a DeviceInfoService exposing the values provided in the 222 // `device_info` for the subset of characteristics selected by kPresentFields. 223 // DeviceInfo fields for characteristics not selected by kPresentFields are 224 // ignored. The `device_info` reference doesn't need to be kept alive after 225 // the constructor returns, however the data pointed to by the various fields 226 // in `device_info` must be kept available while the service is published. DeviceInfoService(const DeviceInfo & device_info)227 explicit constexpr DeviceInfoService(const DeviceInfo& device_info) 228 : DeviceInfoServiceImpl(kServiceInfo, span{values_}) { 229 size_t count = 0; 230 // Get the device information only for the fields we care about. 231 for (const auto& field : kCharacteristicFields) { 232 if (field.field_value & kPresentFields) { 233 values_[count] = device_info.*(field.field_pointer); 234 count++; 235 } 236 } 237 } 238 239 private: 240 // Return the total number of selected characteristics on this service. NumCharacteristics()241 static constexpr size_t NumCharacteristics() { 242 size_t ret = 0; 243 for (const auto& field : kCharacteristicFields) { 244 if (field.field_value & kPresentFields) { 245 ret++; 246 } 247 } 248 return ret; 249 } 250 static constexpr size_t kNumCharacteristics = NumCharacteristics(); 251 252 // Factory method to build the list of characteristics needed for a given 253 // subset of fields. 254 static constexpr std::array<bluetooth::gatt::Characteristic, 255 kNumCharacteristics> BuildServiceInfoCharacteristics()256 BuildServiceInfoCharacteristics() { 257 std::array<bluetooth::gatt::Characteristic, kNumCharacteristics> ret = {}; 258 size_t count = 0; 259 for (const auto& field : kCharacteristicFields) { 260 if (field.field_value & kPresentFields) { 261 ret[count] = bluetooth::gatt::Characteristic{ 262 .handle = bluetooth::gatt::Handle(count), 263 .type = field.characteristic_type, 264 .properties = bluetooth::gatt::CharacteristicPropertyBits::kRead, 265 .permissions = bluetooth::gatt::AttributePermissions{}, 266 .descriptors = {}, 267 }; 268 count++; 269 } 270 } 271 return ret; 272 } 273 // Static constexpr array of characteristics for the current subset of fields 274 // kPresentFields. 275 static constexpr auto kServiceInfoCharacteristics = 276 BuildServiceInfoCharacteristics(); 277 278 // GATT Service information. 279 static constexpr auto kServiceInfo = bluetooth::gatt::LocalServiceInfo{ 280 .handle = kServiceHandle, 281 .primary = true, 282 .type = bluetooth::GattServiceUuid::kDeviceInformation, 283 .characteristics = kServiceInfoCharacteristics, 284 .includes = {}, 285 }; 286 287 // Storage std::array for the span<const std::byte> of values. 288 std::array<span<const std::byte>, kNumCharacteristics> values_; 289 }; 290 291 } // namespace pw::bluetooth_profiles 292