• 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_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 & writable). 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     BT_ASSERT(view.IsComplete());
78     return view;
79   }
80 
data()81   BufferView data() const { return {buffer_.data(), buffer_.size()}; }
mutable_data()82   MutableBufferView mutable_data() {
83     return {buffer_.mutable_data(), buffer_.size()};
84   }
SetToZeros()85   void SetToZeros() { buffer_.SetToZeros(); }
86 
87  private:
88   // The intrinsic size of an Emboss struct is the size required to hold all of
89   // its fields. An Emboss view has a static IntrinsicSizeInBytes() accessor if
90   // the struct does not have dynamic length (i.e. not a variable length
91   // packet).
92   StaticByteBuffer<T::IntrinsicSizeInBytes().Read()> buffer_;
93 };
94 
95 // DynamicPacket is the parent class of a two-level class hierarchy that
96 // implements dynamically-allocated HCI packets to which reading/writing is
97 // mediated by Emboss.
98 //
99 // DynamicPacket contains data and methods that are universal across packet
100 // type. Its children are packet type specializations, i.e. Command, Event, ACL,
101 // and Sco packets. These classes provide header-type-specific functionality.
102 //
103 // Instances of DynamicPacket should not be constructed directly. Instead,
104 // packet type specialization classes should provide static factory functions.
105 //
106 // See EmbossCommandPacket in emboss_control_packets.h for an example of a
107 // packet type specialization.
108 class DynamicPacket {
109  public:
110   // Returns an Emboss view over the buffer. Unlike StaticPacket, which ensures
111   // type security as a struct parameterized over a particular Emboss view type,
112   // DynamicPacket is a generic type for all packets, so view() is to be
113   // parameterized over an Emboss view type on each call.
114   template <typename T, typename... Args>
view(Args...args)115   T view(Args... args) {
116     T view(args..., buffer_.mutable_data(), size());
117     BT_ASSERT_MSG(
118         view.IsComplete(),
119         "emboss packet buffer not large enough to hold requested view");
120     return view;
121   }
122 
123   template <typename T, typename... Args>
view(Args...args)124   T view(Args... args) const {
125     T view(args..., buffer_.data(), size());
126     BT_ASSERT_MSG(
127         view.IsComplete(),
128         "emboss packet buffer not large enough to hold requested view");
129     return view;
130   }
131 
132   template <typename T, typename... Args>
unchecked_view(Args...args)133   T unchecked_view(Args... args) const {
134     return T(args..., buffer_.data(), size());
135   }
136 
137   // Returns the size of the packet, i.e. payload size + header size.
size()138   size_t size() const { return buffer_.size(); }
data()139   BufferView data() const { return {buffer_.data(), size()}; }
mutable_data()140   MutableBufferView mutable_data() { return {buffer_.mutable_data(), size()}; }
release()141   DynamicByteBuffer release() { return std::move(buffer_); }
142 
143  protected:
144   // Construct the buffer to hold |packet_size| bytes (payload + header).
DynamicPacket(size_t packet_size)145   explicit DynamicPacket(size_t packet_size) : buffer_(packet_size) {}
146 
147  private:
148   DynamicByteBuffer buffer_;
149 };
150 
151 }  // namespace bt
152