1 // Copyright 2021 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 #include <optional> 18 19 #include "pw_assert/assert.h" 20 #include "pw_bytes/bit.h" 21 #include "pw_bytes/span.h" 22 #include "pw_result/result.h" 23 24 namespace pw::bluetooth_hci { 25 26 // HCI Packets as defined in the Bluetooth Core Specification Version 5.3 27 // “Host Controller Interface Functional Specification” in Volume 2, Part E. 28 // 29 // Note that for now only the subset of the HCI packets used in the HCI UART 30 // Transport Layer are provided as defined in the Bluetooth Core Specification 31 // version 5.3 "Host Controller Interface Transport Layer" volume 4, part A. 32 class CommandPacket; 33 class AsyncDataPacket; 34 class SyncDataPacket; 35 class EventPacket; 36 37 class Packet { 38 public: 39 enum class Type { 40 kCommandPacket, 41 kAsyncDataPacket, 42 kSyncDataPacket, 43 kEventPacket, 44 }; 45 type()46 constexpr Type type() const { return type_; } 47 size_bytes()48 constexpr size_t size_bytes() const { return packet_size_bytes_; } 49 command_packet()50 const CommandPacket& command_packet() const { 51 PW_ASSERT(type_ == Type::kCommandPacket); 52 return *reinterpret_cast<const CommandPacket*>(this); 53 } 54 command_packet()55 CommandPacket& command_packet() { 56 PW_ASSERT(type_ == Type::kCommandPacket); 57 return *reinterpret_cast<CommandPacket*>(this); 58 } 59 async_data_packet()60 const AsyncDataPacket& async_data_packet() const { 61 PW_ASSERT(type_ == Type::kAsyncDataPacket); 62 return *reinterpret_cast<const AsyncDataPacket*>(this); 63 } 64 async_data_packet()65 AsyncDataPacket& async_data_packet() { 66 PW_ASSERT(type_ == Type::kAsyncDataPacket); 67 return *reinterpret_cast<AsyncDataPacket*>(this); 68 } 69 sync_data_packet()70 const SyncDataPacket& sync_data_packet() const { 71 PW_ASSERT(type_ == Type::kSyncDataPacket); 72 return *reinterpret_cast<const SyncDataPacket*>(this); 73 } 74 sync_data_packet()75 SyncDataPacket& sync_data_packet() { 76 PW_ASSERT(type_ == Type::kSyncDataPacket); 77 return *reinterpret_cast<SyncDataPacket*>(this); 78 } 79 event_packet()80 const EventPacket& event_packet() const { 81 PW_ASSERT(type_ == Type::kEventPacket); 82 return *reinterpret_cast<const EventPacket*>(this); 83 } 84 event_packet()85 EventPacket& event_packet() { 86 PW_ASSERT(type_ == Type::kEventPacket); 87 return *reinterpret_cast<EventPacket*>(this); 88 } 89 90 protected: Packet(Type type,size_t packet_size_bytes)91 constexpr Packet(Type type, size_t packet_size_bytes) 92 : type_(type), packet_size_bytes_(packet_size_bytes) {} 93 94 private: 95 Type type_; 96 size_t packet_size_bytes_; 97 }; 98 99 class CommandPacket : public Packet { 100 private: 101 static constexpr size_t kOpcodeByteOffset = 0; 102 static constexpr size_t kParameterTotalLengthByteOffset = 2; 103 static constexpr size_t kParametersByteOffset = 3; 104 105 static constexpr size_t kOpcodeOcfOffset = 0; 106 static constexpr uint16_t kOpcodeOcfMask = 0x3FF << kOpcodeOcfOffset; 107 static constexpr size_t kOpcodeOgfOffset = 10; 108 static constexpr uint16_t kOpcodeOgfMask = 0x3F << kOpcodeOgfOffset; 109 110 public: 111 // HCI Command Packet Format, little-endian, based on bit offsets: 112 // 0 16 24 24+8*N 113 // | Opcode | Parameter Total Length | Parameter N | 114 // 0 10 16 115 // | OCF | OGF | 116 static constexpr size_t kHeaderSizeBytes = kParametersByteOffset; 117 CommandPacket(uint16_t opcode,const std::byte * parameters,uint8_t parameters_size_bytes)118 constexpr CommandPacket(uint16_t opcode, 119 const std::byte* parameters, 120 uint8_t parameters_size_bytes) 121 : Packet(Type::kCommandPacket, kHeaderSizeBytes + parameters_size_bytes), 122 opcode_(opcode), 123 parameters_(parameters, parameters_size_bytes) {} 124 125 // Precondition: the parameters size must be <= 255 bytes. CommandPacket(uint16_t opcode,ConstByteSpan parameters)126 constexpr CommandPacket(uint16_t opcode, ConstByteSpan parameters) 127 : CommandPacket(opcode, 128 parameters.data(), 129 static_cast<uint8_t>(parameters.size_bytes())) { 130 PW_ASSERT(parameters.size_bytes() <= std::numeric_limits<uint8_t>::max()); 131 } 132 133 // Decodes the packet based on the specified endianness. 134 static std::optional<CommandPacket> Decode(ConstByteSpan data, 135 endian order = endian::little); 136 137 // Encodes the packet based on the specified endianness. 138 // 139 // Returns: 140 // OK - returns the encoded packet. 141 // RESOURCE_EXHAUSTED - The input buffer is too small for this packet. 142 Result<ConstByteSpan> Encode(ByteSpan buffer, 143 endian order = endian::little) const; 144 opcode()145 constexpr uint16_t opcode() const { return opcode_; } 146 opcode_command_field()147 constexpr uint16_t opcode_command_field() const { 148 return static_cast<uint16_t>((opcode_ & kOpcodeOcfMask) >> 149 kOpcodeOcfOffset); 150 } 151 opcode_group_field()152 constexpr uint8_t opcode_group_field() const { 153 return static_cast<uint8_t>((opcode_ & kOpcodeOgfMask) >> kOpcodeOgfOffset); 154 } 155 parameters()156 constexpr const ConstByteSpan& parameters() const { return parameters_; } 157 158 private: 159 uint16_t opcode_; 160 ConstByteSpan parameters_; 161 }; 162 163 class AsyncDataPacket : public Packet { 164 private: 165 static constexpr size_t kHandleAndFragmentationBitsByteOffset = 0; 166 static constexpr size_t kDataTotalLengthByteOffset = 2; 167 static constexpr size_t kDataByteOffset = 4; 168 169 static constexpr size_t kHandleOffset = 0; 170 static constexpr uint16_t kHandleMask = 0xFFF << kHandleOffset; 171 static constexpr size_t kPbFlagOffset = 12; 172 static constexpr uint16_t kPbFlagMask = 0x3 << kPbFlagOffset; 173 static constexpr size_t kBcFlagOffset = 14; 174 static constexpr uint16_t kBcFlagMask = 0x3 << kBcFlagOffset; 175 176 public: 177 // HCI ACL Data Packet Format, little-endian, based on bit offsets: 178 // 0 12 14 16 32 32+8*N 179 // | Handle | PB Flag | BC Flag | Data Total Length | Data N | 180 static constexpr size_t kHeaderSizeBytes = kDataByteOffset; 181 AsyncDataPacket(uint16_t handle_and_fragmentation_bits,const std::byte * data,uint16_t data_size_bytes)182 constexpr AsyncDataPacket(uint16_t handle_and_fragmentation_bits, 183 const std::byte* data, 184 uint16_t data_size_bytes) 185 : Packet(Type::kAsyncDataPacket, kHeaderSizeBytes + data_size_bytes), 186 handle_and_fragmentation_bits_(handle_and_fragmentation_bits), 187 data_(data, data_size_bytes) {} 188 189 // Precondition: the parameters size must be <= 65535 bytes. AsyncDataPacket(uint16_t handle_and_fragmentation_bits,ConstByteSpan data)190 constexpr AsyncDataPacket(uint16_t handle_and_fragmentation_bits, 191 ConstByteSpan data) 192 : AsyncDataPacket(handle_and_fragmentation_bits, 193 data.data(), 194 static_cast<uint16_t>(data.size_bytes())) { 195 PW_ASSERT(data.size_bytes() <= std::numeric_limits<uint16_t>::max()); 196 } 197 198 // Decodes the packet based on the specified endianness. 199 static std::optional<AsyncDataPacket> Decode(ConstByteSpan data, 200 endian order = endian::little); 201 202 // Encodes the packet based on the specified endianness. 203 // 204 // Returns: 205 // OK - returns the encoded packet. 206 // RESOURCE_EXHAUSTED - The input buffer is too small for this packet. 207 Result<ConstByteSpan> Encode(ByteSpan buffer, 208 endian order = endian::little) const; 209 handle_and_fragmentation_bits()210 constexpr uint16_t handle_and_fragmentation_bits() const { 211 return handle_and_fragmentation_bits_; 212 } 213 handle()214 constexpr uint16_t handle() const { 215 return (handle_and_fragmentation_bits_ & kHandleMask) >> kHandleOffset; 216 } 217 pb_flag()218 constexpr uint8_t pb_flag() const { 219 return static_cast<uint8_t>( 220 (handle_and_fragmentation_bits_ & kPbFlagMask) >> kPbFlagOffset); 221 } 222 bc_flag()223 constexpr uint8_t bc_flag() const { 224 return static_cast<uint8_t>( 225 (handle_and_fragmentation_bits_ & kBcFlagMask) >> kBcFlagOffset); 226 } 227 data()228 constexpr const ConstByteSpan& data() const { return data_; } 229 230 private: 231 uint16_t handle_and_fragmentation_bits_; 232 ConstByteSpan data_; 233 }; 234 235 class SyncDataPacket : public Packet { 236 private: 237 static constexpr size_t kHandleAndStatusBitsByteOffset = 0; 238 static constexpr size_t kDataTotalLengthByteOffset = 2; 239 static constexpr size_t kDataByteOffset = 3; 240 241 static constexpr size_t kHandleOffset = 0; 242 static constexpr uint16_t kHandleMask = 0xFFF << kHandleOffset; 243 static constexpr size_t kPacketStatusFlagOffset = 12; 244 static constexpr uint16_t kPacketStatusFlagMask = 0x3 245 << kPacketStatusFlagOffset; 246 247 public: 248 // HCI SCO Data Packet Format, little-endian, based on bit offsets: 249 // 0 12 14 16 24 24+8*N 250 // | Handle | Packet Status Flag | Reserved | Data Total Length | Data N | 251 static constexpr size_t kHeaderSizeBytes = 3; 252 SyncDataPacket(uint16_t handle_and_status_bits,const std::byte * data,uint8_t data_size_bytes)253 constexpr SyncDataPacket(uint16_t handle_and_status_bits, 254 const std::byte* data, 255 uint8_t data_size_bytes) 256 : Packet(Type::kSyncDataPacket, kHeaderSizeBytes + data_size_bytes), 257 handle_and_status_bits_(handle_and_status_bits), 258 data_(data, data_size_bytes) {} 259 260 // Precondition: the parameters size must be <= 255 bytes. SyncDataPacket(uint16_t handle_and_status_bits,ConstByteSpan data)261 constexpr SyncDataPacket(uint16_t handle_and_status_bits, ConstByteSpan data) 262 : SyncDataPacket(handle_and_status_bits, 263 data.data(), 264 static_cast<uint8_t>(data.size_bytes())) { 265 PW_ASSERT(data.size_bytes() <= std::numeric_limits<uint8_t>::max()); 266 } 267 268 // Decodes the packet based on the specified endianness. 269 static std::optional<SyncDataPacket> Decode(ConstByteSpan data, 270 endian order = endian::little); 271 272 // Encodes the packet based on the specified endianness. 273 // 274 // Returns: 275 // OK - returns the encoded packet. 276 // RESOURCE_EXHAUSTED - The input buffer is too small for this packet. 277 Result<ConstByteSpan> Encode(ByteSpan buffer, 278 endian order = endian::little) const; 279 handle_and_status_bits()280 constexpr uint16_t handle_and_status_bits() const { 281 return handle_and_status_bits_; 282 } 283 handle()284 constexpr uint16_t handle() const { 285 return (handle_and_status_bits_ & kHandleMask) >> kHandleOffset; 286 } 287 packet_status_flag()288 constexpr uint8_t packet_status_flag() const { 289 return static_cast<uint8_t>( 290 (handle_and_status_bits_ & kPacketStatusFlagMask) >> 291 kPacketStatusFlagOffset); 292 } 293 data()294 constexpr const ConstByteSpan& data() const { return data_; } 295 296 private: 297 uint16_t handle_and_status_bits_; 298 ConstByteSpan data_; 299 }; 300 301 class EventPacket : public Packet { 302 private: 303 static constexpr size_t kEventCodeByteOffset = 0; 304 static constexpr size_t kParameterTotalLengthByteOffset = 1; 305 static constexpr size_t kParametersByteOffset = 2; 306 307 public: 308 // HCI SCO Data Packet Format, little-endian, based on bit offsets: 309 // 0 8 16 16+8*N 310 // | Event Code | Parameter Total Length | Parameter N | 311 static constexpr size_t kHeaderSizeBytes = kParametersByteOffset; 312 EventPacket(uint8_t event_code,const std::byte * parameters,uint8_t parameters_size_bytes)313 constexpr EventPacket(uint8_t event_code, 314 const std::byte* parameters, 315 uint8_t parameters_size_bytes) 316 : Packet(Type::kEventPacket, kHeaderSizeBytes + parameters_size_bytes), 317 event_code_(event_code), 318 parameters_(parameters, parameters_size_bytes) {} 319 320 // Precondition: the parameters size must be <= 255 bytes. EventPacket(uint16_t event_code,ConstByteSpan parameters)321 constexpr EventPacket(uint16_t event_code, ConstByteSpan parameters) 322 : EventPacket(event_code, 323 parameters.data(), 324 static_cast<uint8_t>(parameters.size_bytes())) { 325 PW_ASSERT(parameters.size_bytes() <= std::numeric_limits<uint8_t>::max()); 326 } 327 328 // Decodes the packet based on the specified endianness. 329 static std::optional<EventPacket> Decode(ConstByteSpan data); 330 331 // Encodes the packet based on the specified endianness. 332 // 333 // Returns: 334 // OK - returns the encoded packet. 335 // RESOURCE_EXHAUSTED - The input buffer is too small for this packet. 336 Result<ConstByteSpan> Encode(ByteSpan buffer) const; 337 event_code()338 constexpr uint8_t event_code() const { return event_code_; } 339 parameters()340 constexpr const ConstByteSpan& parameters() const { return parameters_; } 341 342 private: 343 uint8_t event_code_; 344 ConstByteSpan parameters_; 345 }; 346 347 } // namespace pw::bluetooth_hci 348