• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #pragma once
16 #include <pw_assert/assert.h>
17 
18 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
19 
20 namespace bt {
21 
22 // This file defines classes which provide the interface for constructing HCI
23 // packets and reading/writing them using Emboss
24 // (https://github.com/google/emboss).
25 //
26 // Emboss does not own memory; it provides structured views into user allocated
27 // memory. These views are specified in Emboss source files such as hci.emb in
28 // pw_bluetooth, which implements the HCI protocol packet definitions.
29 //
30 // This file defines two classes: StaticPacket, which provides an Emboss view
31 // over a statically allocated buffer, and DynamicPacket, which is part of a
32 // class hierarchy that provides Emboss views over dynamic memory.
33 //
34 // EXAMPLE:
35 //
36 // Consider the following Emboss definition of the HCI Command packet header and
37 // Inquiry Command.
38 //
39 //  [(cpp) namespace: "bt::hci_spec"]
40 //  struct CommandHeader:
41 //    0     [+2] OpCodeBits opcode
42 //    $next [+1] UInt parameter_total_size
43 //
44 //  struct InquiryCommand:
45 //    let hdr_size = CommandHeader.$size_in_bytes
46 //    0     [+hdr_size] CommandHeader header
47 //    $next [+3] InquiryAccessCode lap
48 //    $next [+1] UInt inquiry_length
49 //    $next [+1] UInt num_responses
50 //
51 // The Emboss compiler generates two types of view for each struct. In the case
52 // of InquiryCommand, it generates InquiryCommandView (read only) and
53 // InquiryCommandWriter (read write). We can parameterize StaticPacket over
54 // one of these views to read and/or write an Inquiry packet:
55 //
56 //  bt::StaticPacket<pw::bluetooth::emboss::InquiryCommandWriter> packet;
57 //  auto view = packet.view();
58 //  view.inquiry_length().Write(100);
59 //  view.lap().Write(pw::bluetooth::emboss::InquiryAccessCode::GIAC);
60 //  cout << "inquiry_length = " << view.inquiry_length().Read();
61 //
62 // StaticPacket does not currently support packets with variable length.
63 template <typename T>
64 class StaticPacket {
65  public:
66   StaticPacket() = default;
67 
68   // Copy this packet from another view.
69   template <typename U>
StaticPacket(const U & other)70   explicit StaticPacket(const U& other) {
71     view().CopyFrom(other);
72   }
73 
74   // Returns an Emboss view over the buffer. Emboss views consist of two
75   // pointers and a length, so they are cheap to construct on-demand.
76   template <typename... Args>
view(Args...args)77   T view(Args... args) {
78     T view(args..., buffer_.mutable_data(), buffer_.size());
79     PW_ASSERT(view.IsComplete());
80     return view;
81   }
82 
83   template <typename... Args>
view(Args...args)84   T view(Args... args) const {
85     T view(args..., buffer_.data(), buffer_.size());
86     PW_ASSERT(view.IsComplete());
87     return view;
88   }
89 
data()90   BufferView data() const { return {buffer_.data(), buffer_.size()}; }
mutable_data()91   MutableBufferView mutable_data() {
92     return {buffer_.mutable_data(), buffer_.size()};
93   }
SetToZeros()94   void SetToZeros() { buffer_.SetToZeros(); }
95 
96  private:
97   // The intrinsic size of an Emboss struct is the size required to hold all of
98   // its fields. An Emboss view has a static IntrinsicSizeInBytes() accessor if
99   // the struct does not have dynamic length (i.e. not a variable length
100   // packet).
101   StaticByteBuffer<T::IntrinsicSizeInBytes().Read()> buffer_;
102 };
103 
104 // DynamicPacket is the parent class of a two-level class hierarchy that
105 // implements dynamically-allocated HCI packets to which reading/writing is
106 // mediated by Emboss.
107 //
108 // DynamicPacket contains data and methods that are universal across packet
109 // type. Its children are packet type specializations, i.e. Command, Event, ACL,
110 // and Sco packets. These classes provide header-type-specific functionality.
111 //
112 // Instances of DynamicPacket should not be constructed directly. Instead,
113 // packet type specialization classes should provide static factory functions.
114 //
115 // See CommandPacket in control_packets.h for an example of a packet type
116 // specialization.
117 class DynamicPacket {
118  public:
119   // Returns an Emboss view over the buffer. Unlike StaticPacket, which ensures
120   // type security as a struct parameterized over a particular Emboss view type,
121   // DynamicPacket is a generic type for all packets, so view() is to be
122   // parameterized over an Emboss view type on each call.
123   template <typename T, typename... Args>
view(Args...args)124   T view(Args... args) {
125     T view(args..., buffer_.mutable_data(), size());
126     PW_ASSERT(view.IsComplete());
127     return view;
128   }
129 
130   template <typename T, typename... Args>
view(Args...args)131   T view(Args... args) const {
132     T view(args..., buffer_.data(), size());
133     PW_ASSERT(view.IsComplete());
134     return view;
135   }
136 
137   template <typename T, typename... Args>
unchecked_view(Args...args)138   T unchecked_view(Args... args) {
139     return T(args..., buffer_.mutable_data(), size());
140   }
141 
142   template <typename T, typename... Args>
unchecked_view(Args...args)143   T unchecked_view(Args... args) const {
144     return T(args..., buffer_.data(), size());
145   }
146 
147   // Returns the size of the packet, i.e. payload size + header size.
size()148   size_t size() const { return buffer_.size(); }
data()149   BufferView data() const { return {buffer_.data(), size()}; }
mutable_data()150   MutableBufferView mutable_data() { return {buffer_.mutable_data(), size()}; }
release()151   DynamicByteBuffer release() { return std::move(buffer_); }
152 
153  protected:
154   // Construct the buffer to hold |packet_size| bytes (payload + header).
DynamicPacket(size_t packet_size)155   explicit DynamicPacket(size_t packet_size) : buffer_(packet_size) {}
156 
157  private:
158   DynamicByteBuffer buffer_;
159 };
160 
161 }  // namespace bt
162