• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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