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