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