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